Add validate numericality in range

This commit is contained in:
Michal Papis 2021-01-05 19:42:16 +01:00
parent c28c800803
commit 2486e887de
No known key found for this signature in database
GPG Key ID: E206C29FBF04FF17
6 changed files with 28 additions and 5 deletions

View File

@ -1,3 +1,7 @@
* Add `in: range` parameter to `numericality` validator.
*Michal Papis*
* Add `locale` argument to `ActiveModel::Name#initialize` to be used to generate the `singular`,
`plural`, `route_key` and `singular_route_key` values.

View File

@ -32,5 +32,6 @@ en:
less_than: "must be less than %{count}"
less_than_or_equal_to: "must be less than or equal to %{count}"
other_than: "must be other than %{count}"
in: "must be in %{count}"
odd: "must be odd"
even: "must be even"

View File

@ -7,7 +7,7 @@ module ActiveModel
class NumericalityValidator < EachValidator # :nodoc:
CHECKS = { greater_than: :>, greater_than_or_equal_to: :>=,
equal_to: :==, less_than: :<, less_than_or_equal_to: :<=,
odd: :odd?, even: :even?, other_than: :!= }.freeze
odd: :odd?, even: :even?, other_than: :!=, in: :in? }.freeze
RESERVED_OPTIONS = CHECKS.keys + [:only_integer]
@ -16,12 +16,17 @@ module ActiveModel
HEXADECIMAL_REGEX = /\A[+-]?0[xX]/
def check_validity!
keys = CHECKS.keys - [:odd, :even]
keys = CHECKS.keys - [:odd, :even, :in]
options.slice(*keys).each do |option, value|
unless value.is_a?(Numeric) || value.is_a?(Proc) || value.is_a?(Symbol)
raise ArgumentError, ":#{option} must be a number, a symbol or a proc"
end
end
options.slice(:in).each do |option, value|
unless value.is_a?(Range)
raise ArgumentError, ":#{option} must be a range"
end
end
end
def validate_each(record, attr_name, value, precision: Float::DIG, scale: nil)
@ -51,7 +56,7 @@ module ActiveModel
option_value = record.send(option_value)
end
option_value = parse_as_number(option_value, precision, scale)
option_value = parse_as_number(option_value, precision, scale, option)
unless value.public_send(CHECKS[option], option_value)
record.errors.add(attr_name, option, **filtered_options(value).merge!(count: option_value))
@ -61,8 +66,10 @@ module ActiveModel
end
private
def parse_as_number(raw_value, precision, scale)
if raw_value.is_a?(Float)
def parse_as_number(raw_value, precision, scale, option = nil)
if option == :in
raw_value if raw_value.is_a?(Range)
elsif raw_value.is_a?(Float)
parse_float(raw_value, precision, scale)
elsif raw_value.is_a?(Numeric)
raw_value

View File

@ -7,6 +7,7 @@ require "models/person"
require "bigdecimal"
require "active_support/core_ext/big_decimal"
require "active_support/core_ext/object/inclusion"
class NumericalityValidationTest < ActiveModel::TestCase
def teardown
@ -206,6 +207,13 @@ class NumericalityValidationTest < ActiveModel::TestCase
valid!([-1, 42])
end
def test_validates_numericality_with_in
Topic.validates_numericality_of :approved, in: 1..3
invalid!([0, 4])
valid!([1, 2, 3])
end
def test_validates_numericality_with_other_than_using_string_value
Topic.validates_numericality_of :approved, other_than: 0

View File

@ -528,6 +528,8 @@ constraints to acceptable values:
less than or equal to %{count}"_.
* `:other_than` - Specifies the value must be other than the supplied value.
The default error message for this option is _"must be other than %{count}"_.
* `:in` - Specifies the value must be in the supplied range.
The default error message for this option is _"must be in %{count}"_.
* `:odd` - Specifies the value must be an odd number if set to true. The
default error message for this option is _"must be odd"_.
* `:even` - Specifies the value must be an even number if set to true. The

View File

@ -993,6 +993,7 @@ So, for example, instead of the default error message `"cannot be blank"` you co
| numericality | :less_than_or_equal_to | :less_than_or_equal_to | count |
| numericality | :other_than | :other_than | count |
| numericality | :only_integer | :not_an_integer | - |
| numericality | :in | :in | count |
| numericality | :odd | :odd | - |
| numericality | :even | :even | - |