Skip to content

Commit 660e982

Browse files
committed
Merge pull request #1726 from cgmckeever/nesting-polymorphs
adds polymorphic option to association definition which includes asso…
2 parents 5e35b7b + bbed128 commit 660e982

File tree

6 files changed

+159
-14
lines changed

6 files changed

+159
-14
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Features:
1616

1717
Fixes:
1818
- [#1700](https://github.com/rails-api/active_model_serializers/pull/1700) Support pagination link for Kaminari when no data is returned. (@iamnader)
19+
- [#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)
1920

2021
Misc:
2122
- [#1673](https://github.com/rails-api/active_model_serializers/pull/1673) Adds "How to" guide on using AMS with POROs (@DrSayre)

docs/general/serializers.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ Where:
5252
- `if:`
5353
- `unless:`
5454
- `virtual_value:`
55+
- `polymorphic:` defines if polymorphic relation type should be nested in serialized association.
5556
- optional: `&block` is a context that returns the association's attributes.
5657
- prevents `association_name` method from being called.
5758
- return value of block is used as the association value.

lib/active_model_serializers/adapter/attributes.rb

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,14 @@ def relationship_value_for(association, options)
4545
return unless association.serializer && association.serializer.object
4646

4747
opts = instance_options.merge(include: @include_tree[association.key])
48-
Attributes.new(association.serializer, opts).serializable_hash(options)
48+
relationship_value = Attributes.new(association.serializer, opts).serializable_hash(options)
49+
50+
if association.options[:polymorphic] && relationship_value
51+
polymorphic_type = association.serializer.object.class.name.underscore
52+
relationship_value = { type: polymorphic_type, polymorphic_type.to_sym => relationship_value }
53+
end
54+
55+
relationship_value
4956
end
5057

5158
# Set @cached_attributes

test/adapter/polymorphic_test.rb

Lines changed: 111 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,17 @@ class PolymorphicTest < ActiveSupport::TestCase
88
@employee = Employee.new(id: 42, name: 'Zoop Zoopler', email: '[email protected]')
99
@picture = @employee.pictures.new(id: 1, title: 'headshot-1.jpg')
1010
@picture.imageable = @employee
11+
end
12+
13+
def serialization(resource, adapter = :attributes)
14+
serializable(resource, adapter: adapter, serializer: PolymorphicBelongsToSerializer).as_json
15+
end
1116

12-
@attributes_serialization = serializable(@picture, serializer: PolymorphicBelongsToSerializer) # uses default adapter: attributes
13-
@json_serialization = serializable(@picture, adapter: :json, serializer: PolymorphicBelongsToSerializer)
14-
@json_api_serialization = serializable(@picture, adapter: :json_api, serializer: PolymorphicBelongsToSerializer)
17+
def tag_serialization(adapter = :attributes)
18+
tag = PolyTag.new(id: 1, phrase: 'foo')
19+
tag.object_tags << ObjectTag.new(id: 1, poly_tag_id: 1, taggable: @employee)
20+
tag.object_tags << ObjectTag.new(id: 5, poly_tag_id: 1, taggable: @picture)
21+
serializable(tag, adapter: adapter, serializer: PolymorphicTagSerializer, include: '*.*').as_json
1522
end
1623

1724
def test_attributes_serialization
@@ -20,31 +27,123 @@ def test_attributes_serialization
2027
id: 1,
2128
title: 'headshot-1.jpg',
2229
imageable: {
23-
id: 42,
24-
name: 'Zoop Zoopler'
30+
type: 'employee',
31+
employee: {
32+
id: 42,
33+
name: 'Zoop Zoopler'
34+
}
2535
}
2636
}
2737

28-
assert_equal(expected, @attributes_serialization.as_json)
38+
assert_equal(expected, serialization(@picture))
2939
end
3040

31-
def test_json_serializer
41+
def test_attributes_serialization_without_polymorphic_association
42+
expected =
43+
{
44+
id: 2,
45+
title: 'headshot-2.jpg',
46+
imageable: nil
47+
}
48+
49+
simple_picture = Picture.new(id: 2, title: 'headshot-2.jpg')
50+
assert_equal(expected, serialization(simple_picture))
51+
end
52+
53+
def test_attributes_serialization_with_polymorphic_has_many
54+
expected =
55+
{
56+
id: 1,
57+
phrase: 'foo',
58+
object_tags: [
59+
{
60+
id: 1,
61+
taggable: {
62+
type: 'employee',
63+
employee: {
64+
id: 42
65+
}
66+
}
67+
},
68+
{
69+
id: 5,
70+
taggable: {
71+
type: 'picture',
72+
picture: {
73+
id: 1
74+
}
75+
}
76+
}
77+
]
78+
}
79+
assert_equal(expected, tag_serialization)
80+
end
81+
82+
def test_json_serialization
3283
expected =
3384
{
3485
picture: {
3586
id: 1,
3687
title: 'headshot-1.jpg',
3788
imageable: {
38-
id: 42,
39-
name: 'Zoop Zoopler'
89+
type: 'employee',
90+
employee: {
91+
id: 42,
92+
name: 'Zoop Zoopler'
93+
}
4094
}
4195
}
4296
}
4397

44-
assert_equal(expected, @json_serialization.as_json)
98+
assert_equal(expected, serialization(@picture, :json))
99+
end
100+
101+
def test_json_serialization_without_polymorphic_association
102+
expected =
103+
{
104+
picture: {
105+
id: 2,
106+
title: 'headshot-2.jpg',
107+
imageable: nil
108+
}
109+
}
110+
111+
simple_picture = Picture.new(id: 2, title: 'headshot-2.jpg')
112+
assert_equal(expected, serialization(simple_picture, :json))
113+
end
114+
115+
def test_json_serialization_with_polymorphic_has_many
116+
expected =
117+
{
118+
poly_tag: {
119+
id: 1,
120+
phrase: 'foo',
121+
object_tags: [
122+
{
123+
id: 1,
124+
taggable: {
125+
type: 'employee',
126+
employee: {
127+
id: 42
128+
}
129+
}
130+
},
131+
{
132+
id: 5,
133+
taggable: {
134+
type: 'picture',
135+
picture: {
136+
id: 1
137+
}
138+
}
139+
}
140+
]
141+
}
142+
}
143+
assert_equal(expected, tag_serialization(:json))
45144
end
46145

47-
def test_json_api_serializer
146+
def test_json_api_serialization
48147
expected =
49148
{
50149
data: {
@@ -64,7 +163,7 @@ def test_json_api_serializer
64163
}
65164
}
66165

67-
assert_equal(expected, @json_api_serialization.as_json)
166+
assert_equal(expected, serialization(@picture, :json_api))
68167
end
69168
end
70169
end

test/fixtures/active_record.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,16 @@
2424
t.string :email
2525
t.timestamp null: false
2626
end
27+
create_table :object_tags, force: true do |t|
28+
t.string :poly_tag_id
29+
t.string :taggable_type
30+
t.string :taggable_id
31+
t.timestamp null: false
32+
end
33+
create_table :poly_tags, force: true do |t|
34+
t.string :phrase
35+
t.timestamp null: false
36+
end
2737
create_table :pictures, force: true do |t|
2838
t.string :title
2939
t.string :imageable_type

test/fixtures/poro.rb

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,21 @@ def cache_key
7070

7171
class Employee < ActiveRecord::Base
7272
has_many :pictures, as: :imageable
73+
has_many :object_tags, as: :taggable
74+
end
75+
76+
class ObjectTag < ActiveRecord::Base
77+
belongs_to :poly_tag
78+
belongs_to :taggable, polymorphic: true
7379
end
7480

7581
class Picture < ActiveRecord::Base
7682
belongs_to :imageable, polymorphic: true
83+
has_many :object_tags, as: :taggable
84+
end
85+
86+
class PolyTag < ActiveRecord::Base
87+
has_many :object_tags
7788
end
7889

7990
module Spam; end
@@ -245,7 +256,23 @@ def maker
245256
PolymorphicBelongsToSerializer = Class.new(ActiveModel::Serializer) do
246257
attributes :id, :title
247258

248-
has_one :imageable, serializer: PolymorphicHasManySerializer
259+
has_one :imageable, serializer: PolymorphicHasManySerializer, polymorphic: true
260+
end
261+
262+
PolymorphicSimpleSerializer = Class.new(ActiveModel::Serializer) do
263+
attributes :id
264+
end
265+
266+
PolymorphicObjectTagSerializer = Class.new(ActiveModel::Serializer) do
267+
attributes :id
268+
269+
has_many :taggable, serializer: PolymorphicSimpleSerializer, polymorphic: true
270+
end
271+
272+
PolymorphicTagSerializer = Class.new(ActiveModel::Serializer) do
273+
attributes :id, :phrase
274+
275+
has_many :object_tags, serializer: PolymorphicObjectTagSerializer
249276
end
250277

251278
Spam::UnrelatedLinkSerializer = Class.new(ActiveModel::Serializer) do

0 commit comments

Comments
 (0)