@@ -175,7 +175,7 @@ def write_attribute(name, value)
175175 localized = fields [ field_name ] . try ( :localized? )
176176 attributes_before_type_cast [ name . to_s ] = value
177177 typed_value = typed_value_for ( field_name , value )
178- unless attributes [ field_name ] == typed_value || attribute_changed? ( field_name )
178+ unless attribute_will_not_change? ( field_name , typed_value ) || attribute_changed? ( field_name )
179179 attribute_will_change! ( field_name )
180180 end
181181 if localized
@@ -366,5 +366,23 @@ def lookup_attribute_presence(name, value)
366366 end
367367 value . present?
368368 end
369+
370+ # If `value` is a `BSON::Decimal128`, convert it to a `BigDecimal` for
371+ # comparison purposes. This is necessary because `BSON::Decimal128` does
372+ # not implement `#==` in a way that is compatible with `BigDecimal`.
373+ def normalize_value ( value )
374+ value . is_a? ( BSON ::Decimal128 ) ? value . to_d : value
375+ end
376+
377+ # Determine if the attribute will not change, by comparing the current
378+ # value with the new value. The values are normalized to account for
379+ # types that do not implement `#==` in a way that is compatible with
380+ # each other, such as `BSON::Decimal128` and `BigDecimal`.
381+ def attribute_will_not_change? ( field_name , typed_value )
382+ normalized_attribute = normalize_value ( attributes [ field_name ] )
383+ normalized_typed_value = normalize_value ( typed_value )
384+
385+ normalized_attribute == normalized_typed_value
386+ end
369387 end
370388end
0 commit comments