Skip to content

Commit 2a986b7

Browse files
Merge pull request rails#49832 from jonathanhefner/active_model-forgetting_assignment-no-dup
Do not rely on `dup` in `forgetting_assignment` optimization
2 parents e741796 + 34be48e commit 2a986b7

File tree

2 files changed

+24
-3
lines changed

2 files changed

+24
-3
lines changed

activemodel/lib/active_model/attribute.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -178,11 +178,11 @@ def type_cast(value)
178178
def forgetting_assignment
179179
# If this attribute was not persisted (with a `value_for_database`
180180
# that might differ from `value_before_type_cast`) and `value` has not
181-
# changed in place, we can simply dup this attribute to avoid
182-
# deserialize / cast / serialize calls from computing the new
181+
# changed in place, we can use the existing `value_before_type_cast`
182+
# to avoid deserialize / cast / serialize calls from computing the new
183183
# attribute's `value_before_type_cast`.
184184
if !defined?(@value_for_database) && !changed_in_place?
185-
dup
185+
with_value_from_database(value_before_type_cast)
186186
else
187187
super
188188
end

activemodel/test/cases/attribute_test.rb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,27 @@ def assert_valid_value(*)
285285
assert_not_predicate forgotten, :changed?
286286
end
287287

288+
test "#forgetting_assignment on an unchanged .from_database attribute re-deserializes its value" do
289+
deserialized_value_class = Struct.new(:id) do
290+
def initialize_dup(*)
291+
self.id = nil # a la ActiveRecord::Base#dup
292+
end
293+
end
294+
295+
type = Type::Value.new
296+
type.define_singleton_method(:deserialize) do |value|
297+
deserialized_value_class.new(value)
298+
end
299+
300+
original = Attribute.from_database(:foo, 123, type)
301+
assert_equal 123, original.value.id
302+
303+
forgotten = original.forgetting_assignment
304+
assert_equal 123, forgotten.value.id
305+
306+
assert_not_same original.value, forgotten.value
307+
end
308+
288309
test "with_value_from_user validates the value" do
289310
type = Type::Value.new
290311
type.define_singleton_method(:assert_valid_value) do |value|

0 commit comments

Comments
 (0)