2014-01-23 13:07:36 -05:00
|
|
|
module Shoulda
|
2010-12-15 17:34:19 -05:00
|
|
|
module Matchers
|
2014-01-23 13:07:36 -05:00
|
|
|
module ActiveModel
|
|
|
|
# The `validate_numericality_of` matcher tests usage of the
|
|
|
|
# `validates_numericality_of` validation.
|
|
|
|
#
|
|
|
|
# class Person
|
|
|
|
# include ActiveModel::Model
|
|
|
|
# attr_accessor :gpa
|
|
|
|
#
|
|
|
|
# validates_numericality_of :gpa
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
|
|
|
# describe Person do
|
|
|
|
# it { should validate_numericality_of(:gpa) }
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 15:15:23 -04:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 13:07:36 -05:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should validate_numericality_of(:gpa)
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# #### Qualifiers
|
|
|
|
#
|
2015-03-28 11:03:31 -04:00
|
|
|
# ##### on
|
|
|
|
#
|
|
|
|
# Use `on` if your validation applies only under a certain context.
|
|
|
|
#
|
|
|
|
# class Person
|
|
|
|
# include ActiveModel::Model
|
|
|
|
# attr_accessor :number_of_dependents
|
|
|
|
#
|
|
|
|
# validates_numericality_of :number_of_dependents, on: :create
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
|
|
|
# describe Person do
|
|
|
|
# it do
|
|
|
|
# should validate_numericality_of(:number_of_dependents).
|
|
|
|
# on(:create)
|
|
|
|
# end
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 15:15:23 -04:00
|
|
|
# # Minitest (Shoulda)
|
2015-03-28 11:03:31 -04:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should validate_numericality_of(:number_of_dependents).on(:create)
|
|
|
|
# end
|
|
|
|
#
|
2014-01-23 13:07:36 -05:00
|
|
|
# ##### only_integer
|
|
|
|
#
|
|
|
|
# Use `only_integer` to test usage of the `:only_integer` option. This
|
|
|
|
# asserts that your attribute only allows integer numbers and disallows
|
|
|
|
# non-integer ones.
|
|
|
|
#
|
|
|
|
# class Person
|
|
|
|
# include ActiveModel::Model
|
|
|
|
# attr_accessor :age
|
|
|
|
#
|
|
|
|
# validates_numericality_of :age, only_integer: true
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
|
|
|
# describe Person do
|
|
|
|
# it { should validate_numericality_of(:age).only_integer }
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 15:15:23 -04:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 13:07:36 -05:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should validate_numericality_of(:age).only_integer
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# ##### is_less_than
|
|
|
|
#
|
|
|
|
# Use `is_less_than` to test usage of the the `:less_than` option. This
|
|
|
|
# asserts that the attribute can take a number which is less than the
|
|
|
|
# given value and cannot take a number which is greater than or equal to
|
|
|
|
# it.
|
|
|
|
#
|
|
|
|
# class Person
|
|
|
|
# include ActiveModel::Model
|
|
|
|
# attr_accessor :number_of_cars
|
|
|
|
#
|
|
|
|
# validates_numericality_of :number_of_cars, less_than: 2
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
|
|
|
# describe Person do
|
|
|
|
# it do
|
|
|
|
# should validate_numericality_of(:number_of_cars).
|
|
|
|
# is_less_than(2)
|
|
|
|
# end
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 15:15:23 -04:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 13:07:36 -05:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should validate_numericality_of(:number_of_cars).
|
|
|
|
# is_less_than(2)
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# ##### is_less_than_or_equal_to
|
|
|
|
#
|
|
|
|
# Use `is_less_than_or_equal_to` to test usage of the
|
|
|
|
# `:less_than_or_equal_to` option. This asserts that the attribute can
|
|
|
|
# take a number which is less than or equal to the given value and cannot
|
|
|
|
# take a number which is greater than it.
|
|
|
|
#
|
|
|
|
# class Person
|
|
|
|
# include ActiveModel::Model
|
|
|
|
# attr_accessor :birth_year
|
|
|
|
#
|
|
|
|
# validates_numericality_of :birth_year, less_than_or_equal_to: 1987
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
|
|
|
# describe Person do
|
|
|
|
# it do
|
|
|
|
# should validate_numericality_of(:birth_year).
|
|
|
|
# is_less_than_or_equal_to(1987)
|
|
|
|
# end
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 15:15:23 -04:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 13:07:36 -05:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should validate_numericality_of(:birth_year).
|
|
|
|
# is_less_than_or_equal_to(1987)
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# ##### is_equal_to
|
|
|
|
#
|
|
|
|
# Use `is_equal_to` to test usage of the `:equal_to` option. This asserts
|
|
|
|
# that the attribute can take a number which is equal to the given value
|
|
|
|
# and cannot take a number which is not equal.
|
|
|
|
#
|
|
|
|
# class Person
|
|
|
|
# include ActiveModel::Model
|
|
|
|
# attr_accessor :weight
|
|
|
|
#
|
|
|
|
# validates_numericality_of :weight, equal_to: 150
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
|
|
|
# describe Person do
|
|
|
|
# it { should validate_numericality_of(:weight).is_equal_to(150) }
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 15:15:23 -04:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 13:07:36 -05:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should validate_numericality_of(:weight).is_equal_to(150)
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# ##### is_greater_than_or_equal_to
|
|
|
|
#
|
|
|
|
# Use `is_greater_than_or_equal_to` to test usage of the
|
|
|
|
# `:greater_than_or_equal_to` option. This asserts that the attribute can
|
|
|
|
# take a number which is greater than or equal to the given value and
|
|
|
|
# cannot take a number which is less than it.
|
|
|
|
#
|
|
|
|
# class Person
|
|
|
|
# include ActiveModel::Model
|
|
|
|
# attr_accessor :height
|
|
|
|
#
|
|
|
|
# validates_numericality_of :height, greater_than_or_equal_to: 55
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
|
|
|
# describe Person do
|
|
|
|
# it do
|
|
|
|
# should validate_numericality_of(:height).
|
|
|
|
# is_greater_than_or_equal_to(55)
|
|
|
|
# end
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 15:15:23 -04:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 13:07:36 -05:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should validate_numericality_of(:height).
|
|
|
|
# is_greater_than_or_equal_to(55)
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# ##### is_greater_than
|
|
|
|
#
|
2015-10-07 08:42:25 -04:00
|
|
|
# Use `is_greater_than` to test usage of the `:greater_than` option.
|
2014-01-23 13:07:36 -05:00
|
|
|
# This asserts that the attribute can take a number which is greater than
|
|
|
|
# the given value and cannot take a number less than or equal to it.
|
|
|
|
#
|
|
|
|
# class Person
|
|
|
|
# include ActiveModel::Model
|
|
|
|
# attr_accessor :legal_age
|
|
|
|
#
|
|
|
|
# validates_numericality_of :legal_age, greater_than: 21
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
|
|
|
# describe Person do
|
|
|
|
# it do
|
|
|
|
# should validate_numericality_of(:legal_age).
|
|
|
|
# is_greater_than(21)
|
|
|
|
# end
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 15:15:23 -04:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 13:07:36 -05:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should validate_numericality_of(:legal_age).
|
|
|
|
# is_greater_than(21)
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# ##### even
|
|
|
|
#
|
|
|
|
# Use `even` to test usage of the `:even` option. This asserts that the
|
|
|
|
# attribute can take odd numbers and cannot take even ones.
|
|
|
|
#
|
|
|
|
# class Person
|
|
|
|
# include ActiveModel::Model
|
|
|
|
# attr_accessor :birth_month
|
|
|
|
#
|
|
|
|
# validates_numericality_of :birth_month, even: true
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
|
|
|
# describe Person do
|
|
|
|
# it { should validate_numericality_of(:birth_month).even }
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 15:15:23 -04:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 13:07:36 -05:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should validate_numericality_of(:birth_month).even
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# ##### odd
|
|
|
|
#
|
|
|
|
# Use `odd` to test usage of the `:odd` option. This asserts that the
|
|
|
|
# attribute can take a number which is odd and cannot take a number which
|
|
|
|
# is even.
|
|
|
|
#
|
|
|
|
# class Person
|
|
|
|
# include ActiveModel::Model
|
|
|
|
# attr_accessor :birth_day
|
|
|
|
#
|
|
|
|
# validates_numericality_of :birth_day, odd: true
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
|
|
|
# describe Person do
|
|
|
|
# it { should validate_numericality_of(:birth_day).odd }
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 15:15:23 -04:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 13:07:36 -05:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should validate_numericality_of(:birth_day).odd
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# ##### with_message
|
|
|
|
#
|
|
|
|
# Use `with_message` if you are using a custom validation message.
|
|
|
|
#
|
|
|
|
# class Person
|
|
|
|
# include ActiveModel::Model
|
|
|
|
# attr_accessor :number_of_dependents
|
|
|
|
#
|
|
|
|
# validates_numericality_of :number_of_dependents,
|
|
|
|
# message: 'Number of dependents must be a number'
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
|
|
|
# describe Person do
|
|
|
|
# it do
|
|
|
|
# should validate_numericality_of(:number_of_dependents).
|
|
|
|
# with_message('Number of dependents must be a number')
|
|
|
|
# end
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 15:15:23 -04:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 13:07:36 -05:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should validate_numericality_of(:number_of_dependents).
|
|
|
|
# with_message('Number of dependents must be a number')
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# ##### allow_nil
|
|
|
|
#
|
|
|
|
# Use `allow_nil` to assert that the attribute allows nil.
|
|
|
|
#
|
Add ignoring_interference_by_writer to all matchers
`allow_value` matcher is, of course, concerned with setting values on a
particular attribute on a particular record, and then checking that the
record is valid after doing so. That comes with a caveat: if the
attribute is overridden in such a way so that the same value going into
the attribute isn't the same value coming out of it, then `allow_value`
will balk -- it'll say, "I can't do that because that changes how I
work."
That's all well and good, but what the attribute intentionally changes
incoming values? ActiveRecord's typecasting behavior, for instance,
would trigger such an exception. What if the developer needs a way to
get around this? This is where `ignoring_interference_by_writer` comes
into play. You can tack it on to the end of the matcher, and you're free
to go on your way.
So, prior to this commit you could already apply it to `allow_value`,
but now in this commit it also works on any other matcher.
But, one little thing: sometimes using this qualifier isn't going to
work. Perhaps you or something else actually *is* overriding the
attribute to change incoming values in a specific way, and perhaps the
value that comes out makes the record fail validation, and there's
nothing you can do about it. So in this case, even if you're using
`ignoring_interference_by_writer`, we want to inform you about what the
attribute is doing -- what the input and output was. And so we do.
2015-12-30 20:47:46 -05:00
|
|
|
# class Post
|
2014-01-23 13:07:36 -05:00
|
|
|
# include ActiveModel::Model
|
|
|
|
# attr_accessor :age
|
|
|
|
#
|
|
|
|
# validates_numericality_of :age, allow_nil: true
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
|
|
|
# describe Post do
|
|
|
|
# it { should validate_numericality_of(:age).allow_nil }
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 15:15:23 -04:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 13:07:36 -05:00
|
|
|
# class PostTest < ActiveSupport::TestCase
|
|
|
|
# should validate_numericality_of(:age).allow_nil
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# @return [ValidateNumericalityOfMatcher]
|
2010-12-15 17:34:19 -05:00
|
|
|
#
|
|
|
|
def validate_numericality_of(attr)
|
|
|
|
ValidateNumericalityOfMatcher.new(attr)
|
|
|
|
end
|
|
|
|
|
2014-01-23 13:07:36 -05:00
|
|
|
# @private
|
2012-10-16 13:45:06 -04:00
|
|
|
class ValidateNumericalityOfMatcher
|
2015-12-13 18:45:24 -05:00
|
|
|
NUMERIC_NAME = 'number'
|
2012-10-23 12:30:18 -04:00
|
|
|
NON_NUMERIC_VALUE = 'abcd'
|
2014-04-26 11:13:57 -04:00
|
|
|
DEFAULT_DIFF_TO_COMPARE = 1
|
|
|
|
|
Add ignoring_interference_by_writer to all matchers
`allow_value` matcher is, of course, concerned with setting values on a
particular attribute on a particular record, and then checking that the
record is valid after doing so. That comes with a caveat: if the
attribute is overridden in such a way so that the same value going into
the attribute isn't the same value coming out of it, then `allow_value`
will balk -- it'll say, "I can't do that because that changes how I
work."
That's all well and good, but what the attribute intentionally changes
incoming values? ActiveRecord's typecasting behavior, for instance,
would trigger such an exception. What if the developer needs a way to
get around this? This is where `ignoring_interference_by_writer` comes
into play. You can tack it on to the end of the matcher, and you're free
to go on your way.
So, prior to this commit you could already apply it to `allow_value`,
but now in this commit it also works on any other matcher.
But, one little thing: sometimes using this qualifier isn't going to
work. Perhaps you or something else actually *is* overriding the
attribute to change incoming values in a specific way, and perhaps the
value that comes out makes the record fail validation, and there's
nothing you can do about it. So in this case, even if you're using
`ignoring_interference_by_writer`, we want to inform you about what the
attribute is doing -- what the input and output was. And so we do.
2015-12-30 20:47:46 -05:00
|
|
|
include Qualifiers::IgnoringInterferenceByWriter
|
|
|
|
|
2014-02-26 22:26:37 -05:00
|
|
|
attr_reader :diff_to_compare
|
2012-10-23 12:30:18 -04:00
|
|
|
|
2012-04-24 18:00:26 -04:00
|
|
|
def initialize(attribute)
|
Add ignoring_interference_by_writer to all matchers
`allow_value` matcher is, of course, concerned with setting values on a
particular attribute on a particular record, and then checking that the
record is valid after doing so. That comes with a caveat: if the
attribute is overridden in such a way so that the same value going into
the attribute isn't the same value coming out of it, then `allow_value`
will balk -- it'll say, "I can't do that because that changes how I
work."
That's all well and good, but what the attribute intentionally changes
incoming values? ActiveRecord's typecasting behavior, for instance,
would trigger such an exception. What if the developer needs a way to
get around this? This is where `ignoring_interference_by_writer` comes
into play. You can tack it on to the end of the matcher, and you're free
to go on your way.
So, prior to this commit you could already apply it to `allow_value`,
but now in this commit it also works on any other matcher.
But, one little thing: sometimes using this qualifier isn't going to
work. Perhaps you or something else actually *is* overriding the
attribute to change incoming values in a specific way, and perhaps the
value that comes out makes the record fail validation, and there's
nothing you can do about it. So in this case, even if you're using
`ignoring_interference_by_writer`, we want to inform you about what the
attribute is doing -- what the input and output was. And so we do.
2015-12-30 20:47:46 -05:00
|
|
|
super
|
2012-10-16 13:45:06 -04:00
|
|
|
@attribute = attribute
|
|
|
|
@submatchers = []
|
2014-02-26 22:26:37 -05:00
|
|
|
@diff_to_compare = DEFAULT_DIFF_TO_COMPARE
|
2015-12-13 18:45:24 -05:00
|
|
|
@expects_custom_validation_message = false
|
|
|
|
@expects_to_allow_nil = false
|
|
|
|
@expects_strict = false
|
|
|
|
@allowed_type_adjective = nil
|
|
|
|
@allowed_type_name = 'number'
|
numericality: lazily assign submatchers/qualifiers
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).
2015-12-26 01:12:43 -05:00
|
|
|
@context = nil
|
|
|
|
@expected_message = nil
|
2012-04-24 18:00:26 -04:00
|
|
|
end
|
2011-10-26 22:21:06 -04:00
|
|
|
|
2014-12-03 17:06:23 -05:00
|
|
|
def strict
|
2015-12-13 18:45:24 -05:00
|
|
|
@expects_strict = true
|
2014-12-03 17:06:23 -05:00
|
|
|
self
|
|
|
|
end
|
|
|
|
|
2015-12-13 18:45:24 -05:00
|
|
|
def expects_strict?
|
|
|
|
@expects_strict
|
|
|
|
end
|
|
|
|
|
2011-10-26 22:21:06 -04:00
|
|
|
def only_integer
|
2014-02-26 22:26:37 -05:00
|
|
|
prepare_submatcher(
|
2015-10-07 01:12:30 -04:00
|
|
|
NumericalityMatchers::OnlyIntegerMatcher.new(self, @attribute)
|
2014-02-26 22:26:37 -05:00
|
|
|
)
|
2013-02-19 05:09:13 -05:00
|
|
|
self
|
|
|
|
end
|
|
|
|
|
2014-02-21 17:31:48 -05:00
|
|
|
def allow_nil
|
2015-12-13 18:45:24 -05:00
|
|
|
@expects_to_allow_nil = true
|
2014-02-26 22:26:37 -05:00
|
|
|
prepare_submatcher(
|
|
|
|
AllowValueMatcher.new(nil)
|
|
|
|
.for(@attribute)
|
|
|
|
.with_message(:not_a_number)
|
|
|
|
)
|
2014-02-21 17:31:48 -05:00
|
|
|
self
|
|
|
|
end
|
|
|
|
|
2015-12-13 18:45:24 -05:00
|
|
|
def expects_to_allow_nil?
|
|
|
|
@expects_to_allow_nil
|
|
|
|
end
|
|
|
|
|
2014-02-26 22:26:37 -05:00
|
|
|
def odd
|
|
|
|
prepare_submatcher(
|
2015-10-07 01:12:30 -04:00
|
|
|
NumericalityMatchers::OddNumberMatcher.new(self, @attribute)
|
2014-02-26 22:26:37 -05:00
|
|
|
)
|
2013-02-19 05:09:13 -05:00
|
|
|
self
|
|
|
|
end
|
|
|
|
|
2014-02-26 22:26:37 -05:00
|
|
|
def even
|
|
|
|
prepare_submatcher(
|
2015-10-07 01:12:30 -04:00
|
|
|
NumericalityMatchers::EvenNumberMatcher.new(self, @attribute)
|
2014-02-26 22:26:37 -05:00
|
|
|
)
|
2013-02-19 05:09:13 -05:00
|
|
|
self
|
|
|
|
end
|
|
|
|
|
2014-02-26 22:26:37 -05:00
|
|
|
def is_greater_than(value)
|
|
|
|
prepare_submatcher(comparison_matcher_for(value, :>).for(@attribute))
|
2013-02-19 05:09:13 -05:00
|
|
|
self
|
|
|
|
end
|
|
|
|
|
2014-02-26 22:26:37 -05:00
|
|
|
def is_greater_than_or_equal_to(value)
|
|
|
|
prepare_submatcher(comparison_matcher_for(value, :>=).for(@attribute))
|
2013-02-19 05:09:13 -05:00
|
|
|
self
|
|
|
|
end
|
|
|
|
|
2014-02-26 22:26:37 -05:00
|
|
|
def is_equal_to(value)
|
|
|
|
prepare_submatcher(comparison_matcher_for(value, :==).for(@attribute))
|
2011-10-26 22:21:06 -04:00
|
|
|
self
|
|
|
|
end
|
|
|
|
|
2014-02-26 22:26:37 -05:00
|
|
|
def is_less_than(value)
|
|
|
|
prepare_submatcher(comparison_matcher_for(value, :<).for(@attribute))
|
2013-03-19 00:50:39 -04:00
|
|
|
self
|
|
|
|
end
|
|
|
|
|
2014-02-26 22:26:37 -05:00
|
|
|
def is_less_than_or_equal_to(value)
|
|
|
|
prepare_submatcher(comparison_matcher_for(value, :<=).for(@attribute))
|
2013-03-19 00:50:39 -04:00
|
|
|
self
|
|
|
|
end
|
|
|
|
|
2010-12-15 17:34:19 -05:00
|
|
|
def with_message(message)
|
2015-12-13 18:45:24 -05:00
|
|
|
@expects_custom_validation_message = true
|
numericality: lazily assign submatchers/qualifiers
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).
2015-12-26 01:12:43 -05:00
|
|
|
@expected_message = message
|
2010-12-15 17:34:19 -05:00
|
|
|
self
|
2015-04-01 02:00:14 -04:00
|
|
|
end
|
|
|
|
|
2015-12-13 18:45:24 -05:00
|
|
|
def expects_custom_validation_message?
|
|
|
|
@expects_custom_validation_message
|
|
|
|
end
|
|
|
|
|
2015-04-01 02:00:14 -04:00
|
|
|
def on(context)
|
numericality: lazily assign submatchers/qualifiers
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).
2015-12-26 01:12:43 -05:00
|
|
|
@context = context
|
2015-04-01 02:00:14 -04:00
|
|
|
self
|
2010-12-15 17:34:19 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def matches?(subject)
|
2012-10-16 13:45:06 -04:00
|
|
|
@subject = subject
|
2015-12-13 18:45:24 -05:00
|
|
|
@number_of_submatchers = @submatchers.size
|
2015-10-07 01:12:30 -04:00
|
|
|
|
2016-01-05 23:30:21 -05:00
|
|
|
add_disallow_value_matcher
|
numericality: lazily assign submatchers/qualifiers
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).
2015-12-26 01:12:43 -05:00
|
|
|
qualify_submatchers
|
2016-01-05 23:30:21 -05:00
|
|
|
|
2015-04-09 10:27:26 -04:00
|
|
|
first_failing_submatcher.nil?
|
2010-12-15 17:34:19 -05:00
|
|
|
end
|
|
|
|
|
2015-12-13 18:45:24 -05:00
|
|
|
def simple_description
|
|
|
|
description = ''
|
|
|
|
|
|
|
|
description << "validate that :#{@attribute} looks like "
|
|
|
|
description << Shoulda::Matchers::Util.a_or_an(full_allowed_type)
|
2014-12-03 17:06:23 -05:00
|
|
|
|
|
|
|
if comparison_descriptions.present?
|
2015-12-13 18:45:24 -05:00
|
|
|
description << ' ' + comparison_descriptions
|
2014-12-03 17:06:23 -05:00
|
|
|
end
|
|
|
|
|
2015-12-13 18:45:24 -05:00
|
|
|
description
|
|
|
|
end
|
|
|
|
|
|
|
|
def description
|
|
|
|
ValidationMatcher::BuildDescription.call(self, simple_description)
|
2012-10-16 13:45:06 -04:00
|
|
|
end
|
|
|
|
|
2013-12-24 06:24:27 -05:00
|
|
|
def failure_message
|
2015-12-25 18:17:20 -05:00
|
|
|
overall_failure_message.dup.tap do |message|
|
|
|
|
message << "\n"
|
|
|
|
message << failure_message_for_first_failing_submatcher
|
2015-12-13 18:45:24 -05:00
|
|
|
end
|
2013-04-12 13:11:07 -04:00
|
|
|
end
|
|
|
|
|
2013-12-24 06:24:27 -05:00
|
|
|
def failure_message_when_negated
|
2015-12-25 18:17:20 -05:00
|
|
|
overall_failure_message_when_negated.dup.tap do |message|
|
2015-12-13 18:45:24 -05:00
|
|
|
if submatcher_failure_message_when_negated.present?
|
|
|
|
raise "hmm, this needs to be implemented."
|
|
|
|
message << "\n"
|
|
|
|
message << Shoulda::Matchers.word_wrap(
|
|
|
|
submatcher_failure_message_when_negated,
|
|
|
|
indent: 2
|
|
|
|
)
|
|
|
|
end
|
|
|
|
end
|
2010-12-15 17:34:19 -05:00
|
|
|
end
|
|
|
|
|
2015-10-07 01:12:30 -04:00
|
|
|
def given_numeric_column?
|
2015-10-28 13:01:26 -04:00
|
|
|
attribute_is_active_record_column? &&
|
|
|
|
[:integer, :float, :decimal].include?(column_type)
|
2015-10-07 01:12:30 -04:00
|
|
|
end
|
|
|
|
|
2012-03-30 10:36:55 -04:00
|
|
|
private
|
|
|
|
|
2015-12-13 18:45:24 -05:00
|
|
|
def model
|
|
|
|
@subject.class
|
|
|
|
end
|
|
|
|
|
|
|
|
def overall_failure_message
|
|
|
|
Shoulda::Matchers.word_wrap(
|
|
|
|
"#{model.name} did not properly #{description}."
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
def overall_failure_message_when_negated
|
|
|
|
Shoulda::Matchers.word_wrap(
|
|
|
|
"Expected #{model.name} not to #{description}, but it did."
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
2015-10-28 13:01:26 -04:00
|
|
|
def attribute_is_active_record_column?
|
|
|
|
columns_hash.key?(@attribute.to_s)
|
|
|
|
end
|
|
|
|
|
2015-10-07 01:12:30 -04:00
|
|
|
def column_type
|
2015-10-28 13:01:26 -04:00
|
|
|
columns_hash[@attribute.to_s].type
|
|
|
|
end
|
|
|
|
|
|
|
|
def columns_hash
|
2015-10-07 01:12:30 -04:00
|
|
|
if @subject.class.respond_to?(:columns_hash)
|
2015-10-28 13:01:26 -04:00
|
|
|
@subject.class.columns_hash
|
|
|
|
else
|
|
|
|
{}
|
2015-10-07 01:12:30 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2012-10-16 13:45:06 -04:00
|
|
|
def add_disallow_value_matcher
|
numericality: lazily assign submatchers/qualifiers
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).
2015-12-26 01:12:43 -05:00
|
|
|
disallow_value_matcher = DisallowValueMatcher.
|
|
|
|
new(NON_NUMERIC_VALUE).
|
2012-10-23 12:30:18 -04:00
|
|
|
for(@attribute).
|
|
|
|
with_message(:not_a_number)
|
|
|
|
|
|
|
|
add_submatcher(disallow_value_matcher)
|
2012-04-03 20:20:50 -04:00
|
|
|
end
|
|
|
|
|
2014-02-26 22:26:37 -05:00
|
|
|
def prepare_submatcher(submatcher)
|
|
|
|
add_submatcher(submatcher)
|
2015-12-27 00:37:15 -05:00
|
|
|
submatcher
|
2014-02-26 22:26:37 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def comparison_matcher_for(value, operator)
|
2015-12-27 00:37:15 -05:00
|
|
|
NumericalityMatchers::ComparisonMatcher.
|
|
|
|
new(self, value, operator).
|
|
|
|
for(@attribute)
|
2014-02-26 22:26:37 -05:00
|
|
|
end
|
|
|
|
|
2012-10-16 13:45:06 -04:00
|
|
|
def add_submatcher(submatcher)
|
2015-12-13 18:45:24 -05:00
|
|
|
if submatcher.respond_to?(:allowed_type_name)
|
|
|
|
@allowed_type_name = submatcher.allowed_type_name
|
|
|
|
end
|
|
|
|
|
|
|
|
if submatcher.respond_to?(:allowed_type_adjective)
|
|
|
|
@allowed_type_adjective = submatcher.allowed_type_adjective
|
|
|
|
end
|
|
|
|
|
2015-12-27 00:37:15 -05:00
|
|
|
if submatcher.respond_to?(:diff_to_compare)
|
|
|
|
@diff_to_compare = [@diff_to_compare, submatcher.diff_to_compare].max
|
|
|
|
end
|
|
|
|
|
2012-10-16 13:45:06 -04:00
|
|
|
@submatchers << submatcher
|
2011-10-26 22:21:06 -04:00
|
|
|
end
|
|
|
|
|
numericality: lazily assign submatchers/qualifiers
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).
2015-12-26 01:12:43 -05:00
|
|
|
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
|
Add ignoring_interference_by_writer to all matchers
`allow_value` matcher is, of course, concerned with setting values on a
particular attribute on a particular record, and then checking that the
record is valid after doing so. That comes with a caveat: if the
attribute is overridden in such a way so that the same value going into
the attribute isn't the same value coming out of it, then `allow_value`
will balk -- it'll say, "I can't do that because that changes how I
work."
That's all well and good, but what the attribute intentionally changes
incoming values? ActiveRecord's typecasting behavior, for instance,
would trigger such an exception. What if the developer needs a way to
get around this? This is where `ignoring_interference_by_writer` comes
into play. You can tack it on to the end of the matcher, and you're free
to go on your way.
So, prior to this commit you could already apply it to `allow_value`,
but now in this commit it also works on any other matcher.
But, one little thing: sometimes using this qualifier isn't going to
work. Perhaps you or something else actually *is* overriding the
attribute to change incoming values in a specific way, and perhaps the
value that comes out makes the record fail validation, and there's
nothing you can do about it. So in this case, even if you're using
`ignoring_interference_by_writer`, we want to inform you about what the
attribute is doing -- what the input and output was. And so we do.
2015-12-30 20:47:46 -05:00
|
|
|
|
|
|
|
submatcher.ignoring_interference_by_writer(
|
|
|
|
ignore_interference_by_writer
|
|
|
|
)
|
numericality: lazily assign submatchers/qualifiers
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).
2015-12-26 01:12:43 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-12-13 18:45:24 -05:00
|
|
|
def number_of_submatchers_for_failure_message
|
|
|
|
if has_been_qualified?
|
|
|
|
@submatchers.size - 1
|
|
|
|
else
|
|
|
|
@submatchers.size
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def has_been_qualified?
|
|
|
|
@submatchers.any? do |submatcher|
|
|
|
|
submatcher.class.parent == NumericalityMatchers
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-04-09 10:27:26 -04:00
|
|
|
def first_failing_submatcher
|
2015-12-26 01:13:29 -05:00
|
|
|
@_failing_submatchers ||= @submatchers.detect do |submatcher|
|
2015-04-09 10:27:26 -04:00
|
|
|
!submatcher.matches?(@subject)
|
|
|
|
end
|
2012-10-16 13:45:06 -04:00
|
|
|
end
|
|
|
|
|
2015-12-13 18:45:24 -05:00
|
|
|
def submatcher_failure_message
|
|
|
|
first_failing_submatcher.failure_message
|
2012-10-16 13:45:06 -04:00
|
|
|
end
|
|
|
|
|
2015-12-13 18:45:24 -05:00
|
|
|
def submatcher_failure_message_when_negated
|
|
|
|
first_failing_submatcher.failure_message_when_negated
|
|
|
|
end
|
|
|
|
|
2015-12-25 18:17:20 -05:00
|
|
|
def failure_message_for_first_failing_submatcher
|
|
|
|
submatcher = first_failing_submatcher
|
|
|
|
|
|
|
|
if number_of_submatchers_for_failure_message > 1
|
|
|
|
submatcher_description = submatcher.simple_description.
|
|
|
|
sub(/\bvalidate that\b/, 'validates').
|
|
|
|
sub(/\bdisallow\b/, 'disallows').
|
|
|
|
sub(/\ballow\b/, 'allows')
|
|
|
|
submatcher_message =
|
|
|
|
"In checking that #{model.name} #{submatcher_description}, " +
|
|
|
|
submatcher.failure_message[0].downcase +
|
|
|
|
submatcher.failure_message[1..-1]
|
|
|
|
else
|
|
|
|
submatcher_message = submatcher.failure_message
|
|
|
|
end
|
|
|
|
|
|
|
|
Shoulda::Matchers.word_wrap(submatcher_message, indent: 2)
|
|
|
|
end
|
|
|
|
|
2015-12-13 18:45:24 -05:00
|
|
|
def full_allowed_type
|
|
|
|
"#{@allowed_type_adjective} #{@allowed_type_name}".strip
|
2014-01-12 20:43:36 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def comparison_descriptions
|
|
|
|
description_array = submatcher_comparison_descriptions
|
2015-12-13 18:45:24 -05:00
|
|
|
description_array.empty? ? '' : submatcher_comparison_descriptions.join(' and ')
|
2014-01-12 20:43:36 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def submatcher_comparison_descriptions
|
2014-02-26 22:26:37 -05:00
|
|
|
@submatchers.inject([]) do |arr, submatcher|
|
|
|
|
if submatcher.respond_to? :comparison_description
|
|
|
|
arr << submatcher.comparison_description
|
|
|
|
end
|
|
|
|
arr
|
|
|
|
end
|
2012-03-30 10:36:55 -04:00
|
|
|
end
|
|
|
|
end
|
2010-12-15 17:34:19 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|