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
|
2016-06-15 20:00:51 -04:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-01-23 13:07:36 -05:00
|
|
|
# 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
|
2016-06-15 20:00:51 -04:00
|
|
|
# RSpec.describe Person, type: :model do
|
2015-03-28 11:03:31 -04:00
|
|
|
# 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
|
2016-06-15 20:00:51 -04:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-01-23 13:07:36 -05:00
|
|
|
# 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
|
2016-06-15 20:00:51 -04:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-01-23 13:07:36 -05:00
|
|
|
# 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
|
2016-06-15 20:00:51 -04:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-01-23 13:07:36 -05:00
|
|
|
# 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
|
2016-06-15 20:00:51 -04:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-01-23 13:07:36 -05:00
|
|
|
# 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
|
2016-06-15 20:00:51 -04:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-01-23 13:07:36 -05:00
|
|
|
# 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
|
2016-06-15 20:00:51 -04:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-01-23 13:07:36 -05:00
|
|
|
# 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
|
2016-06-15 20:00:51 -04:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-01-23 13:07:36 -05:00
|
|
|
# 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
|
2016-06-15 20:00:51 -04:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-01-23 13:07:36 -05:00
|
|
|
# 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
|
2016-06-15 20:00:51 -04:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-01-23 13:07:36 -05:00
|
|
|
# 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
|
2016-06-15 20:00:51 -04:00
|
|
|
# RSpec.describe Post, type: :model do
|
2014-01-23 13:07:36 -05:00
|
|
|
# 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)
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 16:59:24 -04:00
|
|
|
matches_or_does_not_match?(subject)
|
|
|
|
first_submatcher_that_fails_to_match.nil?
|
|
|
|
end
|
2016-01-05 23:30:21 -05:00
|
|
|
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 16:59:24 -04:00
|
|
|
def does_not_match?(subject)
|
|
|
|
matches_or_does_not_match?(subject)
|
|
|
|
first_submatcher_that_fails_to_not_match.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"
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 16:59:24 -04:00
|
|
|
message << failure_message_for_first_submatcher_that_fails_to_match
|
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|
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 16:59:24 -04:00
|
|
|
message << "\n"
|
|
|
|
message << failure_message_for_first_submatcher_that_fails_to_not_match
|
2015-12-13 18:45:24 -05:00
|
|
|
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
|
|
|
|
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 16:59:24 -04:00
|
|
|
def matches_or_does_not_match?(subject)
|
|
|
|
@subject = subject
|
|
|
|
@number_of_submatchers = @submatchers.size
|
|
|
|
|
|
|
|
add_disallow_value_matcher
|
|
|
|
qualify_submatchers
|
2015-12-13 18:45:24 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def overall_failure_message
|
|
|
|
Shoulda::Matchers.word_wrap(
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 16:59:24 -04:00
|
|
|
"Expected #{model.name} to #{description}, but this could not " +
|
|
|
|
'be proved.'
|
2015-12-13 18:45:24 -05:00
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
def overall_failure_message_when_negated
|
|
|
|
Shoulda::Matchers.word_wrap(
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 16:59:24 -04:00
|
|
|
"Expected #{model.name} not to #{description}, but this could not " +
|
|
|
|
'be proved.'
|
2015-12-13 18:45:24 -05:00
|
|
|
)
|
|
|
|
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|
|
2019-03-21 01:24:02 -04:00
|
|
|
Shoulda::Matchers::RailsShim.parent_of(submatcher.class) ==
|
|
|
|
NumericalityMatchers
|
2015-12-13 18:45:24 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 16:59:24 -04:00
|
|
|
def first_submatcher_that_fails_to_match
|
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
|
|
|
|
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 16:59:24 -04:00
|
|
|
def first_submatcher_that_fails_to_not_match
|
|
|
|
@_failing_submatchers ||= @submatchers.detect do |submatcher|
|
|
|
|
!submatcher.does_not_match?(@subject)
|
|
|
|
end
|
2012-10-16 13:45:06 -04:00
|
|
|
end
|
|
|
|
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 16:59:24 -04:00
|
|
|
def failure_message_for_first_submatcher_that_fails_to_match
|
|
|
|
build_submatcher_failure_message_for(
|
|
|
|
first_submatcher_that_fails_to_match,
|
|
|
|
:failure_message
|
|
|
|
)
|
2015-12-13 18:45:24 -05:00
|
|
|
end
|
|
|
|
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 16:59:24 -04:00
|
|
|
def failure_message_for_first_submatcher_that_fails_to_not_match
|
|
|
|
build_submatcher_failure_message_for(
|
|
|
|
first_submatcher_that_fails_to_not_match,
|
|
|
|
:failure_message_when_negated
|
|
|
|
)
|
|
|
|
end
|
2015-12-25 18:17:20 -05:00
|
|
|
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 16:59:24 -04:00
|
|
|
def build_submatcher_failure_message_for(
|
|
|
|
submatcher,
|
|
|
|
failure_message_method
|
|
|
|
)
|
|
|
|
failure_message = submatcher.public_send(failure_message_method)
|
|
|
|
submatcher_description = submatcher.simple_description.
|
|
|
|
sub(/\bvalidate that\b/, 'validates').
|
|
|
|
sub(/\bdisallow\b/, 'disallows').
|
|
|
|
sub(/\ballow\b/, 'allows')
|
|
|
|
submatcher_message =
|
|
|
|
if number_of_submatchers_for_failure_message > 1
|
2015-12-25 18:17:20 -05:00
|
|
|
"In checking that #{model.name} #{submatcher_description}, " +
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 16:59:24 -04:00
|
|
|
failure_message[0].downcase +
|
|
|
|
failure_message[1..-1]
|
|
|
|
else
|
|
|
|
failure_message
|
|
|
|
end
|
2015-12-25 18:17:20 -05:00
|
|
|
|
|
|
|
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
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 16:59:24 -04:00
|
|
|
|
|
|
|
def model
|
|
|
|
@subject.class
|
|
|
|
end
|
2012-03-30 10:36:55 -04:00
|
|
|
end
|
2010-12-15 17:34:19 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|