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