jsonapi-serializers 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +417 -0
- data/Rakefile +2 -0
- data/jsonapi-serializers.gemspec +26 -0
- data/lib/jsonapi-serializers/attributes.rb +58 -0
- data/lib/jsonapi-serializers/serializer.rb +435 -0
- data/lib/jsonapi-serializers/version.rb +5 -0
- data/lib/jsonapi-serializers.rb +11 -0
- data/spec/serializer_spec.rb +630 -0
- data/spec/spec_helper.rb +19 -0
- data/spec/support/factory.rb +26 -0
- data/spec/support/serializers.rb +98 -0
- metadata +134 -0
@@ -0,0 +1,630 @@
|
|
1
|
+
describe JSONAPI::Serializer do
|
2
|
+
def serialize_primary(object, options = {})
|
3
|
+
# Note: intentional high-coupling to protected method for tests.
|
4
|
+
JSONAPI::Serializer.send(:serialize_primary, object, options)
|
5
|
+
end
|
6
|
+
|
7
|
+
describe 'internal-only serialize_primary' do
|
8
|
+
it 'serializes nil to nil' do
|
9
|
+
# Spec: Primary data MUST be either:
|
10
|
+
# - a single resource object or null, for requests that target single resources
|
11
|
+
# http://jsonapi.org/format/#document-structure-top-level
|
12
|
+
primary_data = serialize_primary(nil, {serializer: MyApp::PostSerializer})
|
13
|
+
expect(primary_data).to be_nil
|
14
|
+
end
|
15
|
+
it 'can serialize primary data for a simple object' do
|
16
|
+
post = create(:post)
|
17
|
+
primary_data = serialize_primary(post, {serializer: MyApp::SimplestPostSerializer})
|
18
|
+
expect(primary_data).to eq({
|
19
|
+
'id' => '1',
|
20
|
+
'type' => 'posts',
|
21
|
+
'attributes' => {
|
22
|
+
'title' => 'Title for Post 1',
|
23
|
+
'long-content' => 'Body for Post 1',
|
24
|
+
},
|
25
|
+
'links' => {
|
26
|
+
'self' => '/posts/1',
|
27
|
+
},
|
28
|
+
})
|
29
|
+
end
|
30
|
+
it 'can serialize primary data for a simple object with a long name' do
|
31
|
+
long_comment = create(:long_comment, post: create(:post))
|
32
|
+
primary_data = serialize_primary(long_comment, {serializer: MyApp::LongCommentSerializer})
|
33
|
+
expect(primary_data).to eq({
|
34
|
+
'id' => '1',
|
35
|
+
'type' => 'long-comments',
|
36
|
+
'attributes' => {
|
37
|
+
'body' => 'Body for LongComment 1',
|
38
|
+
},
|
39
|
+
'links' => {
|
40
|
+
'self' => '/long-comments/1',
|
41
|
+
'user' => {
|
42
|
+
'self' => '/long-comments/1/links/user',
|
43
|
+
'related' => '/long-comments/1/user',
|
44
|
+
},
|
45
|
+
'post' => {
|
46
|
+
'self' => '/long-comments/1/links/post',
|
47
|
+
'related' => '/long-comments/1/post',
|
48
|
+
},
|
49
|
+
},
|
50
|
+
})
|
51
|
+
end
|
52
|
+
it 'can serialize primary data for a simple object with resource-level metadata' do
|
53
|
+
post = create(:post)
|
54
|
+
primary_data = serialize_primary(post, {serializer: MyApp::PostSerializerWithMetadata})
|
55
|
+
expect(primary_data).to eq({
|
56
|
+
'id' => '1',
|
57
|
+
'type' => 'posts',
|
58
|
+
'attributes' => {
|
59
|
+
'title' => 'Title for Post 1',
|
60
|
+
'long-content' => 'Body for Post 1',
|
61
|
+
},
|
62
|
+
'links' => {
|
63
|
+
'self' => '/posts/1',
|
64
|
+
},
|
65
|
+
'meta' => {
|
66
|
+
'copyright' => 'Copyright 2015 Example Corp.',
|
67
|
+
'authors' => [
|
68
|
+
'Aliens',
|
69
|
+
],
|
70
|
+
},
|
71
|
+
})
|
72
|
+
end
|
73
|
+
context 'without any linkage includes (default)' do
|
74
|
+
it 'can serialize primary data for an object with to-one and to-many relationships' do
|
75
|
+
post = create(:post)
|
76
|
+
primary_data = serialize_primary(post, {serializer: MyApp::PostSerializer})
|
77
|
+
expect(primary_data).to eq({
|
78
|
+
'id' => '1',
|
79
|
+
'type' => 'posts',
|
80
|
+
'attributes' => {
|
81
|
+
'title' => 'Title for Post 1',
|
82
|
+
'long-content' => 'Body for Post 1',
|
83
|
+
},
|
84
|
+
'links' => {
|
85
|
+
'self' => '/posts/1',
|
86
|
+
# Both to-one and to-many links are present, but neither include linkage:
|
87
|
+
'author' => {
|
88
|
+
'self' => '/posts/1/links/author',
|
89
|
+
'related' => '/posts/1/author',
|
90
|
+
},
|
91
|
+
'long-comments' => {
|
92
|
+
'self' => '/posts/1/links/long-comments',
|
93
|
+
'related' => '/posts/1/long-comments',
|
94
|
+
},
|
95
|
+
},
|
96
|
+
})
|
97
|
+
end
|
98
|
+
end
|
99
|
+
context 'with linkage includes' do
|
100
|
+
it 'can serialize primary data for a null to-one relationship' do
|
101
|
+
post = create(:post, author: nil)
|
102
|
+
options = {
|
103
|
+
serializer: MyApp::PostSerializer,
|
104
|
+
include_linkages: ['author', 'long-comments'],
|
105
|
+
}
|
106
|
+
primary_data = serialize_primary(post, options)
|
107
|
+
expect(primary_data).to eq({
|
108
|
+
'id' => '1',
|
109
|
+
'type' => 'posts',
|
110
|
+
'attributes' => {
|
111
|
+
'title' => 'Title for Post 1',
|
112
|
+
'long-content' => 'Body for Post 1',
|
113
|
+
},
|
114
|
+
'links' => {
|
115
|
+
'self' => '/posts/1',
|
116
|
+
'author' => {
|
117
|
+
'self' => '/posts/1/links/author',
|
118
|
+
'related' => '/posts/1/author',
|
119
|
+
# Spec: Resource linkage MUST be represented as one of the following:
|
120
|
+
# - null for empty to-one relationships.
|
121
|
+
# http://jsonapi.org/format/#document-structure-resource-relationships
|
122
|
+
'linkage' => nil,
|
123
|
+
},
|
124
|
+
'long-comments' => {
|
125
|
+
'self' => '/posts/1/links/long-comments',
|
126
|
+
'related' => '/posts/1/long-comments',
|
127
|
+
'linkage' => [],
|
128
|
+
},
|
129
|
+
},
|
130
|
+
})
|
131
|
+
end
|
132
|
+
it 'can serialize primary data for a simple to-one relationship' do
|
133
|
+
post = create(:post, :with_author)
|
134
|
+
options = {
|
135
|
+
serializer: MyApp::PostSerializer,
|
136
|
+
include_linkages: ['author', 'long-comments'],
|
137
|
+
}
|
138
|
+
primary_data = serialize_primary(post, options)
|
139
|
+
expect(primary_data).to eq({
|
140
|
+
'id' => '1',
|
141
|
+
'type' => 'posts',
|
142
|
+
'attributes' => {
|
143
|
+
'title' => 'Title for Post 1',
|
144
|
+
'long-content' => 'Body for Post 1',
|
145
|
+
},
|
146
|
+
'links' => {
|
147
|
+
'self' => '/posts/1',
|
148
|
+
'author' => {
|
149
|
+
'self' => '/posts/1/links/author',
|
150
|
+
'related' => '/posts/1/author',
|
151
|
+
# Spec: Resource linkage MUST be represented as one of the following:
|
152
|
+
# - a 'linkage object' (defined below) for non-empty to-one relationships.
|
153
|
+
# http://jsonapi.org/format/#document-structure-resource-relationships
|
154
|
+
'linkage' => {
|
155
|
+
'type' => 'users',
|
156
|
+
'id' => '1',
|
157
|
+
},
|
158
|
+
},
|
159
|
+
'long-comments' => {
|
160
|
+
'self' => '/posts/1/links/long-comments',
|
161
|
+
'related' => '/posts/1/long-comments',
|
162
|
+
'linkage' => [],
|
163
|
+
},
|
164
|
+
},
|
165
|
+
})
|
166
|
+
end
|
167
|
+
it 'can serialize primary data for an empty to-many relationship' do
|
168
|
+
post = create(:post, long_comments: [])
|
169
|
+
options = {
|
170
|
+
serializer: MyApp::PostSerializer,
|
171
|
+
include_linkages: ['author', 'long-comments'],
|
172
|
+
}
|
173
|
+
primary_data = serialize_primary(post, options)
|
174
|
+
expect(primary_data).to eq({
|
175
|
+
'id' => '1',
|
176
|
+
'type' => 'posts',
|
177
|
+
'attributes' => {
|
178
|
+
'title' => 'Title for Post 1',
|
179
|
+
'long-content' => 'Body for Post 1',
|
180
|
+
},
|
181
|
+
'links' => {
|
182
|
+
'self' => '/posts/1',
|
183
|
+
'author' => {
|
184
|
+
'self' => '/posts/1/links/author',
|
185
|
+
'related' => '/posts/1/author',
|
186
|
+
'linkage' => nil,
|
187
|
+
},
|
188
|
+
'long-comments' => {
|
189
|
+
'self' => '/posts/1/links/long-comments',
|
190
|
+
'related' => '/posts/1/long-comments',
|
191
|
+
# Spec: Resource linkage MUST be represented as one of the following:
|
192
|
+
# - an empty array ([]) for empty to-many relationships.
|
193
|
+
# http://jsonapi.org/format/#document-structure-resource-relationships
|
194
|
+
'linkage' => [],
|
195
|
+
},
|
196
|
+
},
|
197
|
+
})
|
198
|
+
end
|
199
|
+
it 'can serialize primary data for a simple to-many relationship' do
|
200
|
+
long_comments = create_list(:long_comment, 2)
|
201
|
+
post = create(:post, long_comments: long_comments)
|
202
|
+
options = {
|
203
|
+
serializer: MyApp::PostSerializer,
|
204
|
+
include_linkages: ['author', 'long-comments'],
|
205
|
+
}
|
206
|
+
primary_data = serialize_primary(post, options)
|
207
|
+
expect(primary_data).to eq({
|
208
|
+
'id' => '1',
|
209
|
+
'type' => 'posts',
|
210
|
+
'attributes' => {
|
211
|
+
'title' => 'Title for Post 1',
|
212
|
+
'long-content' => 'Body for Post 1',
|
213
|
+
},
|
214
|
+
'links' => {
|
215
|
+
'self' => '/posts/1',
|
216
|
+
'author' => {
|
217
|
+
'self' => '/posts/1/links/author',
|
218
|
+
'related' => '/posts/1/author',
|
219
|
+
'linkage' => nil,
|
220
|
+
},
|
221
|
+
'long-comments' => {
|
222
|
+
'self' => '/posts/1/links/long-comments',
|
223
|
+
'related' => '/posts/1/long-comments',
|
224
|
+
# Spec: Resource linkage MUST be represented as one of the following:
|
225
|
+
# - an array of linkage objects for non-empty to-many relationships.
|
226
|
+
# http://jsonapi.org/format/#document-structure-resource-relationships
|
227
|
+
'linkage' => [
|
228
|
+
{
|
229
|
+
'type' => 'long-comments',
|
230
|
+
'id' => '1',
|
231
|
+
},
|
232
|
+
{
|
233
|
+
'type' => 'long-comments',
|
234
|
+
'id' => '2',
|
235
|
+
},
|
236
|
+
],
|
237
|
+
},
|
238
|
+
},
|
239
|
+
})
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
describe 'JSONAPI::Serializer.serialize' do
|
245
|
+
# The following tests rely on the fact that serialize_primary has been tested above, so object
|
246
|
+
# primary data is not explicitly tested here. If things are broken, look above here first.
|
247
|
+
|
248
|
+
it 'can serialize a nil object' do
|
249
|
+
expect(JSONAPI::Serializer.serialize(nil)).to eq({'data' => nil})
|
250
|
+
end
|
251
|
+
it 'can serialize a nil object with includes' do
|
252
|
+
# Also, the include argument is not validated in this case because we don't know the type.
|
253
|
+
data = JSONAPI::Serializer.serialize(nil, include: ['fake'])
|
254
|
+
expect(data).to eq({'data' => nil, 'included' => []})
|
255
|
+
end
|
256
|
+
it 'can serialize an empty array' do
|
257
|
+
# Also, the include argument is not validated in this case because we don't know the type.
|
258
|
+
data = JSONAPI::Serializer.serialize([], is_collection: true, include: ['fake'])
|
259
|
+
expect(data).to eq({'data' => [], 'included' => []})
|
260
|
+
end
|
261
|
+
it 'can serialize a simple object' do
|
262
|
+
post = create(:post)
|
263
|
+
expect(JSONAPI::Serializer.serialize(post)).to eq({
|
264
|
+
'data' => serialize_primary(post, {serializer: MyApp::PostSerializer}),
|
265
|
+
})
|
266
|
+
end
|
267
|
+
it 'can serialize a collection' do
|
268
|
+
posts = create_list(:post, 2)
|
269
|
+
expect(JSONAPI::Serializer.serialize(posts, is_collection: true)).to eq({
|
270
|
+
'data' => [
|
271
|
+
serialize_primary(posts.first, {serializer: MyApp::PostSerializer}),
|
272
|
+
serialize_primary(posts.last, {serializer: MyApp::PostSerializer}),
|
273
|
+
],
|
274
|
+
})
|
275
|
+
end
|
276
|
+
it 'raises AmbiguousCollectionError if is_collection is not passed' do
|
277
|
+
posts = create_list(:post, 2)
|
278
|
+
error = JSONAPI::Serializer::AmbiguousCollectionError
|
279
|
+
expect { JSONAPI::Serializer.serialize(posts) }.to raise_error(error)
|
280
|
+
end
|
281
|
+
it 'can serialize a nil object when given serializer' do
|
282
|
+
options = {serializer: MyApp::PostSerializer}
|
283
|
+
expect(JSONAPI::Serializer.serialize(nil, options)).to eq({'data' => nil})
|
284
|
+
end
|
285
|
+
it 'can serialize an empty array when given serializer' do
|
286
|
+
options = {is_collection: true, serializer: MyApp::PostSerializer}
|
287
|
+
expect(JSONAPI::Serializer.serialize([], options)).to eq({'data' => []})
|
288
|
+
end
|
289
|
+
it 'can serialize a simple object when given serializer' do
|
290
|
+
post = create(:post)
|
291
|
+
options = {serializer: MyApp::SimplestPostSerializer}
|
292
|
+
expect(JSONAPI::Serializer.serialize(post, options)).to eq({
|
293
|
+
'data' => serialize_primary(post, {serializer: MyApp::SimplestPostSerializer}),
|
294
|
+
})
|
295
|
+
end
|
296
|
+
it 'handles include of nil to-one relationship with compound document' do
|
297
|
+
post = create(:post)
|
298
|
+
|
299
|
+
expected_primary_data = serialize_primary(post, {
|
300
|
+
serializer: MyApp::PostSerializer,
|
301
|
+
include_linkages: ['author'],
|
302
|
+
})
|
303
|
+
expect(JSONAPI::Serializer.serialize(post, include: ['author'])).to eq({
|
304
|
+
'data' => expected_primary_data,
|
305
|
+
'included' => [],
|
306
|
+
})
|
307
|
+
end
|
308
|
+
it 'handles include of simple to-one relationship with compound document' do
|
309
|
+
post = create(:post, :with_author)
|
310
|
+
|
311
|
+
expected_primary_data = serialize_primary(post, {
|
312
|
+
serializer: MyApp::PostSerializer,
|
313
|
+
include_linkages: ['author'],
|
314
|
+
})
|
315
|
+
expect(JSONAPI::Serializer.serialize(post, include: ['author'])).to eq({
|
316
|
+
'data' => expected_primary_data,
|
317
|
+
'included' => [
|
318
|
+
serialize_primary(post.author, {serializer: MyApp::UserSerializer}),
|
319
|
+
],
|
320
|
+
})
|
321
|
+
end
|
322
|
+
it 'handles include of empty to-many relationships with compound document' do
|
323
|
+
post = create(:post, :with_author, long_comments: [])
|
324
|
+
|
325
|
+
expected_primary_data = serialize_primary(post, {
|
326
|
+
serializer: MyApp::PostSerializer,
|
327
|
+
include_linkages: ['long-comments'],
|
328
|
+
})
|
329
|
+
expect(JSONAPI::Serializer.serialize(post, include: ['long-comments'])).to eq({
|
330
|
+
'data' => expected_primary_data,
|
331
|
+
'included' => [],
|
332
|
+
})
|
333
|
+
end
|
334
|
+
it 'handles include of to-many relationships with compound document' do
|
335
|
+
long_comments = create_list(:long_comment, 2)
|
336
|
+
post = create(:post, :with_author, long_comments: long_comments)
|
337
|
+
|
338
|
+
expected_primary_data = serialize_primary(post, {
|
339
|
+
serializer: MyApp::PostSerializer,
|
340
|
+
include_linkages: ['long-comments'],
|
341
|
+
})
|
342
|
+
expect(JSONAPI::Serializer.serialize(post, include: ['long-comments'])).to eq({
|
343
|
+
'data' => expected_primary_data,
|
344
|
+
'included' => [
|
345
|
+
serialize_primary(long_comments.first, {serializer: MyApp::LongCommentSerializer}),
|
346
|
+
serialize_primary(long_comments.last, {serializer: MyApp::LongCommentSerializer}),
|
347
|
+
],
|
348
|
+
})
|
349
|
+
end
|
350
|
+
it 'only includes one copy of each referenced relationship' do
|
351
|
+
long_comment = create(:long_comment)
|
352
|
+
long_comments = [long_comment, long_comment]
|
353
|
+
post = create(:post, :with_author, long_comments: long_comments)
|
354
|
+
|
355
|
+
expected_primary_data = serialize_primary(post, {
|
356
|
+
serializer: MyApp::PostSerializer,
|
357
|
+
include_linkages: ['long-comments'],
|
358
|
+
})
|
359
|
+
expect(JSONAPI::Serializer.serialize(post, include: ['long-comments'])).to eq({
|
360
|
+
'data' => expected_primary_data,
|
361
|
+
'included' => [
|
362
|
+
serialize_primary(long_comment, {serializer: MyApp::LongCommentSerializer}),
|
363
|
+
],
|
364
|
+
})
|
365
|
+
end
|
366
|
+
it 'handles circular-referencing relationships with compound document' do
|
367
|
+
long_comments = create_list(:long_comment, 2)
|
368
|
+
post = create(:post, :with_author, long_comments: long_comments)
|
369
|
+
|
370
|
+
# Make sure each long-comment has a circular reference back to the post.
|
371
|
+
long_comments.each { |c| c.post = post }
|
372
|
+
|
373
|
+
expected_primary_data = serialize_primary(post, {
|
374
|
+
serializer: MyApp::PostSerializer,
|
375
|
+
include_linkages: ['long-comments'],
|
376
|
+
})
|
377
|
+
expect(JSONAPI::Serializer.serialize(post, include: ['long-comments'])).to eq({
|
378
|
+
'data' => expected_primary_data,
|
379
|
+
'included' => [
|
380
|
+
serialize_primary(post.long_comments.first, {serializer: MyApp::LongCommentSerializer}),
|
381
|
+
serialize_primary(post.long_comments.last, {serializer: MyApp::LongCommentSerializer}),
|
382
|
+
],
|
383
|
+
})
|
384
|
+
end
|
385
|
+
it 'errors if include is not a defined attribute' do
|
386
|
+
user = create(:user)
|
387
|
+
expect { JSONAPI::Serializer.serialize(user, include: ['fake-attr']) }.to raise_error
|
388
|
+
end
|
389
|
+
it 'handles recursive loading of relationships' do
|
390
|
+
user = create(:user)
|
391
|
+
long_comments = create_list(:long_comment, 2, user: user)
|
392
|
+
post = create(:post, :with_author, long_comments: long_comments)
|
393
|
+
# Make sure each long-comment has a circular reference back to the post.
|
394
|
+
long_comments.each { |c| c.post = post }
|
395
|
+
|
396
|
+
expected_data = {
|
397
|
+
# Note that in this case the primary data does not include linkage for 'long-comments',
|
398
|
+
# forcing clients to still have to request linkage from long-comments and post. This is an
|
399
|
+
# odd but valid data state because the user requested to only include the leaf author node,
|
400
|
+
# and we only automatically expose direct children linkages if they match given includes.
|
401
|
+
#
|
402
|
+
# Spec: Resource linkage in a compound document allows a client to link together
|
403
|
+
# all of the included resource objects without having to GET any relationship URLs.
|
404
|
+
# http://jsonapi.org/format/#document-structure-resource-relationships
|
405
|
+
#
|
406
|
+
# Also, spec: A request for comments.author should not automatically also include
|
407
|
+
# comments in the response. This can happen if the client already has the comments locally,
|
408
|
+
# and now wants to fetch the associated authors without fetching the comments again.
|
409
|
+
# http://jsonapi.org/format/#fetching-includes
|
410
|
+
'data' => serialize_primary(post, {serializer: MyApp::PostSerializer}),
|
411
|
+
'included' => [
|
412
|
+
# Only the author is included:
|
413
|
+
serialize_primary(post.author, {serializer: MyApp::UserSerializer}),
|
414
|
+
],
|
415
|
+
}
|
416
|
+
includes = ['long-comments.post.author']
|
417
|
+
actual_data = JSONAPI::Serializer.serialize(post, include: ['long-comments.post.author'])
|
418
|
+
# Multiple expectations for better diff output for debugging.
|
419
|
+
expect(actual_data['data']).to eq(expected_data['data'])
|
420
|
+
expect(actual_data['included']).to eq(expected_data['included'])
|
421
|
+
expect(actual_data).to eq(expected_data)
|
422
|
+
end
|
423
|
+
it 'handles recursive loading of multiple to-one relationships on children' do
|
424
|
+
first_user = create(:user)
|
425
|
+
second_user = create(:user)
|
426
|
+
first_comment = create(:long_comment, user: first_user)
|
427
|
+
second_comment = create(:long_comment, user: second_user)
|
428
|
+
long_comments = [first_comment, second_comment]
|
429
|
+
post = create(:post, :with_author, long_comments: long_comments)
|
430
|
+
# Make sure each long-comment has a circular reference back to the post.
|
431
|
+
long_comments.each { |c| c.post = post }
|
432
|
+
|
433
|
+
expected_data = {
|
434
|
+
# Same note about primary data linkages as above.
|
435
|
+
'data' => serialize_primary(post, {serializer: MyApp::PostSerializer}),
|
436
|
+
'included' => [
|
437
|
+
serialize_primary(first_user, {serializer: MyApp::UserSerializer}),
|
438
|
+
serialize_primary(second_user, {serializer: MyApp::UserSerializer}),
|
439
|
+
],
|
440
|
+
}
|
441
|
+
includes = ['long-comments.user']
|
442
|
+
actual_data = JSONAPI::Serializer.serialize(post, include: includes)
|
443
|
+
|
444
|
+
# Multiple expectations for better diff output for debugging.
|
445
|
+
expect(actual_data['data']).to eq(expected_data['data'])
|
446
|
+
expect(actual_data['included']).to eq(expected_data['included'])
|
447
|
+
expect(actual_data).to eq(expected_data)
|
448
|
+
end
|
449
|
+
it 'includes linkage in compounded resources only if the immediate parent was also included' do
|
450
|
+
comment_user = create(:user)
|
451
|
+
long_comments = [create(:long_comment, user: comment_user)]
|
452
|
+
post = create(:post, :with_author, long_comments: long_comments)
|
453
|
+
|
454
|
+
expected_primary_data = serialize_primary(post, {
|
455
|
+
serializer: MyApp::PostSerializer,
|
456
|
+
include_linkages: ['long-comments'],
|
457
|
+
})
|
458
|
+
expected_data = {
|
459
|
+
'data' => expected_primary_data,
|
460
|
+
'included' => [
|
461
|
+
serialize_primary(long_comments.first, {
|
462
|
+
serializer: MyApp::LongCommentSerializer,
|
463
|
+
include_linkages: ['user'],
|
464
|
+
}),
|
465
|
+
# Note: post.author does not show up here because it was not included.
|
466
|
+
serialize_primary(comment_user, {serializer: MyApp::UserSerializer}),
|
467
|
+
],
|
468
|
+
}
|
469
|
+
includes = ['long-comments', 'long-comments.user']
|
470
|
+
actual_data = JSONAPI::Serializer.serialize(post, include: includes)
|
471
|
+
|
472
|
+
# Multiple expectations for better diff output for debugging.
|
473
|
+
expect(actual_data['data']).to eq(expected_data['data'])
|
474
|
+
expect(actual_data['included']).to eq(expected_data['included'])
|
475
|
+
expect(actual_data).to eq(expected_data)
|
476
|
+
end
|
477
|
+
it 'handles recursive loading of to-many relationships with overlapping include paths' do
|
478
|
+
user = create(:user)
|
479
|
+
long_comments = create_list(:long_comment, 2, user: user)
|
480
|
+
post = create(:post, :with_author, long_comments: long_comments)
|
481
|
+
# Make sure each long-comment has a circular reference back to the post.
|
482
|
+
long_comments.each { |c| c.post = post }
|
483
|
+
|
484
|
+
expected_primary_data = serialize_primary(post, {
|
485
|
+
serializer: MyApp::PostSerializer,
|
486
|
+
include_linkages: ['long-comments'],
|
487
|
+
})
|
488
|
+
expected_data = {
|
489
|
+
'data' => expected_primary_data,
|
490
|
+
'included' => [
|
491
|
+
serialize_primary(long_comments.first, {serializer: MyApp::LongCommentSerializer}),
|
492
|
+
serialize_primary(long_comments.last, {serializer: MyApp::LongCommentSerializer}),
|
493
|
+
serialize_primary(post.author, {serializer: MyApp::UserSerializer}),
|
494
|
+
],
|
495
|
+
}
|
496
|
+
# Also test that it handles string include arguments.
|
497
|
+
includes = 'long-comments, long-comments.post.author'
|
498
|
+
actual_data = JSONAPI::Serializer.serialize(post, include: includes)
|
499
|
+
|
500
|
+
# Multiple expectations for better diff output for debugging.
|
501
|
+
expect(actual_data['data']).to eq(expected_data['data'])
|
502
|
+
expect(actual_data['included']).to eq(expected_data['included'])
|
503
|
+
expect(actual_data).to eq(expected_data)
|
504
|
+
end
|
505
|
+
|
506
|
+
context 'on collection' do
|
507
|
+
it 'handles include of has_many relationships with compound document' do
|
508
|
+
long_comments = create_list(:long_comment, 2)
|
509
|
+
posts = create_list(:post, 2, :with_author, long_comments: long_comments)
|
510
|
+
|
511
|
+
expected_primary_data = JSONAPI::Serializer.send(:serialize_primary_multi, posts, {
|
512
|
+
serializer: MyApp::PostSerializer,
|
513
|
+
include_linkages: ['long-comments'],
|
514
|
+
})
|
515
|
+
data = JSONAPI::Serializer.serialize(posts, is_collection: true, include: ['long-comments'])
|
516
|
+
expect(data).to eq({
|
517
|
+
'data' => expected_primary_data,
|
518
|
+
'included' => [
|
519
|
+
serialize_primary(long_comments.first, {serializer: MyApp::LongCommentSerializer}),
|
520
|
+
serialize_primary(long_comments.last, {serializer: MyApp::LongCommentSerializer}),
|
521
|
+
],
|
522
|
+
})
|
523
|
+
end
|
524
|
+
end
|
525
|
+
end
|
526
|
+
|
527
|
+
describe 'serialize (class method)' do
|
528
|
+
it 'delegates to module method but overrides serializer' do
|
529
|
+
post = create(:post)
|
530
|
+
expect(MyApp::SimplestPostSerializer.serialize(post)).to eq({
|
531
|
+
'data' => serialize_primary(post, {serializer: MyApp::SimplestPostSerializer}),
|
532
|
+
})
|
533
|
+
end
|
534
|
+
end
|
535
|
+
|
536
|
+
describe 'internal-only parse_relationship_paths' do
|
537
|
+
it 'correctly handles empty arrays' do
|
538
|
+
result = JSONAPI::Serializer.send(:parse_relationship_paths, [])
|
539
|
+
expect(result).to eq({})
|
540
|
+
end
|
541
|
+
it 'correctly handles single-level relationship paths' do
|
542
|
+
result = JSONAPI::Serializer.send(:parse_relationship_paths, ['foo'])
|
543
|
+
expect(result).to eq({
|
544
|
+
'foo' => {_include: true}
|
545
|
+
})
|
546
|
+
end
|
547
|
+
it 'correctly handles multi-level relationship paths' do
|
548
|
+
result = JSONAPI::Serializer.send(:parse_relationship_paths, ['foo.bar'])
|
549
|
+
expect(result).to eq({
|
550
|
+
'foo' => {'bar' => {_include: true}}
|
551
|
+
})
|
552
|
+
end
|
553
|
+
it 'correctly handles multi-level relationship paths with same parent' do
|
554
|
+
paths = ['foo', 'foo.bar']
|
555
|
+
result = JSONAPI::Serializer.send(:parse_relationship_paths, paths)
|
556
|
+
expect(result).to eq({
|
557
|
+
'foo' => {_include: true, 'bar' => {_include: true}}
|
558
|
+
})
|
559
|
+
end
|
560
|
+
it 'correctly handles multi-level relationship paths with different parent' do
|
561
|
+
paths = ['foo', 'bar', 'bar.baz']
|
562
|
+
result = JSONAPI::Serializer.send(:parse_relationship_paths, paths)
|
563
|
+
expect(result).to eq({
|
564
|
+
'foo' => {_include: true},
|
565
|
+
'bar' => {_include: true, 'baz' => {_include: true}},
|
566
|
+
})
|
567
|
+
end
|
568
|
+
it 'correctly handles three-leveled path' do
|
569
|
+
paths = ['foo', 'foo.bar', 'foo.bar.baz']
|
570
|
+
result = JSONAPI::Serializer.send(:parse_relationship_paths, paths)
|
571
|
+
expect(result).to eq({
|
572
|
+
'foo' => {_include: true, 'bar' => {_include: true, 'baz' => {_include: true}}}
|
573
|
+
})
|
574
|
+
end
|
575
|
+
it 'correctly handles three-leveled path with skipped middle' do
|
576
|
+
paths = ['foo', 'foo.bar.baz']
|
577
|
+
result = JSONAPI::Serializer.send(:parse_relationship_paths, paths)
|
578
|
+
expect(result).to eq({
|
579
|
+
'foo' => {_include: true, 'bar' => {'baz' => {_include: true}}}
|
580
|
+
})
|
581
|
+
end
|
582
|
+
end
|
583
|
+
describe 'if/unless handling with contexts' do
|
584
|
+
it 'can be used to show/hide attributes' do
|
585
|
+
post = create(:post)
|
586
|
+
options = {serializer: MyApp::PostSerializerWithContextHandling}
|
587
|
+
|
588
|
+
options[:context] = {show_body: false}
|
589
|
+
data = JSONAPI::Serializer.serialize(post, options)
|
590
|
+
expect(data['data']['attributes']).to_not have_key('body')
|
591
|
+
|
592
|
+
options[:context] = {show_body: true}
|
593
|
+
data = JSONAPI::Serializer.serialize(post, options)
|
594
|
+
expect(data['data']['attributes']).to have_key('body')
|
595
|
+
expect(data['data']['attributes']['body']).to eq('Body for Post 1')
|
596
|
+
|
597
|
+
options[:context] = {hide_body: true}
|
598
|
+
data = JSONAPI::Serializer.serialize(post, options)
|
599
|
+
expect(data['data']['attributes']).to_not have_key('body')
|
600
|
+
|
601
|
+
options[:context] = {hide_body: false}
|
602
|
+
data = JSONAPI::Serializer.serialize(post, options)
|
603
|
+
expect(data['data']['attributes']).to have_key('body')
|
604
|
+
expect(data['data']['attributes']['body']).to eq('Body for Post 1')
|
605
|
+
|
606
|
+
options[:context] = {show_body: false, hide_body: false}
|
607
|
+
data = JSONAPI::Serializer.serialize(post, options)
|
608
|
+
expect(data['data']['attributes']).to_not have_key('body')
|
609
|
+
|
610
|
+
options[:context] = {show_body: true, hide_body: false}
|
611
|
+
data = JSONAPI::Serializer.serialize(post, options)
|
612
|
+
expect(data['data']['attributes']).to have_key('body')
|
613
|
+
expect(data['data']['attributes']['body']).to eq('Body for Post 1')
|
614
|
+
|
615
|
+
# Remember: attribute is configured as if: show_body?, unless: hide_body?
|
616
|
+
# and the results should be logically AND'd together:
|
617
|
+
options[:context] = {show_body: false, hide_body: true}
|
618
|
+
data = JSONAPI::Serializer.serialize(post, options)
|
619
|
+
expect(data['data']['attributes']).to_not have_key('body')
|
620
|
+
|
621
|
+
options[:context] = {show_body: true, hide_body: true}
|
622
|
+
data = JSONAPI::Serializer.serialize(post, options)
|
623
|
+
expect(data['data']['attributes']).to_not have_key('body')
|
624
|
+
end
|
625
|
+
end
|
626
|
+
describe 'context' do
|
627
|
+
xit 'is correctly passed through all serializers' do
|
628
|
+
end
|
629
|
+
end
|
630
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'factory_girl'
|
2
|
+
require './lib/jsonapi-serializers'
|
3
|
+
require './spec/support/serializers'
|
4
|
+
|
5
|
+
RSpec.configure do |config|
|
6
|
+
config.include FactoryGirl::Syntax::Methods
|
7
|
+
|
8
|
+
config.mock_with :rspec do |mocks|
|
9
|
+
mocks.verify_partial_doubles = true
|
10
|
+
end
|
11
|
+
|
12
|
+
config.before(:each) do
|
13
|
+
# Force FactoryGirl sequences to be fully reset before each test run to simplify ID testing
|
14
|
+
# since we are not using a database or real fixtures. Inside of each test case, IDs will
|
15
|
+
# increment per type starting at 1.
|
16
|
+
FactoryGirl.reload
|
17
|
+
load './spec/support/factory.rb'
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'factory_girl'
|
2
|
+
|
3
|
+
FactoryGirl.define do
|
4
|
+
factory :post, class: MyApp::Post do
|
5
|
+
skip_create
|
6
|
+
sequence(:id) {|n| n }
|
7
|
+
sequence(:title) {|n| "Title for Post #{n}" }
|
8
|
+
sequence(:body) {|n| "Body for Post #{n}" }
|
9
|
+
|
10
|
+
trait :with_author do
|
11
|
+
association :author, factory: :user
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
factory :long_comment, class: MyApp::LongComment do
|
16
|
+
skip_create
|
17
|
+
sequence(:id) {|n| n }
|
18
|
+
sequence(:body) {|n| "Body for LongComment #{n}" }
|
19
|
+
end
|
20
|
+
|
21
|
+
factory :user, class: MyApp::User do
|
22
|
+
skip_create
|
23
|
+
sequence(:id) {|n| n }
|
24
|
+
sequence(:name) {|n| "User ##{n}"}
|
25
|
+
end
|
26
|
+
end
|