Skip to content

Commit

Permalink
numericality: lazily assign submatchers/qualifiers
Browse files Browse the repository at this point in the history
The numericality matcher has two kinds of qualifiers: those that add new
tests directly to the matcher, and those that change the behavior of
those tests. For instance, consider `odd` and `strict`. Using `odd`
instructs the matcher to verify not only that the attribute can only be
set to a number, but that it must be set to an *odd* number. `strict`
then instructs the `odd` matcher to make this verification with the
assumption that a validation exception will be raised instead of the
record producing a validation error.

It follows, then, that these qualifiers are order-dependent. This test:

    should validate_numericality_of(:attr).strict.odd

is actually a different test than:

    should validate_numericality_of(:attr).odd.strict

Why? Because if `strict` is written before `odd`, then it doesn't have
any submatchers to affect. But if it is written after, then it can see
that `odd` is present and can apply itself to it.

The fact that users have to remember to specify submatchers in a certain
order is inconvenient, and we don't like it. So to fix this, this commit
records whenever `strict` or other like qualifiers are used, but then
waits to apply them to the other submatchers until #matches? is called
(that is, when the matcher is run).
  • Loading branch information
mcmire committed Dec 31, 2015
1 parent 8e24a6e commit 6c67a5e
Showing 1 changed file with 25 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -319,13 +319,12 @@ def initialize(attribute)
@expects_strict = false
@allowed_type_adjective = nil
@allowed_type_name = 'number'

add_disallow_value_matcher
@context = nil
@expected_message = nil
end

def strict
@expects_strict = true
@submatchers.each(&:strict)
self
end

Expand Down Expand Up @@ -395,7 +394,7 @@ def is_less_than_or_equal_to(value)

def with_message(message)
@expects_custom_validation_message = true
@submatchers.each { |matcher| matcher.with_message(message) }
@expected_message = message
self
end

Expand All @@ -404,16 +403,16 @@ def expects_custom_validation_message?
end

def on(context)
@submatchers.each { |matcher| matcher.on(context) }
@context = context
self
end

def matches?(subject)
@subject = subject
@number_of_submatchers = @submatchers.size

if given_numeric_column?
remove_disallow_value_matcher
unless given_numeric_column?
add_disallow_value_matcher
end

if @submatchers.empty?
Expand All @@ -424,6 +423,7 @@ def matches?(subject)
)
end

qualify_submatchers
first_failing_submatcher.nil?
end

Expand Down Expand Up @@ -493,17 +493,14 @@ def column_type
end

def add_disallow_value_matcher
disallow_value_matcher = DisallowValueMatcher.new(NON_NUMERIC_VALUE).
disallow_value_matcher = DisallowValueMatcher.
new(NON_NUMERIC_VALUE).
for(@attribute).
with_message(:not_a_number)

add_submatcher(disallow_value_matcher)
end

def remove_disallow_value_matcher
@submatchers.shift
end

def prepare_submatcher(submatcher)
add_submatcher(submatcher)

Expand All @@ -530,6 +527,22 @@ def add_submatcher(submatcher)
@submatchers << submatcher
end

def qualify_submatchers
@submatchers.each do |submatcher|
if @expects_strict
submatcher.strict(@expects_strict)
end

if @expected_message.present?
submatcher.with_message(@expected_message)
end

if @context
submatcher.on(@context)
end
end
end

def number_of_submatchers_for_failure_message
if has_been_qualified?
@submatchers.size - 1
Expand Down

0 comments on commit 6c67a5e

Please sign in to comment.