diff --git a/NEWS.md b/NEWS.md
index 97ff1337..8f6f827e 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,5 +1,6 @@
# HEAD
+* Add `:touch` option to the association matcher.
* Ruby 2.0.0 is now officially supported.
* Fixes the issue where using %{attribute} or %{model} in I18n translations
raised exceptions
diff --git a/lib/shoulda/matchers/active_record/association_matcher.rb b/lib/shoulda/matchers/active_record/association_matcher.rb
index 7ff0bcf5..e372ce09 100644
--- a/lib/shoulda/matchers/active_record/association_matcher.rb
+++ b/lib/shoulda/matchers/active_record/association_matcher.rb
@@ -7,6 +7,8 @@ module Shoulda # :nodoc:
# * :class_name - tests that the association resolves to class_name.
# * :validate - tests that the association makes use of the validate
# option.
+ # * :touch - tests that the association makes use of the touch
+ # option.
#
# Example:
# it { should belong_to(:parent) }
@@ -107,7 +109,12 @@ module Shoulda # :nodoc:
end
def validate(validate = true)
- @validate = validate
+ @options[:validate] = validate
+ self
+ end
+
+ def touch(touch = true)
+ @options[:touch] = touch
self
end
@@ -122,7 +129,8 @@ module Shoulda # :nodoc:
order_correct? &&
conditions_correct? &&
join_table_exists? &&
- validate_correct?
+ validate_correct? &&
+ touch_correct?
end
def failure_message_for_should
@@ -258,14 +266,31 @@ module Shoulda # :nodoc:
end
def validate_correct?
- if !@validate && !reflection.options[:validate] || @validate == reflection.options[:validate]
+ if option_correct?(:validate)
true
else
- @missing = "#{@name} should have :validate => #{@validate}"
+ @missing = "#{@name} should have :validate => #{@options[:validate]}"
false
end
end
+ def touch_correct?
+ if option_correct?(:touch)
+ true
+ else
+ @missing = "#{@name} should have :touch => #{@options[:touch]}"
+ false
+ end
+ end
+
+ def option_correct?(key)
+ !@options.key?(key) || reflection_set_properly_for?(key)
+ end
+
+ def reflection_set_properly_for?(key)
+ @options[key] == !!reflection.options[key]
+ end
+
def class_has_foreign_key?(klass)
if @options.key?(:foreign_key)
reflection.options[:foreign_key] == @options[:foreign_key]
diff --git a/spec/shoulda/matchers/active_record/association_matcher_spec.rb b/spec/shoulda/matchers/active_record/association_matcher_spec.rb
index 2a84896e..8ea9f056 100644
--- a/spec/shoulda/matchers/active_record/association_matcher_spec.rb
+++ b/spec/shoulda/matchers/active_record/association_matcher_spec.rb
@@ -81,9 +81,9 @@ describe Shoulda::Matchers::ActiveRecord::AssociationMatcher do
belonging_to_parent.should_not belong_to(:parent).class_name('TreeChild')
end
- context 'validate' do
+ context 'an association with a :validate option' do
[false, true].each do |validate_value|
- context 'when the model has :validate => #{validate_value}' do
+ context "when the model has :validate => #{validate_value}" do
it 'accepts a matching validate option' do
belonging_to_parent(:validate => validate_value).
should belong_to(:parent).validate(validate_value)
@@ -103,6 +103,10 @@ describe Shoulda::Matchers::ActiveRecord::AssociationMatcher do
should_not belong_to(:parent).validate
end
end
+
+ it 'will not break matcher when validate option is unspecified' do
+ belonging_to_parent(:validate => validate_value).should belong_to(:parent)
+ end
end
end
end
@@ -121,6 +125,50 @@ describe Shoulda::Matchers::ActiveRecord::AssociationMatcher do
end
end
+ context 'an association with a :touch option' do
+ [false, true].each do |touch_value|
+ context "when the model has :touch => #{touch_value}" do
+ it 'accepts a matching touch option' do
+ belonging_to_parent(:touch => touch_value).
+ should belong_to(:parent).touch(touch_value)
+ end
+
+ it 'rejects a non-matching touch option' do
+ belonging_to_parent(:touch => touch_value).
+ should_not belong_to(:parent).touch(!touch_value)
+ end
+
+ it 'defaults to touch(true)' do
+ if touch_value
+ belonging_to_parent(:touch => touch_value).
+ should belong_to(:parent).touch
+ else
+ belonging_to_parent(:touch => touch_value).
+ should_not belong_to(:parent).touch
+ end
+ end
+
+ it 'will not break matcher when touch option is unspecified' do
+ belonging_to_parent(:touch => touch_value).should belong_to(:parent)
+ end
+ end
+ end
+ end
+
+ context 'an association without a :touch option' do
+ it 'accepts touch(false)' do
+ belonging_to_parent.should belong_to(:parent).touch(false)
+ end
+
+ it 'rejects touch(true)' do
+ belonging_to_parent.should_not belong_to(:parent).touch(true)
+ end
+
+ it 'rejects touch()' do
+ belonging_to_parent.should_not belong_to(:parent).touch
+ end
+ end
+
def belonging_to_parent(options = {})
define_model :parent
define_model :child, :parent_id => :integer do