From 417f289a86fb18ec95ce0d713ad31d09b63db26c Mon Sep 17 00:00:00 2001 From: Elliot Winkler Date: Mon, 21 Dec 2015 14:15:19 -0500 Subject: [PATCH] confirmation: Refactor to use AttributeSetter When the confirmation matcher is setting the confirmation attribute, we want it to raise an exception if the model does not have that attribute, same as if it would raise an exception if the attribute under test did not exist. --- .../validate_confirmation_of_matcher.rb | 32 ++++----- .../record_validating_confirmation_builder.rb | 10 +-- .../validate_confirmation_of_matcher_spec.rb | 66 +++++++++++++++++-- 3 files changed, 79 insertions(+), 29 deletions(-) diff --git a/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb b/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb index f5f92c39f..4edc71e27 100644 --- a/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb +++ b/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb @@ -89,7 +89,7 @@ def initialize(attribute) end def simple_description - "validate that #{@confirmation_attribute} matches #{@attribute}" + "validate that :#{@confirmation_attribute} matches :#{@attribute}" end def matches?(subject) @@ -103,30 +103,27 @@ def matches?(subject) private def disallows_different_value - set_confirmation('some value') - disallows_value_of('different value') do |matcher| - qualify_matcher(matcher) + qualify_matcher(matcher, 'some value') end end def allows_same_value - set_confirmation('same value') - allows_value_of('same value') do |matcher| - qualify_matcher(matcher) + qualify_matcher(matcher, 'same value') end end def allows_missing_confirmation - set_confirmation(nil) - allows_value_of('any value') do |matcher| - qualify_matcher(matcher) + qualify_matcher(matcher, nil) end end - def qualify_matcher(matcher) + def qualify_matcher(matcher, confirmation_attribute_value) + matcher.values_to_preset = { + confirmation_attribute => confirmation_attribute_value + } matcher.with_message( @expected_message, against: confirmation_attribute, @@ -134,12 +131,15 @@ def qualify_matcher(matcher) ) end - def set_confirmation(val) - setter = :"#{@confirmation_attribute}=" + def set_confirmation(value) + @last_value_set_on_confirmation_attribute = value - if @subject.respond_to?(setter) - @subject.__send__(setter, val) - end + AttributeSetter.set( + matcher_name: 'confirmation', + object: @subject, + attribute_name: confirmation_attribute, + value: value + ) end end end diff --git a/spec/support/unit/record_validating_confirmation_builder.rb b/spec/support/unit/record_validating_confirmation_builder.rb index 40d23bc48..57368536e 100644 --- a/spec/support/unit/record_validating_confirmation_builder.rb +++ b/spec/support/unit/record_validating_confirmation_builder.rb @@ -25,9 +25,8 @@ def message=(message) end def attribute_to_confirm - :attribute_to_confirm + options.fetch(:attribute, :attribute_to_confirm) end - alias_method :attribute, :attribute_to_confirm def confirmation_attribute :"#{attribute_to_confirm}_confirmation" @@ -44,11 +43,8 @@ def attribute_that_receives_error private def create_model - _attribute = attribute_to_confirm - _options = options - - define_model(model_name, _attribute => :string) do - validates_confirmation_of(_attribute, _options) + define_model(model_name, attribute_to_confirm => :string) do |model| + model.validates_confirmation_of(attribute_to_confirm, options) end end end diff --git a/spec/unit/shoulda/matchers/active_model/validate_confirmation_of_matcher_spec.rb b/spec/unit/shoulda/matchers/active_model/validate_confirmation_of_matcher_spec.rb index db7fae995..b651f81d4 100644 --- a/spec/unit/shoulda/matchers/active_model/validate_confirmation_of_matcher_spec.rb +++ b/spec/unit/shoulda/matchers/active_model/validate_confirmation_of_matcher_spec.rb @@ -6,7 +6,7 @@ context '#description' do it 'states that the confirmation must match its base attribute' do builder = builder_for_record_validating_confirmation - message = "validate that #{builder.confirmation_attribute} matches #{builder.attribute_to_confirm}" + message = "validate that :#{builder.confirmation_attribute} matches :#{builder.attribute_to_confirm}" matcher = described_class.new(builder.attribute_to_confirm) expect(matcher.description).to eq(message) end @@ -29,11 +29,65 @@ end end - context 'when the model does not have a confirmation validation' do - it 'fails' do - model = define_model(:example, attribute_to_confirm: :string) - record = model.new - expect(record).not_to validate_confirmation_of(:attribute_to_confirm) + context 'when the model does not have a confirmation attribute' do + it 'raises an AttributeDoesNotExistError' do + model = define_model(:example) + + assertion = lambda do + expect(model.new).to validate_confirmation_of(:attribute_to_confirm) + end + + message = <<-MESSAGE.rstrip +The matcher attempted to set :attribute_to_confirm_confirmation to "some +value" on the Example, but that attribute does not exist. + MESSAGE + + expect(&assertion).to raise_error( + Shoulda::Matchers::ActiveModel::AllowValueMatcher::AttributeDoesNotExistError, + message + ) + end + end + + context 'when the model does not have the attribute under test' do + it 'raises an AttributeDoesNotExistError' do + model = define_model(:example, attribute_to_confirm_confirmation: :string) + + assertion = lambda do + expect(model.new).to validate_confirmation_of(:attribute_to_confirm) + end + + message = <<-MESSAGE.rstrip +The matcher attempted to set :attribute_to_confirm to "different value" +on the Example, but that attribute does not exist. + MESSAGE + + expect(&assertion).to raise_error( + Shoulda::Matchers::ActiveModel::AllowValueMatcher::AttributeDoesNotExistError, + message + ) + end + end + + context 'when the model has all attributes, but does not have the validation' do + it 'fails with an appropriate failure message' do + model = define_model(:example, attribute_to_confirm: :string) do + attr_accessor :attribute_to_confirm_confirmation + end + + assertion = lambda do + expect(model.new).to validate_confirmation_of(:attribute_to_confirm) + end + + message = <<-MESSAGE +Example did not properly validate that +:attribute_to_confirm_confirmation matches :attribute_to_confirm. + After setting :attribute_to_confirm_confirmation to "some value", then + setting :attribute_to_confirm to "different value", the matcher + expected the Example to be invalid, but it was valid instead. + MESSAGE + + expect(&assertion).to fail_with_message(message) end end