Skip to content

Commit

Permalink
Fix dirty check for Float::NaN and BigDecimal::NaN
Browse files Browse the repository at this point in the history
Float::NaN and BigDecimal::NaN in Ruby are [special values](https://bugs.ruby-lang.org/issues/1720) and can't be compared with `==`.
  • Loading branch information
marcelolx committed Jul 22, 2021
1 parent 5d78c54 commit d1df4c1
Show file tree
Hide file tree
Showing 4 changed files with 20 additions and 1 deletion.
7 changes: 7 additions & 0 deletions 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.
Expand Down
10 changes: 9 additions & 1 deletion activemodel/lib/active_model/type/helpers/numeric.rb
Expand Up @@ -25,10 +25,18 @@ def cast(value)
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
Expand Down
2 changes: 2 additions & 0 deletions activemodel/test/cases/type/decimal_test.rb
Expand Up @@ -66,6 +66,8 @@ def test_changed?
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
Expand Down
2 changes: 2 additions & 0 deletions activemodel/test/cases/type/float_test.rb
Expand Up @@ -28,6 +28,8 @@ def test_changing_float
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
Expand Down

0 comments on commit d1df4c1

Please sign in to comment.