Skip to content

Commit e0a548b

Browse files
committed
handle BSON::Decimal128 specially when checking for changes
1 parent 83a051c commit e0a548b

File tree

2 files changed

+32
-1
lines changed

2 files changed

+32
-1
lines changed

lib/mongoid/attributes.rb

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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
370388
end

spec/mongoid/attributes_spec.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1729,6 +1729,19 @@
17291729
end
17301730
end
17311731
end
1732+
1733+
context 'when map_big_decimal_as_bson_decimal128 is enabled' do
1734+
config_override :map_big_decimal_to_decimal128, true
1735+
1736+
context 'when writing an identical number' do
1737+
let(:band) { Band.create!(name: 'Nirvana', sales: 123456.78).reload }
1738+
1739+
it 'does not mark the document as changed' do
1740+
band.sales = 123456.78
1741+
expect(band.changed?).to be false
1742+
end
1743+
end
1744+
end
17321745
end
17331746

17341747
describe "#typed_value_for" do

0 commit comments

Comments
 (0)