Skip to content

Commit

Permalink
Extract ExceptionMessageFinder from AllowValueMatcher
Browse files Browse the repository at this point in the history
* Replaces repeated conditional with polymorphism
  • Loading branch information
jferris committed Sep 13, 2012
1 parent 3dbf5d4 commit 87327c2
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 68 deletions.
1 change: 1 addition & 0 deletions 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'
Expand Down
76 changes: 9 additions & 67 deletions lib/shoulda/matchers/active_model/allow_value_matcher.rb
Expand Up @@ -29,7 +29,7 @@ class AllowValueMatcher # :nodoc:

def initialize(*values)
@values_to_match = values
@strict = false
@message_finder_factory = ValidationMessageFinder
@options = {}
end

Expand All @@ -44,7 +44,7 @@ def with_message(message)
end

def strict
@strict = true
@message_finder_factory = ExceptionMessageFinder
self
end

Expand All @@ -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
Expand All @@ -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?
Expand All @@ -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?
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
58 changes: 58 additions & 0 deletions 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


2 changes: 1 addition & 1 deletion spec/shoulda/active_model/allow_value_matcher_spec.rb
Expand Up @@ -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
Expand Down
112 changes: 112 additions & 0 deletions 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

0 comments on commit 87327c2

Please sign in to comment.