Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adds polymorphic option to association definition which includes asso… #1726

Merged
merged 1 commit into from
May 17, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions docs/general/serializers.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
9 changes: 8 additions & 1 deletion lib/active_model_serializers/adapter/attributes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@beauby good call, added check for empty relationship which wont wrap the resultant in a polymorphic type if there is no association (corresponding tests added for :json and :attributes adapters)

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
Expand Down
123 changes: 111 additions & 12 deletions test/adapter/polymorphic_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pulled out into a helper method, as serialization variations grew by 2 more for empty relationship testing

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
Expand All @@ -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
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@beauby 'heterogeneous has-many" tests for :json and :attribute adapters added. If my understanding was correct, all the plumbing of the Attributes class acted as expected, tests demonstrate the nesting (which I feel is in alignment with previous versions [https://github.com//pull/1726#issuecomment-219323927])

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'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nested assoication

}
}
}
}

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: {
Expand All @@ -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
Expand Down
10 changes: 10 additions & 0 deletions test/fixtures/active_record.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
29 changes: 28 additions & 1 deletion test/fixtures/poro.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There were other tests using a non-active record version of Tags. Didnt want to do to much upheaval to that pattern so made a new Tag Class

end

module Spam; end
Expand Down Expand Up @@ -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
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

optional association polymorphic: flag

end

PolymorphicSimpleSerializer = Class.new(ActiveModel::Serializer) do
attributes :id
end
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

simple serializer to demonstrate polymorph inclusions (as they didnt have their own)


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
Expand Down