From bbed12864d96952293f9157ee53f8978bffd8307 Mon Sep 17 00:00:00 2001 From: cgmckeever Date: Sun, 15 May 2016 13:19:37 -0500 Subject: [PATCH] adds polymorphic option to association definition which includes association type in serializer regen gemlock regen gemlock better variable naming rubocop fixes adds to changelog adds empty relationship and has_many polymorph tests indent test cleaning -rubocop rubocop rubocop rubocop changelog remove silly .DS fix roque failure fix --- CHANGELOG.md | 1 + docs/general/serializers.md | 1 + .../adapter/attributes.rb | 9 +- test/adapter/polymorphic_test.rb | 123 ++++++++++++++++-- test/fixtures/active_record.rb | 10 ++ test/fixtures/poro.rb | 29 ++++- 6 files changed, 159 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 153983704..9e597f269 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ Features: Fixes: - [#1700](https://github.com/rails-api/active_model_serializers/pull/1700) Support pagination link for Kaminari when no data is returned. (@iamnader) +- [#1726](https://github.com/rails-api/active_model_serializers/pull/1726) Adds polymorphic option to association definition which includes association type/nesting in serializer (@cgmckeever) Misc: - [#1673](https://github.com/rails-api/active_model_serializers/pull/1673) Adds "How to" guide on using AMS with POROs (@DrSayre) diff --git a/docs/general/serializers.md b/docs/general/serializers.md index 664799d0d..bf6307644 100644 --- a/docs/general/serializers.md +++ b/docs/general/serializers.md @@ -52,6 +52,7 @@ Where: - `if:` - `unless:` - `virtual_value:` + - `polymorphic:` defines if polymorphic relation type should be nested in serialized association. - optional: `&block` is a context that returns the association's attributes. - prevents `association_name` method from being called. - return value of block is used as the association value. diff --git a/lib/active_model_serializers/adapter/attributes.rb b/lib/active_model_serializers/adapter/attributes.rb index aae898b09..8fc8441e4 100644 --- a/lib/active_model_serializers/adapter/attributes.rb +++ b/lib/active_model_serializers/adapter/attributes.rb @@ -45,7 +45,14 @@ def relationship_value_for(association, options) return unless association.serializer && association.serializer.object opts = instance_options.merge(include: @include_tree[association.key]) - Attributes.new(association.serializer, opts).serializable_hash(options) + relationship_value = Attributes.new(association.serializer, opts).serializable_hash(options) + + if association.options[:polymorphic] && relationship_value + polymorphic_type = association.serializer.object.class.name.underscore + relationship_value = { type: polymorphic_type, polymorphic_type.to_sym => relationship_value } + end + + relationship_value end # Set @cached_attributes diff --git a/test/adapter/polymorphic_test.rb b/test/adapter/polymorphic_test.rb index 1375322c2..91459b014 100644 --- a/test/adapter/polymorphic_test.rb +++ b/test/adapter/polymorphic_test.rb @@ -8,10 +8,17 @@ class PolymorphicTest < ActiveSupport::TestCase @employee = Employee.new(id: 42, name: 'Zoop Zoopler', email: 'zoop@example.com') @picture = @employee.pictures.new(id: 1, title: 'headshot-1.jpg') @picture.imageable = @employee + end + + def serialization(resource, adapter = :attributes) + serializable(resource, adapter: adapter, serializer: PolymorphicBelongsToSerializer).as_json + end - @attributes_serialization = serializable(@picture, serializer: PolymorphicBelongsToSerializer) # uses default adapter: attributes - @json_serialization = serializable(@picture, adapter: :json, serializer: PolymorphicBelongsToSerializer) - @json_api_serialization = serializable(@picture, adapter: :json_api, serializer: PolymorphicBelongsToSerializer) + def tag_serialization(adapter = :attributes) + tag = PolyTag.new(id: 1, phrase: 'foo') + tag.object_tags << ObjectTag.new(id: 1, poly_tag_id: 1, taggable: @employee) + tag.object_tags << ObjectTag.new(id: 5, poly_tag_id: 1, taggable: @picture) + serializable(tag, adapter: adapter, serializer: PolymorphicTagSerializer, include: '*.*').as_json end def test_attributes_serialization @@ -20,31 +27,123 @@ def test_attributes_serialization id: 1, title: 'headshot-1.jpg', imageable: { - id: 42, - name: 'Zoop Zoopler' + type: 'employee', + employee: { + id: 42, + name: 'Zoop Zoopler' + } } } - assert_equal(expected, @attributes_serialization.as_json) + assert_equal(expected, serialization(@picture)) end - def test_json_serializer + def test_attributes_serialization_without_polymorphic_association + expected = + { + id: 2, + title: 'headshot-2.jpg', + imageable: nil + } + + simple_picture = Picture.new(id: 2, title: 'headshot-2.jpg') + assert_equal(expected, serialization(simple_picture)) + end + + def test_attributes_serialization_with_polymorphic_has_many + expected = + { + id: 1, + phrase: 'foo', + object_tags: [ + { + id: 1, + taggable: { + type: 'employee', + employee: { + id: 42 + } + } + }, + { + id: 5, + taggable: { + type: 'picture', + picture: { + id: 1 + } + } + } + ] + } + assert_equal(expected, tag_serialization) + end + + def test_json_serialization expected = { picture: { id: 1, title: 'headshot-1.jpg', imageable: { - id: 42, - name: 'Zoop Zoopler' + type: 'employee', + employee: { + id: 42, + name: 'Zoop Zoopler' + } } } } - assert_equal(expected, @json_serialization.as_json) + assert_equal(expected, serialization(@picture, :json)) + end + + def test_json_serialization_without_polymorphic_association + expected = + { + picture: { + id: 2, + title: 'headshot-2.jpg', + imageable: nil + } + } + + simple_picture = Picture.new(id: 2, title: 'headshot-2.jpg') + assert_equal(expected, serialization(simple_picture, :json)) + end + + def test_json_serialization_with_polymorphic_has_many + expected = + { + poly_tag: { + id: 1, + phrase: 'foo', + object_tags: [ + { + id: 1, + taggable: { + type: 'employee', + employee: { + id: 42 + } + } + }, + { + id: 5, + taggable: { + type: 'picture', + picture: { + id: 1 + } + } + } + ] + } + } + assert_equal(expected, tag_serialization(:json)) end - def test_json_api_serializer + def test_json_api_serialization expected = { data: { @@ -64,7 +163,7 @@ def test_json_api_serializer } } - assert_equal(expected, @json_api_serialization.as_json) + assert_equal(expected, serialization(@picture, :json_api)) end end end diff --git a/test/fixtures/active_record.rb b/test/fixtures/active_record.rb index 3f0b2dc09..77ac030d6 100644 --- a/test/fixtures/active_record.rb +++ b/test/fixtures/active_record.rb @@ -24,6 +24,16 @@ t.string :email t.timestamp null: false end + create_table :object_tags, force: true do |t| + t.string :poly_tag_id + t.string :taggable_type + t.string :taggable_id + t.timestamp null: false + end + create_table :poly_tags, force: true do |t| + t.string :phrase + t.timestamp null: false + end create_table :pictures, force: true do |t| t.string :title t.string :imageable_type diff --git a/test/fixtures/poro.rb b/test/fixtures/poro.rb index cfcc0ede5..71be01089 100644 --- a/test/fixtures/poro.rb +++ b/test/fixtures/poro.rb @@ -70,10 +70,21 @@ def cache_key class Employee < ActiveRecord::Base has_many :pictures, as: :imageable + has_many :object_tags, as: :taggable +end + +class ObjectTag < ActiveRecord::Base + belongs_to :poly_tag + belongs_to :taggable, polymorphic: true end class Picture < ActiveRecord::Base belongs_to :imageable, polymorphic: true + has_many :object_tags, as: :taggable +end + +class PolyTag < ActiveRecord::Base + has_many :object_tags end module Spam; end @@ -245,7 +256,23 @@ def maker PolymorphicBelongsToSerializer = Class.new(ActiveModel::Serializer) do attributes :id, :title - has_one :imageable, serializer: PolymorphicHasManySerializer + has_one :imageable, serializer: PolymorphicHasManySerializer, polymorphic: true +end + +PolymorphicSimpleSerializer = Class.new(ActiveModel::Serializer) do + attributes :id +end + +PolymorphicObjectTagSerializer = Class.new(ActiveModel::Serializer) do + attributes :id + + has_many :taggable, serializer: PolymorphicSimpleSerializer, polymorphic: true +end + +PolymorphicTagSerializer = Class.new(ActiveModel::Serializer) do + attributes :id, :phrase + + has_many :object_tags, serializer: PolymorphicObjectTagSerializer end Spam::UnrelatedLinkSerializer = Class.new(ActiveModel::Serializer) do