From 2486e887deac01988ba49932c16d345e2a6dd434 Mon Sep 17 00:00:00 2001 From: Michal Papis Date: Tue, 5 Jan 2021 19:42:16 +0100 Subject: [PATCH] Add validate numericality in range --- activemodel/CHANGELOG.md | 4 ++++ activemodel/lib/active_model/locale/en.yml | 1 + .../active_model/validations/numericality.rb | 17 ++++++++++++----- .../validations/numericality_validation_test.rb | 8 ++++++++ guides/source/active_record_validations.md | 2 ++ guides/source/i18n.md | 1 + 6 files changed, 28 insertions(+), 5 deletions(-) diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md index f6c41e7ca7..79b869c5d0 100644 --- a/activemodel/CHANGELOG.md +++ b/activemodel/CHANGELOG.md @@ -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. diff --git a/activemodel/lib/active_model/locale/en.yml b/activemodel/lib/active_model/locale/en.yml index 061e35dd1e..cbd3f70747 100644 --- a/activemodel/lib/active_model/locale/en.yml +++ b/activemodel/lib/active_model/locale/en.yml @@ -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" diff --git a/activemodel/lib/active_model/validations/numericality.rb b/activemodel/lib/active_model/validations/numericality.rb index da75199cc9..d9348ece1e 100644 --- a/activemodel/lib/active_model/validations/numericality.rb +++ b/activemodel/lib/active_model/validations/numericality.rb @@ -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 diff --git a/activemodel/test/cases/validations/numericality_validation_test.rb b/activemodel/test/cases/validations/numericality_validation_test.rb index a720f0fa3b..9324764755 100644 --- a/activemodel/test/cases/validations/numericality_validation_test.rb +++ b/activemodel/test/cases/validations/numericality_validation_test.rb @@ -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 diff --git a/guides/source/active_record_validations.md b/guides/source/active_record_validations.md index f6e9a4884e..5fbe76c518 100644 --- a/guides/source/active_record_validations.md +++ b/guides/source/active_record_validations.md @@ -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 diff --git a/guides/source/i18n.md b/guides/source/i18n.md index 1fac86a0e0..29ab45b2e4 100644 --- a/guides/source/i18n.md +++ b/guides/source/i18n.md @@ -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 | - |