diff --git a/lib/shoulda/matchers/active_model.rb b/lib/shoulda/matchers/active_model.rb index cd40c0624..e32906a3e 100644 --- a/lib/shoulda/matchers/active_model.rb +++ b/lib/shoulda/matchers/active_model.rb @@ -1,6 +1,7 @@ require 'shoulda/matchers/active_model/helpers' require 'shoulda/matchers/active_model/validation_matcher' require 'shoulda/matchers/active_model/validation_message_finder' +require 'shoulda/matchers/active_model/exception_message_finder' require 'shoulda/matchers/active_model/allow_value_matcher' require 'shoulda/matchers/active_model/ensure_length_of_matcher' require 'shoulda/matchers/active_model/ensure_inclusion_of_matcher' diff --git a/lib/shoulda/matchers/active_model/allow_value_matcher.rb b/lib/shoulda/matchers/active_model/allow_value_matcher.rb index 272d4b374..967b372a9 100644 --- a/lib/shoulda/matchers/active_model/allow_value_matcher.rb +++ b/lib/shoulda/matchers/active_model/allow_value_matcher.rb @@ -29,7 +29,7 @@ class AllowValueMatcher # :nodoc: def initialize(*values) @values_to_match = values - @strict = false + @message_finder_factory = ValidationMessageFinder @options = {} end @@ -44,7 +44,7 @@ def with_message(message) end def strict - @strict = true + @message_finder_factory = ExceptionMessageFinder self end @@ -66,13 +66,7 @@ def negative_failure_message end def description - base = "allow #{@attribute} to be set to #{allowed_values}" - - if strict? - "strictly #{base}" - else - message_finder.allow_description(allowed_values) - end + message_finder.allow_description(allowed_values) end private @@ -82,19 +76,7 @@ def errors_match? end def has_messages? - if strict? - has_exception_message? - else - message_finder.has_messages? - end - end - - def has_exception_message? - @instance.valid? - false - rescue ::ActiveModel::StrictValidationFailed => exception - @strict_exception = exception - true + message_finder.has_messages? end def errors_for_attribute_match? @@ -106,19 +88,7 @@ def errors_for_attribute_match? end def errors_for_attribute - if strict? - [strict_exception_message] - else - message_finder.messages - end - end - - def strict_exception_message - @strict_exception.message - end - - def strict? - @strict + message_finder.messages end def errors_match_regexp? @@ -139,27 +109,11 @@ def expectation end def error_source - if strict? - 'exception' - else - message_finder.source_description - end + message_finder.source_description end def error_description - if strict? - exception_description - else - message_finder.messages_description - end - end - - def exception_description - if @strict_exception - strict_exception_message - else - 'no exception' - end + message_finder.messages_description end def allowed_values @@ -181,19 +135,7 @@ def expected_message end def default_expected_message - if strict? - default_full_message - else - message_finder.expected_message_from(default_attribute_message) - end - end - - def default_full_message - "#{human_attribute_name} #{default_attribute_message}" - end - - def human_attribute_name - @instance.class.human_attribute_name(@attribute) + message_finder.expected_message_from(default_attribute_message) end def default_attribute_message @@ -209,7 +151,7 @@ def model_name end def message_finder - @message_finder ||= ValidationMessageFinder.new(@instance, @attribute) + @message_finder ||= @message_finder_factory.new(@instance, @attribute) end end end diff --git a/lib/shoulda/matchers/active_model/exception_message_finder.rb b/lib/shoulda/matchers/active_model/exception_message_finder.rb new file mode 100644 index 000000000..b1f16ab61 --- /dev/null +++ b/lib/shoulda/matchers/active_model/exception_message_finder.rb @@ -0,0 +1,58 @@ +module Shoulda + module Matchers + module ActiveModel + + # Finds message information from exceptions thrown by #valid? + class ExceptionMessageFinder + def initialize(instance, attribute) + @instance = instance + @attribute = attribute + end + + def allow_description(allowed_values) + "doesn't raise when #{@attribute} is set to #{allowed_values}" + end + + def messages_description + if has_messages? + messages.join + else + 'no exception' + end + end + + def has_messages? + messages.any? + end + + def messages + @messages ||= validate_and_rescue + end + + def source_description + 'exception' + end + + def expected_message_from(attribute_message) + "#{human_attribute_name} #{attribute_message}" + end + + private + + def validate_and_rescue + @instance.valid? + [] + rescue ::ActiveModel::StrictValidationFailed => exception + [exception.message] + end + + def human_attribute_name + @instance.class.human_attribute_name(@attribute) + end + end + + end + end +end + + diff --git a/spec/shoulda/active_model/allow_value_matcher_spec.rb b/spec/shoulda/active_model/allow_value_matcher_spec.rb index 3bc27f72a..9eb321be7 100644 --- a/spec/shoulda/active_model/allow_value_matcher_spec.rb +++ b/spec/shoulda/active_model/allow_value_matcher_spec.rb @@ -107,7 +107,7 @@ it "describes itself" do allow_value("xyz").for(:attr).strict.description. - should == 'strictly allow attr to be set to "xyz"' + should == %{doesn't raise when attr is set to "xyz"} end it "provides a useful negative failure message" do diff --git a/spec/shoulda/active_model/exception_message_finder_spec.rb b/spec/shoulda/active_model/exception_message_finder_spec.rb new file mode 100644 index 000000000..bf5199401 --- /dev/null +++ b/spec/shoulda/active_model/exception_message_finder_spec.rb @@ -0,0 +1,112 @@ +require 'spec_helper' + +describe Shoulda::Matchers::ActiveModel::ExceptionMessageFinder do + if Rails::VERSION::STRING.to_f >= 3.2 + context '#allow_description' do + it 'describes its attribute' do + finder = build_finder(:attribute => :attr) + + description = finder.allow_description('allowed values') + + description.should == "doesn't raise when attr is set to allowed values" + end + end + + context '#expected_message_from' do + it 'returns the message with the attribute name prefixed' do + finder = build_finder(:attribute => :attr) + + message = finder.expected_message_from('some message') + + message.should == 'Attr some message' + end + end + + context '#has_messages?' do + it 'has messages when some validations fail' do + finder = build_finder(:format => /abc/, :value => 'xyz') + + result = finder.has_messages? + + result.should be_true + end + + it 'has no messages when all validations pass' do + finder = build_finder(:format => /abc/, :value => 'abc') + + result = finder.has_messages? + + result.should be_false + end + end + + context '#messages' do + it 'returns errors for the given attribute' do + finder = build_finder( + :attribute => :attr, + :format => /abc/, + :value => 'xyz' + ) + + messages = finder.messages + + messages.should == ['Attr is invalid'] + end + end + + context '#messages_description' do + it 'describes errors for the given attribute' do + finder = build_finder( + :attribute => :attr, + :format => /abc/, + :value => 'xyz' + ) + + description = finder.messages_description + + description.should == 'Attr is invalid' + end + + it 'describes errors when there are none' do + finder = build_finder(:format => /abc/, :value => 'abc') + + description = finder.messages_description + + description.should == 'no exception' + end + end + + context '#source_description' do + it 'describes the source of its messages' do + finder = build_finder + + description = finder.source_description + + description.should == 'exception' + end + end + end + + def build_finder(arguments = {}) + arguments[:attribute] ||= :attr + instance = build_instance_validating( + arguments[:attribute], + arguments[:format] || /abc/, + arguments[:value] || 'abc' + ) + Shoulda::Matchers::ActiveModel::ExceptionMessageFinder.new( + instance, + arguments[:attribute] + ) + end + + def build_instance_validating(attribute, format, value) + model_class = define_model(:example, attribute => :string) do + attr_accessible attribute + validates_format_of attribute, :with => format, :strict => true + end + + model_class.new(attribute => value) + end +end +