Merge pull request #46231 from jonathanhefner/active_model-memoize-value_for_database

Avoid unnecessary `serialize` calls after save
This commit is contained in:
Jonathan Hefner 2022-10-16 17:13:21 -05:00 committed by GitHub
commit caf9413604
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 50 additions and 6 deletions

View File

@ -53,7 +53,10 @@ module ActiveModel
end
def value_for_database
type.serialize(value)
if !defined?(@value_for_database) || type.changed_in_place?(@value_for_database, value)
@value_for_database = _value_for_database
end
@value_for_database
end
def serializable?(&block)
@ -159,6 +162,10 @@ module ActiveModel
assigned? && type.changed?(original_value, value, value_before_type_cast)
end
def _value_for_database
type.serialize(value)
end
def _original_value_for_database
type.serialize(original_value)
end
@ -179,13 +186,14 @@ module ActiveModel
type.cast(value)
end
def value_for_database
Type::SerializeCastValue.serialize(type, value)
end
def came_from_user?
!type.value_constructed_by_mass_assignment?(value_before_type_cast)
end
private
def _value_for_database
Type::SerializeCastValue.serialize(type, value)
end
end
class WithCastValue < Attribute # :nodoc:

View File

@ -5,6 +5,10 @@ require "cases/helper"
module ActiveModel
class AttributeTest < ActiveModel::TestCase
class InscribingType
def changed_in_place?(raw_old_value, new_value)
false
end
def cast(value)
"cast(#{value})"
end
@ -96,6 +100,37 @@ module ActiveModel
assert_equal "serialize_cast_value(cast(whatever))", attribute.value_for_database
end
test "value_for_database is memoized" do
count = 0
@type.define_singleton_method(:serialize) do |value|
count += 1
nil
end
attribute = Attribute.from_user(nil, "whatever", @type)
attribute.value_for_database
attribute.value_for_database
assert_equal 1, count
end
test "value_for_database is recomputed when value changes in place" do
count = 0
@type.define_singleton_method(:serialize) do |value|
count += 1
nil
end
@type.define_singleton_method(:changed_in_place?) do |*|
true
end
attribute = Attribute.from_user(nil, "whatever", @type)
attribute.value_for_database
attribute.value_for_database
assert_equal 2, count
end
test "duping dups the value" do
attribute = Attribute.from_database(nil, "a value", @type)

View File

@ -10,7 +10,8 @@ module ActiveRecord
end
def value_for_database
@value_for_database ||= super
@value_for_database = _value_for_database unless defined?(@value_for_database)
@value_for_database
end
def with_cast_value(value)