diff --git a/lib/active_model/serializer.rb b/lib/active_model/serializer.rb index e2a6228b6..bf19b3696 100644 --- a/lib/active_model/serializer.rb +++ b/lib/active_model/serializer.rb @@ -386,6 +386,10 @@ def include!(name, options={}) if association.embed_in_root? && hash.nil? raise IncludeError.new(self.class, association.name) + elsif association.embed_in_root? and association.instance_of? Association::HasMany and association.polymorphic? + association.roots.each do |root| + merge_association hash, root, association.serializables, unique_values + end elsif association.embed_in_root? && association.embeddable? merge_association hash, association.root, association.serializables, unique_values end diff --git a/lib/active_model/serializer/associations.rb b/lib/active_model/serializer/associations.rb index 1f2b0b53f..07a79b1ac 100644 --- a/lib/active_model/serializer/associations.rb +++ b/lib/active_model/serializer/associations.rb @@ -72,10 +72,21 @@ def find_serializable(object) end class HasMany < Association #:nodoc: + def initialize(name, options={}, serializer_options={}) + super + @polymorphic = options[:polymorphic] + end + def root options[:root] || name end + def roots + object.map do |item| + pluralized_polymorphic_key_for_item(item) + end + end + def id_key "#{name.to_s.singularize}_ids".to_sym end @@ -88,20 +99,53 @@ def serializables def serialize object.map do |item| - find_serializable(item).serializable_hash + if polymorphic? + polymorphic_key = polymorphic_key_for_item(item) + + { + :type => polymorphic_key, + polymorphic_key => find_serializable(item).serializable_hash + } + else + find_serializable(item).serializable_hash + end end end def serialize_ids object.map do |item| serializer = find_serializable(item) - if serializer.respond_to?(embed_key) - serializer.send(embed_key) + id = if serializer.respond_to?(embed_key) + serializer.send(embed_key) + else + item.read_attribute_for_serialization(embed_key) + end + + if polymorphic? + polymorphic_key = polymorphic_key_for_item(item) + + { + type: polymorphic_key, + id: id + } else - item.read_attribute_for_serialization(embed_key) + id end end end + + attr_reader :polymorphic + alias polymorphic? polymorphic + + private + + def polymorphic_key_for_item(item) + item.class.to_s.underscore.to_sym + end + + def pluralized_polymorphic_key_for_item(item) + item.class.to_s.pluralize.underscore.to_sym + end end class HasOne < Association #:nodoc: diff --git a/test/serializer_test.rb b/test/serializer_test.rb index 5da28d8aa..94672299f 100644 --- a/test/serializer_test.rb +++ b/test/serializer_test.rb @@ -1124,6 +1124,52 @@ def self.to_s }, actual) end + def tests_can_handle_polymorphic_has_many_associations + email_serializer = Class.new(ActiveModel::Serializer) do + attributes :subject, :body, :id + end + + email_class = Class.new(Model) do + def self.to_s + "Email" + end + + define_method :active_model_serializer do + email_serializer + end + end + + attachment_serializer = Class.new(ActiveModel::Serializer) do + root :attachment + attributes :name, :url + embed :ids, include: true + has_many :attachables, polymorphic: true + end + + email = email_class.new id: 1, subject: 'foo', body: 'bar' + + attachment = AttachmentWithMany.new name: 'logo.png', url: 'http://example.com/logo.png', attachables: [ email ] + + actual = attachment_serializer.new(attachment, {}).as_json + + assert_equal({ + :attachment => { + :name => 'logo.png', + :url => 'http://example.com/logo.png', + :attachable_ids => [{ + :type => :email, + :id => 1 + }] + }, + + :emails => [{ + :id => 1, + :subject => "foo", + :body => "bar" + }] + }, actual) + end + def test_can_handle_polymoprhic_ids email_serializer = Class.new(ActiveModel::Serializer) do attributes :subject, :body diff --git a/test/test_fakes.rb b/test/test_fakes.rb index a0a244c13..55c4cb39a 100644 --- a/test/test_fakes.rb +++ b/test/test_fakes.rb @@ -200,3 +200,17 @@ def edible @attributes[:edible] end end + +class AttachmentWithMany < Model + def attachables + @attributes[:attachables] + end + + def readable + @attributes[:readable] + end + + def edible + @attributes[:edible] + end +end