diff --git a/lib/shoulda/matchers/active_record/association_matcher.rb b/lib/shoulda/matchers/active_record/association_matcher.rb index 1dda8e98..9c4c2989 100644 --- a/lib/shoulda/matchers/active_record/association_matcher.rb +++ b/lib/shoulda/matchers/active_record/association_matcher.rb @@ -3,6 +3,12 @@ module Shoulda # :nodoc: module ActiveRecord # :nodoc: # Ensure that the belongs_to relationship exists. # + # Options: + # * :class_name - tests that the association makes use of the class_name option. + # * :validate - tests that the association makes use of the validate + # option. + # + # Example: # it { should belong_to(:parent) } # def belong_to(name) @@ -18,6 +24,8 @@ module Shoulda # :nodoc: # * dependent - tests that the association makes use of the # dependent option. # * :class_name - tests that the association makes use of the class_name option. + # * :validate - tests that the association makes use of the validate + # option. # # Example: # it { should have_many(:friends) } @@ -36,6 +44,8 @@ module Shoulda # :nodoc: # * :dependent - tests that the association makes use of the # dependent option. # * :class_name - tests that the association makes use of the class_name option. + # * :validate - tests that the association makes use of the validate + # option. # # Example: # it { should have_one(:god) } # unless hindu @@ -47,6 +57,11 @@ module Shoulda # :nodoc: # Ensures that the has_and_belongs_to_many relationship exists, and that # the join table is in place. # + # Options: + # * :validate - tests that the association makes use of the validate + # option. + # + # Example: # it { should have_and_belong_to_many(:posts) } # def have_and_belong_to_many(name) @@ -85,6 +100,11 @@ module Shoulda # :nodoc: self end + def validate(validate = true) + @validate = validate + self + end + def matches?(subject) @subject = subject association_exists? && @@ -95,7 +115,8 @@ module Shoulda # :nodoc: class_name_correct? && order_correct? && conditions_correct? && - join_table_exists? + join_table_exists? && + validate_correct? end def failure_message @@ -230,6 +251,15 @@ module Shoulda # :nodoc: end end + def validate_correct? + if !@validate && !reflection.options[:validate] || @validate == reflection.options[:validate] + true + else + @missing = "#{@name} should have #{@validate} as validate" + false + end + end + def class_has_foreign_key?(klass) if klass.column_names.include?(foreign_key) true diff --git a/spec/shoulda/active_record/association_matcher_spec.rb b/spec/shoulda/active_record/association_matcher_spec.rb index 8293b6d9..a2a957c4 100644 --- a/spec/shoulda/active_record/association_matcher_spec.rb +++ b/spec/shoulda/active_record/association_matcher_spec.rb @@ -98,6 +98,46 @@ describe Shoulda::Matchers::ActiveRecord::AssociationMatcher do end Child.new.should_not @matcher.class_name('TreeChild') end + + context 'should accept an association with a false :validate option' do + before do + define_model :parent, :adopter => :boolean + define_model :child, :parent_id => :integer do + belongs_to :parent, :validate => false + end + end + subject { Child.new } + specify { subject.should @matcher.validate(false) } + specify { subject.should_not @matcher.validate(true) } + specify { subject.should_not @matcher.validate } + end + + context 'should accept an association with a true :validate option' do + before do + define_model :parent, :adopter => :boolean + define_model :child, :parent_id => :integer do + belongs_to :parent, :validate => true + end + end + subject { Child.new } + specify { subject.should_not @matcher.validate(false) } + specify { subject.should @matcher.validate(true) } + specify { subject.should @matcher.validate } + end + + context 'should accept an association without a :validate option' do + before do + define_model :parent, :adopter => :boolean + define_model :child, :parent_id => :integer do + belongs_to :parent + end + end + subject { Child.new } + specify { subject.should @matcher.validate(false) } + specify { subject.should_not @matcher.validate(true) } + specify { subject.should_not @matcher.validate } + end + end context "have_many" do @@ -240,6 +280,45 @@ describe Shoulda::Matchers::ActiveRecord::AssociationMatcher do Parent.new.should_not @matcher.class_name('Node') end + context 'should accept an association with a false :validate option' do + before do + define_model :child, :parent_id => :integer, :adopted => :boolean + define_model :parent do + has_many :children, :validate => false + end + end + subject { Parent.new } + specify { subject.should @matcher.validate(false) } + specify { subject.should_not @matcher.validate(true) } + specify { subject.should_not @matcher.validate } + end + + context 'should accept an association with a true :validate option' do + before do + define_model :child, :parent_id => :integer, :adopted => :boolean + define_model :parent do + has_many :children, :validate => true + end + end + subject { Parent.new } + specify { subject.should_not @matcher.validate(false) } + specify { subject.should @matcher.validate(true) } + specify { subject.should @matcher.validate } + end + + context 'should accept an association without a :validate option' do + before do + define_model :child, :parent_id => :integer, :adopted => :boolean + define_model :parent do + has_many :children + end + end + subject { Parent.new } + specify { subject.should @matcher.validate(false) } + specify { subject.should_not @matcher.validate(true) } + specify { subject.should_not @matcher.validate } + end + it "should accept an association with a nonstandard reverse foreign key, using :inverse_of" do define_model :child, :ancestor_id => :integer do belongs_to :ancestor, :inverse_of => :children, :class_name => :Parent @@ -364,6 +443,45 @@ describe Shoulda::Matchers::ActiveRecord::AssociationMatcher do Person.new.should_not @matcher.class_name('PersonDetail') end + context 'should accept an association with a false :validate option' do + before do + define_model :detail, :person_id => :integer, :disabled => :boolean + define_model :person do + has_one :detail, :validate => false + end + end + subject { Person.new } + specify { subject.should @matcher.validate(false) } + specify { subject.should_not @matcher.validate(true) } + specify { subject.should_not @matcher.validate } + end + + context 'should accept an association with a true :validate option' do + before do + define_model :detail, :person_id => :integer, :disabled => :boolean + define_model :person do + has_one :detail, :validate => true + end + end + subject { Person.new } + specify { subject.should_not @matcher.validate(false) } + specify { subject.should @matcher.validate(true) } + specify { subject.should @matcher.validate } + end + + context 'should accept an association without a :validate option' do + before do + define_model :detail, :person_id => :integer, :disabled => :boolean + define_model :person do + has_one :detail + end + end + subject { Person.new } + specify { subject.should @matcher.validate(false) } + specify { subject.should_not @matcher.validate(true) } + specify { subject.should_not @matcher.validate } + end + end context "have_and_belong_to_many" do @@ -445,5 +563,47 @@ describe Shoulda::Matchers::ActiveRecord::AssociationMatcher do Person.new.should_not @matcher.class_name('PersonRelatives') end + context 'should accept an association with a false :validate option' do + before do + define_model :relatives, :adopted => :boolean + define_model :person do + has_and_belongs_to_many :relatives, :validate => false + end + define_model :people_relative, :person_id => :integer, :relative_id => :integer + end + subject { Person.new } + specify { subject.should @matcher.validate(false) } + specify { subject.should_not @matcher.validate(true) } + specify { subject.should_not @matcher.validate } + end + + context 'should accept an association with a true :validate option' do + before do + define_model :relatives, :adopted => :boolean + define_model :person do + has_and_belongs_to_many :relatives, :validate => true + end + define_model :people_relative, :person_id => :integer, :relative_id => :integer + end + subject { Person.new } + specify { subject.should_not @matcher.validate(false) } + specify { subject.should @matcher.validate(true) } + specify { subject.should @matcher.validate } + end + + context 'should accept an association without a :validate option' do + before do + define_model :relatives, :adopted => :boolean + define_model :person do + has_and_belongs_to_many :relatives + end + define_model :people_relative, :person_id => :integer, :relative_id => :integer + end + subject { Person.new } + specify { subject.should @matcher.validate(false) } + specify { subject.should_not @matcher.validate(true) } + specify { subject.should_not @matcher.validate } + end + end end