Skip to content

Commit f26bde2

Browse files
committed
add relationship level links and meta options
1 parent 86fa7a9 commit f26bde2

File tree

5 files changed

+615
-4
lines changed

5 files changed

+615
-4
lines changed

lib/active_model/serializer/adapter/json_api.rb

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ def primary_data_for(serializer)
161161
end
162162
end
163163

164-
def relationship_value_for(serializer, options = {})
164+
def relationship_data_for(serializer, options = {})
165165
if serializer.respond_to?(:each)
166166
serializer.map { |s| resource_identifier_for(s) }
167167
else
@@ -173,16 +173,48 @@ def relationship_value_for(serializer, options = {})
173173
end
174174
end
175175

176+
def relationship_links_for(name, owner, options = {})
177+
options[:links].each_with_object({}) do |(key, link), hash|
178+
if link.is_a? Proc
179+
hash[key] = link.call(owner, name)
180+
else
181+
hash[key] = link
182+
end
183+
184+
hash
185+
end
186+
end
187+
188+
def relationship_meta_for(name, owner, options = {})
189+
if options[:meta].is_a? Proc
190+
options[:meta].call(owner, name)
191+
else
192+
options[:meta]
193+
end
194+
end
195+
176196
def relationships_for(serializer)
177197
serializer.associations.each_with_object({}) do |association, hash|
178-
hash[association.key] = { data: relationship_value_for(association.serializer, association.options) }
198+
hash[association.key] = {}
199+
200+
if association.options.fetch(:data, true)
201+
hash[association.key][:data] = relationship_data_for(association.serializer, association.options)
202+
end
203+
204+
if association.options.fetch(:links, false)
205+
hash[association.key][:links] = relationship_links_for(association.name, serializer.object, association.options)
206+
end
207+
208+
if association.options.fetch(:meta, false)
209+
hash[association.key][:meta] = relationship_meta_for(association.name, serializer.object, association.options)
210+
end
179211
end
180212
end
181213

182214
def included_resources(include_tree, primary_data)
183215
included = []
184216

185-
serializer.associations(include_tree).each do |association|
217+
serializer.associations(include_tree).select { |association| association.options.fetch(:data, true) }.each do |association|
186218
add_included_resources_for(association.serializer, include_tree[association.key], primary_data, included)
187219
end
188220

@@ -202,7 +234,7 @@ def add_included_resources_for(serializer, include_tree, primary_data, included)
202234
return if included.include?(resource_object) || primary_data.include?(resource_object)
203235
included.push(resource_object)
204236

205-
serializer.associations(include_tree).each do |association|
237+
serializer.associations(include_tree).select { |association| association.options.fetch(:data, true) }.each do |association|
206238
add_included_resources_for(association.serializer, include_tree[association.key], primary_data, included)
207239
end
208240
end
Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
require 'test_helper'
2+
3+
module ActionController
4+
module Serialization
5+
class JsonApiHasManyLinksTest < ActionController::TestCase
6+
LinkTagSerializer = Class.new(ActiveModel::Serializer) do
7+
has_many :posts, links: { related: proc { |object, name| "http://test.host/tags/#{object.id}/#{name}" } }, data: false
8+
end
9+
10+
class MyController < ActionController::Base
11+
def render_resource_with_has_many_association
12+
@tag = Tag.new(id: 1)
13+
@tag.posts = []
14+
render json: @tag, adapter: :json_api, serializer: LinkTagSerializer
15+
end
16+
end
17+
18+
tests MyController
19+
20+
def test_render_resource_with_has_many_association
21+
get :render_resource_with_has_many_association
22+
expected = {
23+
data: {
24+
id: '1',
25+
type: 'tags',
26+
relationships: {
27+
posts: {
28+
links: {
29+
related: 'http://test.host/tags/1/posts'
30+
}
31+
}
32+
}
33+
}
34+
}
35+
assert_equal expected.to_json, response.body
36+
end
37+
end
38+
39+
class JsonApiHasManyLinksWithIncludedTest < ActionController::TestCase
40+
CommentSerializer = Class.new(ActiveModel::Serializer) do
41+
attribute :text
42+
end
43+
44+
LinkPostSerializer = Class.new(ActiveModel::Serializer) do
45+
has_many :comments, links: { related: 'comments-link' }, data: true, serializer: CommentSerializer
46+
end
47+
48+
LinkTagSerializer = Class.new(ActiveModel::Serializer) do
49+
has_many :posts, links: { related: 'posts-link' }, data: true, serializer: LinkPostSerializer
50+
end
51+
52+
class MyController < ActionController::Base
53+
def render_resource_with_has_many_association
54+
@tag = Tag.new(id: 1)
55+
@post = Post.new(id: 2)
56+
@comment = Comment.new(id: 3, text: nil)
57+
@post.comments = [@comment]
58+
@tag.posts = [@post]
59+
render json: @tag,
60+
adapter: :json_api,
61+
serializer: LinkTagSerializer,
62+
include: 'posts,posts.comments'
63+
end
64+
end
65+
66+
tests MyController
67+
68+
def test_render_resource_with_has_many_association
69+
get :render_resource_with_has_many_association
70+
expected = {
71+
data: {
72+
id: '1',
73+
type: 'tags',
74+
relationships: {
75+
posts: {
76+
data: [{ id: '2', type: 'posts' }],
77+
links: {
78+
related: 'posts-link'
79+
}
80+
}
81+
}
82+
},
83+
included: [
84+
{
85+
id: '2',
86+
type: 'posts',
87+
relationships: {
88+
comments: {
89+
data: [{ id: '3', type: 'comments' }],
90+
links: {
91+
related: 'comments-link'
92+
}
93+
}
94+
}
95+
},
96+
{
97+
id: '3',
98+
type: 'comments',
99+
attributes: {
100+
text: nil
101+
}
102+
}
103+
]
104+
}
105+
106+
assert_equal expected.to_json, response.body
107+
end
108+
end
109+
110+
class JsonApiHasManyLinksWithIncludedAndNoDataTest < ActionController::TestCase
111+
LinkPostSerializer = Class.new(ActiveModel::Serializer) do
112+
has_many :comments, links: { related: 'comments-link' }, data: false
113+
end
114+
115+
LinkTagSerializer = Class.new(ActiveModel::Serializer) do
116+
has_many :posts, links: { related: 'posts-link' }, data: false, serializer: LinkPostSerializer
117+
end
118+
119+
class MyController < ActionController::Base
120+
def render_resource_with_has_many_association
121+
@tag = Tag.new(id: 1)
122+
@post = Post.new(id: 2)
123+
@comment = Comment.new(id: 3, text: nil)
124+
@post.comments = [@comment]
125+
@tag.posts = [@post]
126+
render json: @tag,
127+
adapter: :json_api,
128+
serializer: LinkTagSerializer,
129+
include: 'posts,posts.comments'
130+
end
131+
end
132+
133+
tests MyController
134+
135+
def test_render_resource_with_has_many_association
136+
get :render_resource_with_has_many_association
137+
expected = {
138+
data: {
139+
id: '1',
140+
type: 'tags',
141+
relationships: {
142+
posts: {
143+
links: {
144+
related: 'posts-link'
145+
}
146+
}
147+
}
148+
}
149+
}
150+
151+
assert_equal expected.to_json, response.body
152+
end
153+
end
154+
155+
class JsonApiHasManyLinksWithNestedIncludedAndNoDataTest < ActionController::TestCase
156+
LinkPostSerializer = Class.new(ActiveModel::Serializer) do
157+
has_many :comments, links: { related: 'comments-link' }, data: false
158+
end
159+
160+
LinkTagSerializer = Class.new(ActiveModel::Serializer) do
161+
has_many :posts, links: { related: 'posts-link' }, data: true, serializer: LinkPostSerializer
162+
end
163+
164+
class MyController < ActionController::Base
165+
def render_resource_with_has_many_association
166+
@tag = Tag.new(id: 1)
167+
@post = Post.new(id: 2)
168+
@comment = Comment.new(id: 3, text: nil)
169+
@post.comments = [@comment]
170+
@tag.posts = [@post]
171+
render json: @tag,
172+
adapter: :json_api,
173+
serializer: LinkTagSerializer,
174+
include: 'posts,posts.comments'
175+
end
176+
end
177+
178+
tests MyController
179+
180+
def test_render_resource_with_has_many_association
181+
get :render_resource_with_has_many_association
182+
expected = {
183+
data: {
184+
id: '1',
185+
type: 'tags',
186+
relationships: {
187+
posts: {
188+
data: [{ id: '2', type: 'posts' }],
189+
links: {
190+
related: 'posts-link'
191+
}
192+
}
193+
}
194+
},
195+
included: [
196+
{
197+
id: '2',
198+
type: 'posts',
199+
relationships: {
200+
comments: {
201+
links: {
202+
related: 'comments-link'
203+
}
204+
}
205+
}
206+
}
207+
]
208+
}
209+
210+
assert_equal expected.to_json, response.body
211+
end
212+
end
213+
214+
class JsonApiHasManyLinksWithDataTest < ActionController::TestCase
215+
LinkTagSerializer = Class.new(ActiveModel::Serializer) do
216+
has_many :posts, links: { related: proc { |object, name| "http://test.host/tags/#{object.id}/#{name}" } }
217+
end
218+
219+
class MyController < ActionController::Base
220+
def render_resource_with_has_many_association
221+
@tag = Tag.new(id: 1)
222+
@post = Post.new(id: 2)
223+
@tag.posts = [@post]
224+
render json: @tag, adapter: :json_api, serializer: LinkTagSerializer
225+
end
226+
end
227+
228+
tests MyController
229+
230+
def test_render_resource_with_has_many_association
231+
get :render_resource_with_has_many_association
232+
expected = {
233+
data: {
234+
id: '1',
235+
type: 'tags',
236+
relationships: {
237+
posts: {
238+
data: [{ id: '2', type: 'posts' }],
239+
links: {
240+
related: 'http://test.host/tags/1/posts'
241+
}
242+
}
243+
}
244+
}
245+
}
246+
assert_equal expected.to_json, response.body
247+
end
248+
end
249+
end
250+
end
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
require 'test_helper'
2+
3+
module ActionController
4+
module Serialization
5+
class JsonApiHasManyMetaTest < ActionController::TestCase
6+
MetaTagSerializer = Class.new(ActiveModel::Serializer) do
7+
has_many :posts, meta: proc { { page: '1' } }
8+
end
9+
10+
class MyController < ActionController::Base
11+
def render_resource_with_has_many_association
12+
@tag = Tag.new(id: 1)
13+
@tag.posts = []
14+
render json: @tag, adapter: :json_api, serializer: MetaTagSerializer
15+
end
16+
end
17+
18+
tests MyController
19+
20+
def test_render_resource_with_has_many_association
21+
get :render_resource_with_has_many_association
22+
expected = {
23+
data: {
24+
id: '1',
25+
type: 'tags',
26+
relationships: {
27+
posts: {
28+
data: [],
29+
meta: {
30+
page: '1'
31+
}
32+
}
33+
}
34+
}
35+
}
36+
assert_equal expected.to_json, response.body
37+
end
38+
end
39+
end
40+
end

0 commit comments

Comments
 (0)