From 318d6905e29483dc1281da045caa8c1240239566 Mon Sep 17 00:00:00 2001 From: Orhan Toy Date: Thu, 16 Dec 2021 22:15:01 +0100 Subject: [PATCH] Add only_numeric option to numericality validator --- .../active_model/validations/numericality.rb | 9 ++++++++- .../numericality_validation_test.rb | 20 +++++++++++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/activemodel/lib/active_model/validations/numericality.rb b/activemodel/lib/active_model/validations/numericality.rb index c7871c4ed8..166b29df47 100644 --- a/activemodel/lib/active_model/validations/numericality.rb +++ b/activemodel/lib/active_model/validations/numericality.rb @@ -11,7 +11,7 @@ module ActiveModel RANGE_CHECKS = { in: :in? } NUMBER_CHECKS = { odd: :odd?, even: :even? } - RESERVED_OPTIONS = COMPARE_CHECKS.keys + NUMBER_CHECKS.keys + RANGE_CHECKS.keys + [:only_integer] + RESERVED_OPTIONS = COMPARE_CHECKS.keys + NUMBER_CHECKS.keys + RANGE_CHECKS.keys + [:only_integer, :only_numeric] INTEGER_REGEX = /\A[+-]?\d+\z/ @@ -90,6 +90,10 @@ module ActiveModel end def is_number?(raw_value, precision, scale) + if options[:only_numeric] && !raw_value.is_a?(Numeric) + return false + end + !parse_as_number(raw_value, precision, scale).nil? rescue ArgumentError, TypeError false @@ -162,6 +166,9 @@ module ActiveModel # * :message - A custom error message (default is: "is not a number"). # * :only_integer - Specifies whether the value has to be an # integer (default is +false+). + # * :only_numeric - Specifies whether the value has to be an + # instance of Numeric (default is +false+). The default behavior is to + # attempt parsing the value if it is a String. # * :allow_nil - Skip validation if attribute is +nil+ (default is # +false+). Notice that for Integer and Float columns empty strings are # converted to +nil+. diff --git a/activemodel/test/cases/validations/numericality_validation_test.rb b/activemodel/test/cases/validations/numericality_validation_test.rb index 8fd6e706b0..37c34d6543 100644 --- a/activemodel/test/cases/validations/numericality_validation_test.rb +++ b/activemodel/test/cases/validations/numericality_validation_test.rb @@ -19,8 +19,10 @@ class NumericalityValidationTest < ActiveModel::TestCase BIGDECIMAL_STRINGS = %w(12345678901234567890.1234567890) # 30 significant digits FLOAT_STRINGS = %w(0.0 +0.0 -0.0 10.0 10.5 -10.5 -0.0001 -090.1 90.1e1 -90.1e5 -90.1e-5 90e-5) INTEGER_STRINGS = %w(0 +0 -0 10 +10 -10 0090 -090) - FLOATS = [0.0, 10.0, 10.5, -10.5, -0.0001] + FLOAT_STRINGS - INTEGERS = [0, 10, -10] + INTEGER_STRINGS + NUMERIC_FLOATS = [0.0, 10.0, 10.5, -10.5, -0.0001] + NUMERIC_INTEGERS = [0, 10, -10] + FLOATS = NUMERIC_FLOATS + FLOAT_STRINGS + INTEGERS = NUMERIC_INTEGERS + INTEGER_STRINGS BIGDECIMAL = BIGDECIMAL_STRINGS.collect! { |bd| BigDecimal(bd) } JUNK = ["not a number", "42 not a number", "0xdeadbeef", "-0xdeadbeef", "+0xdeadbeef", "0xinvalidhex", "0Xdeadbeef", "00-1", "--3", "+-3", "+3-1", "-+019.0", "12.12.13.12", "123\nnot a number"] INFINITY = [1.0 / 0.0] @@ -74,6 +76,20 @@ class NumericalityValidationTest < ActiveModel::TestCase assert_valid_values(FLOATS + INTEGERS + BIGDECIMAL + INFINITY) end + def test_validates_numericality_of_with_numeric_only + Topic.validates_numericality_of :approved, only_numeric: true + + assert_invalid_values(NIL + BLANK + JUNK + FLOAT_STRINGS + INTEGER_STRINGS) + assert_valid_values(NUMERIC_FLOATS + NUMERIC_INTEGERS + BIGDECIMAL + INFINITY) + end + + def test_validates_numericality_of_with_numeric_only_and_nil_allowed + Topic.validates_numericality_of :approved, only_numeric: true, allow_nil: true + + assert_invalid_values(JUNK + BLANK + FLOAT_STRINGS + INTEGER_STRINGS) + assert_valid_values(NIL + NUMERIC_FLOATS + NUMERIC_INTEGERS + BIGDECIMAL + INFINITY) + end + def test_validates_numericality_with_greater_than Topic.validates_numericality_of :approved, greater_than: 10