mirror of
https://github.com/ditkrg/active_model_serializers.git
synced 2026-01-22 22:06:50 +00:00
When objects are sideloaded multiple times, serialize them only once
To achieve this, we make the following change when sideloading: Instead
of serializing associations and discarding duplicate *hashes*, we
memorize the *objects* (records) that we have already serialized, and
only serialize those that are new.
This change is mostly transparent, and brings down serialization time
from 3.1 seconds to 1.0 seconds on my set of sample data.
There is one change in the behavior: If you sideload the same object
multiple times, and it yields different hashes, like so:
embed :ids, include: true
has_many :comments
has_many :recent_comments, root: comments, serializer: CommentShortSerializer
then previously, it would be included multiple times, whereas now, the
first hash wins. (I haven't actually tested this.) I don't know that
either option is preferable. It's not covered by the test suite, and I
think it's an edge case that is OK to ignore entirely.
This commit is contained in:
parent
28ee88ca9a
commit
ee3cec3d0c
@ -310,7 +310,7 @@ module ActiveModel
|
||||
if association.embed_in_root? && hash.nil?
|
||||
raise IncludeError.new(self.class, association.name)
|
||||
elsif association.embed_in_root? && association.embeddable?
|
||||
merge_association hash, association.root, association.serialize_many, unique_values
|
||||
merge_association hash, association.root, association.serializables, unique_values
|
||||
end
|
||||
elsif association.embed_objects?
|
||||
node[association.key] = association.serialize
|
||||
@ -326,13 +326,15 @@ module ActiveModel
|
||||
# a unique list of all of the objects that are already in the Array. This
|
||||
# avoids the need to scan through the Array looking for entries every time
|
||||
# we want to merge a new list of values.
|
||||
def merge_association(hash, key, value, unique_values)
|
||||
if current_value = unique_values[key]
|
||||
current_value.merge! value
|
||||
hash[key] = current_value.to_a
|
||||
elsif value
|
||||
hash[key] = value
|
||||
unique_values[key] = OrderedSet.new(value)
|
||||
def merge_association(hash, key, serializables, unique_values)
|
||||
already_serialized = (unique_values[key] ||= {})
|
||||
serializable_hashes = (hash[key] ||= [])
|
||||
|
||||
serializables.each do |serializable|
|
||||
unless already_serialized.include? serializable.object
|
||||
already_serialized[serializable.object] = true
|
||||
serializable_hashes << serializable.serializable_hash
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -99,7 +99,12 @@ module ActiveModel
|
||||
find_serializable(item).serializable_hash
|
||||
end
|
||||
end
|
||||
alias serialize_many serialize
|
||||
|
||||
def serializables
|
||||
associated_object.map do |item|
|
||||
find_serializable(item)
|
||||
end
|
||||
end
|
||||
|
||||
def serialize_ids
|
||||
# Use pluck or select_columns if available
|
||||
@ -149,9 +154,9 @@ module ActiveModel
|
||||
end
|
||||
end
|
||||
|
||||
def serialize_many
|
||||
def serializables
|
||||
object = associated_object
|
||||
value = object && find_serializable(object).serializable_hash
|
||||
value = object && find_serializable(object)
|
||||
value ? [value] : []
|
||||
end
|
||||
|
||||
|
||||
@ -284,6 +284,29 @@ class AssociationTest < ActiveModel::TestCase
|
||||
]
|
||||
}, @root_hash)
|
||||
end
|
||||
|
||||
def test_embed_ids_include_true_does_not_serialize_multiple_times
|
||||
@post.recent_comment = @comment
|
||||
|
||||
@post_serializer_class.class_eval do
|
||||
has_one :comment, :embed => :ids, :include => true
|
||||
has_one :recent_comment, :embed => :ids, :include => true, :root => :comments
|
||||
end
|
||||
|
||||
# Count how often the @comment record is serialized.
|
||||
serialized_times = 0
|
||||
@comment.class_eval do
|
||||
define_method :read_attribute_for_serialization, lambda { |name|
|
||||
serialized_times += 1 if name == :body
|
||||
super(name)
|
||||
}
|
||||
end
|
||||
|
||||
include_bare! :comment
|
||||
include_bare! :recent_comment
|
||||
|
||||
assert_equal 1, serialized_times
|
||||
end
|
||||
end
|
||||
|
||||
class InclusionTest < AssociationTest
|
||||
|
||||
@ -73,11 +73,13 @@ class SerializerTest < ActiveModel::TestCase
|
||||
|
||||
class CommentSerializer
|
||||
def initialize(comment, options={})
|
||||
@comment = comment
|
||||
@object = comment
|
||||
end
|
||||
|
||||
attr_reader :object
|
||||
|
||||
def serializable_hash
|
||||
{ :title => @comment.read_attribute_for_serialization(:title) }
|
||||
{ :title => @object.read_attribute_for_serialization(:title) }
|
||||
end
|
||||
|
||||
def as_json(options=nil)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user