Skip to content

Commit

Permalink
Merge pull request #49832 from jonathanhefner/active_model-forgetting…
Browse files Browse the repository at this point in the history
…_assignment-no-dup

Do not rely on `dup` in `forgetting_assignment` optimization
  • Loading branch information
jonathanhefner committed Oct 28, 2023
2 parents e741796 + 34be48e commit 2a986b7
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 3 deletions.
6 changes: 3 additions & 3 deletions activemodel/lib/active_model/attribute.rb
Original file line number Diff line number Diff line change
Expand Up @@ -178,11 +178,11 @@ def type_cast(value)
def forgetting_assignment
# If this attribute was not persisted (with a `value_for_database`
# that might differ from `value_before_type_cast`) and `value` has not
# changed in place, we can simply dup this attribute to avoid
# deserialize / cast / serialize calls from computing the new
# changed in place, we can use the existing `value_before_type_cast`
# to avoid deserialize / cast / serialize calls from computing the new
# attribute's `value_before_type_cast`.
if !defined?(@value_for_database) && !changed_in_place?
dup
with_value_from_database(value_before_type_cast)
else
super
end
Expand Down
21 changes: 21 additions & 0 deletions activemodel/test/cases/attribute_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,27 @@ def assert_valid_value(*)
assert_not_predicate forgotten, :changed?
end

test "#forgetting_assignment on an unchanged .from_database attribute re-deserializes its value" do
deserialized_value_class = Struct.new(:id) do
def initialize_dup(*)
self.id = nil # a la ActiveRecord::Base#dup
end
end

type = Type::Value.new
type.define_singleton_method(:deserialize) do |value|
deserialized_value_class.new(value)
end

original = Attribute.from_database(:foo, 123, type)
assert_equal 123, original.value.id

forgotten = original.forgetting_assignment
assert_equal 123, forgotten.value.id

assert_not_same original.value, forgotten.value
end

test "with_value_from_user validates the value" do
type = Type::Value.new
type.define_singleton_method(:assert_valid_value) do |value|
Expand Down

0 comments on commit 2a986b7

Please sign in to comment.