From d1df4c100fee09efe85acb6663bfebcfa7036485 Mon Sep 17 00:00:00 2001 From: Marcelo Lauxen Date: Wed, 21 Jul 2021 08:45:09 -0300 Subject: [PATCH] Fix dirty check for Float::NaN and BigDecimal::NaN Float::NaN and BigDecimal::NaN in Ruby are [special values](https://bugs.ruby-lang.org/issues/1720) and can't be compared with `==`. --- activemodel/CHANGELOG.md | 7 +++++++ activemodel/lib/active_model/type/helpers/numeric.rb | 10 +++++++++- activemodel/test/cases/type/decimal_test.rb | 2 ++ activemodel/test/cases/type/float_test.rb | 2 ++ 4 files changed, 20 insertions(+), 1 deletion(-) diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md index 0659793575..9138bcabad 100644 --- a/activemodel/CHANGELOG.md +++ b/activemodel/CHANGELOG.md @@ -1,3 +1,10 @@ +* Fix dirty check for Float::NaN and BigDecimal::NaN. + + Float::NaN and BigDecimal::NaN in Ruby are [special values](https://bugs.ruby-lang.org/issues/1720) + and can't be compared with `==`. + + *Marcelo Lauxen* + * Fix `to_json` for `ActiveModel::Dirty` object. Exclude `mutations_from_database` attribute from json as it lead to recursion. diff --git a/activemodel/lib/active_model/type/helpers/numeric.rb b/activemodel/lib/active_model/type/helpers/numeric.rb index 5b1db1f36b..7a5c0e681e 100644 --- a/activemodel/lib/active_model/type/helpers/numeric.rb +++ b/activemodel/lib/active_model/type/helpers/numeric.rb @@ -25,10 +25,18 @@ module ActiveModel end def changed?(old_value, _new_value, new_value_before_type_cast) # :nodoc: - super || number_to_non_number?(old_value, new_value_before_type_cast) + (super || number_to_non_number?(old_value, new_value_before_type_cast)) && + !equal_nan?(old_value, new_value_before_type_cast) end private + def equal_nan?(old_value, new_value) + (old_value.is_a?(::Float) || old_value.is_a?(BigDecimal)) && + old_value.nan? && + old_value.instance_of?(new_value.class) && + new_value.nan? + end + def number_to_non_number?(old_value, new_value_before_type_cast) old_value != nil && non_numeric_string?(new_value_before_type_cast.to_s) end diff --git a/activemodel/test/cases/type/decimal_test.rb b/activemodel/test/cases/type/decimal_test.rb index 38941ba8b5..384bae1d90 100644 --- a/activemodel/test/cases/type/decimal_test.rb +++ b/activemodel/test/cases/type/decimal_test.rb @@ -66,6 +66,8 @@ module ActiveModel assert_not type.changed?(5.0, 5.0, "5.0") assert_not type.changed?(-5.0, -5.0, "-5.0") assert_not type.changed?(5.0, 5.0, "0.5e+1") + assert_not type.changed?(BigDecimal("0.0") / 0, BigDecimal("0.0") / 0, BigDecimal("0.0") / 0) + assert type.changed?(BigDecimal("0.0") / 0, 0.0 / 0.0, 0.0 / 0.0) end def test_scale_is_applied_before_precision_to_prevent_rounding_errors diff --git a/activemodel/test/cases/type/float_test.rb b/activemodel/test/cases/type/float_test.rb index 230a8dda32..ca18e125d2 100644 --- a/activemodel/test/cases/type/float_test.rb +++ b/activemodel/test/cases/type/float_test.rb @@ -28,6 +28,8 @@ module ActiveModel assert_not type.changed?(5.0, 5.0, "5.0") assert_not type.changed?(500.0, 500.0, "0.5E+4") assert_not type.changed?(nil, nil, nil) + assert_not type.changed?(0.0 / 0.0, 0.0 / 0.0, 0.0 / 0.0) + assert type.changed?(0.0 / 0.0, BigDecimal("0.0") / 0, BigDecimal("0.0") / 0) end end end