From 6657e3428b1dff30df78b42c1304dde35fc4de4a Mon Sep 17 00:00:00 2001 From: Jacopo Date: Tue, 5 Jan 2021 18:08:01 +0100 Subject: [PATCH] Change Numericaly validator to use round f72f743 introduces truncate(scale) in the Numericality validator. This behaviour conflicts with AR decimal type conversion, which uses round(scale) instead. Changes the Numericality validator in order to use round(scale) for consistency. --- .../lib/active_model/validations/numericality.rb | 8 +++++++- .../validations/numericality_validation_test.rb | 15 +++++++++++++++ activerecord/test/cases/validations_test.rb | 14 +++++++++++--- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/activemodel/lib/active_model/validations/numericality.rb b/activemodel/lib/active_model/validations/numericality.rb index d9348ece1e3eb..f6c15a70aa4fb 100644 --- a/activemodel/lib/active_model/validations/numericality.rb +++ b/activemodel/lib/active_model/validations/numericality.rb @@ -71,6 +71,8 @@ def parse_as_number(raw_value, precision, scale, option = nil) 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?(BigDecimal) + round(raw_value, scale) elsif raw_value.is_a?(Numeric) raw_value elsif is_integer?(raw_value) @@ -81,7 +83,11 @@ def parse_as_number(raw_value, precision, scale, option = nil) end def parse_float(raw_value, precision, scale) - (scale ? raw_value.truncate(scale) : raw_value).to_d(precision) + round(raw_value, scale).to_d(precision) + end + + def round(raw_value, scale) + scale ? raw_value.round(scale) : raw_value end def is_number?(raw_value, precision, scale) diff --git a/activerecord/test/cases/validations/numericality_validation_test.rb b/activerecord/test/cases/validations/numericality_validation_test.rb index 91cd6a0b0d1f9..d79f4a0e1ae73 100644 --- a/activerecord/test/cases/validations/numericality_validation_test.rb +++ b/activerecord/test/cases/validations/numericality_validation_test.rb @@ -103,6 +103,21 @@ def test_virtual_attribute_with_scale assert_not_predicate subject, :valid? end + def test_virtual_attribute_with_precision_and_scale + model_class.attribute(:virtual_decimal_number, :decimal, precision: 4, scale: 2) + model_class.validates_numericality_of( + :virtual_decimal_number, less_than_or_equal_to: 99.99 + ) + + subject = model_class.new(virtual_decimal_number: 99.994) + assert_equal 99.99.to_d(4), subject.virtual_decimal_number + assert_predicate subject, :valid? + + subject = model_class.new(virtual_decimal_number: 99.999) + assert_equal 100.00.to_d(4), subject.virtual_decimal_number + assert_not_predicate subject, :valid? + end + def test_aliased_attribute model_class.validates_numericality_of(:new_bank_balance, greater_or_equal_than: 0) diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb index 4f98a6b7fc196..f108d4ca65587 100644 --- a/activerecord/test/cases/validations_test.rb +++ b/activerecord/test/cases/validations_test.rb @@ -187,9 +187,17 @@ def self.model_name validates_numericality_of :wibble, greater_than_or_equal_to: BigDecimal("97.18") end - assert_not_predicate klass.new(wibble: "97.179"), :valid? - assert_not_predicate klass.new(wibble: 97.179), :valid? - assert_not_predicate klass.new(wibble: BigDecimal("97.179")), :valid? + ["97.179", 97.179, BigDecimal("97.179")].each do |raw_value| + subject = klass.new(wibble: raw_value) + assert_equal 97.18.to_d(4), subject.wibble + assert_predicate subject, :valid? + end + + ["97.174", 97.174, BigDecimal("97.174")].each do |raw_value| + subject = klass.new(wibble: raw_value) + assert_equal 97.17.to_d(4), subject.wibble + assert_not_predicate subject, :valid? + end end def test_numericality_validator_wont_be_affected_by_custom_getter