diff --git a/lib/active_model/serializer.rb b/lib/active_model/serializer.rb index 03a8fa79..74374455 100644 --- a/lib/active_model/serializer.rb +++ b/lib/active_model/serializer.rb @@ -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 diff --git a/lib/active_model/serializer/associations.rb b/lib/active_model/serializer/associations.rb index 64c68b20..74afc1ca 100644 --- a/lib/active_model/serializer/associations.rb +++ b/lib/active_model/serializer/associations.rb @@ -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 diff --git a/test/association_test.rb b/test/association_test.rb index a87e9936..f766b5ab 100644 --- a/test/association_test.rb +++ b/test/association_test.rb @@ -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 diff --git a/test/serializer_test.rb b/test/serializer_test.rb index 941d371c..1d8e3d69 100644 --- a/test/serializer_test.rb +++ b/test/serializer_test.rb @@ -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)