jsonapi-resources 0.3.3 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +274 -102
  3. data/jsonapi-resources.gemspec +1 -0
  4. data/lib/jsonapi-resources.rb +15 -0
  5. data/lib/jsonapi/active_record_operations_processor.rb +21 -10
  6. data/lib/jsonapi/acts_as_resource_controller.rb +175 -0
  7. data/lib/jsonapi/configuration.rb +11 -0
  8. data/lib/jsonapi/error_codes.rb +7 -4
  9. data/lib/jsonapi/exceptions.rb +23 -15
  10. data/lib/jsonapi/formatter.rb +5 -5
  11. data/lib/jsonapi/include_directives.rb +67 -0
  12. data/lib/jsonapi/operation.rb +185 -65
  13. data/lib/jsonapi/operation_result.rb +38 -5
  14. data/lib/jsonapi/operation_results.rb +33 -0
  15. data/lib/jsonapi/operations_processor.rb +49 -9
  16. data/lib/jsonapi/paginator.rb +31 -17
  17. data/lib/jsonapi/request.rb +347 -163
  18. data/lib/jsonapi/resource.rb +159 -56
  19. data/lib/jsonapi/resource_controller.rb +1 -234
  20. data/lib/jsonapi/resource_serializer.rb +55 -69
  21. data/lib/jsonapi/resources/version.rb +1 -1
  22. data/lib/jsonapi/response_document.rb +87 -0
  23. data/lib/jsonapi/routing_ext.rb +17 -11
  24. data/test/controllers/controller_test.rb +602 -326
  25. data/test/fixtures/active_record.rb +96 -6
  26. data/test/fixtures/line_items.yml +7 -1
  27. data/test/fixtures/numeros_telefone.yml +3 -0
  28. data/test/fixtures/purchase_orders.yml +6 -0
  29. data/test/integration/requests/request_test.rb +129 -60
  30. data/test/integration/routes/routes_test.rb +17 -17
  31. data/test/test_helper.rb +23 -5
  32. data/test/unit/jsonapi_request/jsonapi_request_test.rb +48 -0
  33. data/test/unit/operation/operations_processor_test.rb +242 -54
  34. data/test/unit/resource/resource_test.rb +108 -2
  35. data/test/unit/serializer/include_directives_test.rb +108 -0
  36. data/test/unit/serializer/response_document_test.rb +61 -0
  37. data/test/unit/serializer/serializer_test.rb +679 -520
  38. metadata +26 -2
@@ -132,7 +132,113 @@ class ResourceTest < ActiveSupport::TestCase
132
132
  end
133
133
  end
134
134
 
135
- def test_updateable_fields_does_not_include_id
136
- assert(!CatResource.updateable_fields.include?(:id))
135
+ def test_updatable_fields_does_not_include_id
136
+ assert(!CatResource.updatable_fields.include?(:id))
137
+ end
138
+
139
+ # TODO: Please remove after `updateable_fields` is removed
140
+ def test_updateable_fields_delegates_to_updatable_fields_with_deprecation
141
+ ActiveSupport::Deprecation.silence do
142
+ assert_empty(CatResource.updateable_fields(nil) - [:mother, :father, :name, :breed])
143
+ end
144
+ end
145
+
146
+ # TODO: Please remove after `createable_fields` is removed
147
+ def test_createable_fields_delegates_to_creatable_fields_with_deprecation
148
+ ActiveSupport::Deprecation.silence do
149
+ assert_empty(CatResource.createable_fields(nil) - [:mother, :father, :name, :breed, :id])
150
+ end
151
+ end
152
+
153
+ def test_has_many_association_filters
154
+ post_resource = PostResource.new(Post.find(1))
155
+ comments = post_resource.comments
156
+ assert_equal(2, comments.size)
157
+
158
+ # define apply_filters method on post resource to not respect filters
159
+ PostResource.instance_eval do
160
+ def apply_filters(records, filters, options)
161
+ # :nocov:
162
+ records
163
+ # :nocov:
164
+ end
165
+ end
166
+
167
+ filtered_comments = post_resource.comments({ filters: { body: 'i liked it' } })
168
+ assert_equal(1, filtered_comments.size)
169
+
170
+ # reset method to original implementation
171
+ PostResource.instance_eval do
172
+ def apply_filters(records, filters, options)
173
+ # :nocov:
174
+ super
175
+ # :nocov:
176
+ end
177
+ end
178
+ end
179
+
180
+ def test_has_many_association_sorts
181
+ post_resource = PostResource.new(Post.find(1))
182
+ comment_ids = post_resource.comments.map{|c| c.model.id }
183
+ assert_equal [1,2], comment_ids
184
+
185
+ # define apply_filters method on post resource to not respect filters
186
+ PostResource.instance_eval do
187
+ def apply_sort(records, criteria)
188
+ # :nocov:
189
+ records
190
+ # :nocov:
191
+ end
192
+ end
193
+
194
+ sorted_comment_ids = post_resource.comments(sort_criteria: [{ field: 'id', direction: 'desc'}]).map{|c| c.model.id }
195
+ assert_equal [2,1], sorted_comment_ids
196
+
197
+ # reset method to original implementation
198
+ PostResource.instance_eval do
199
+ def apply_sort(records, criteria)
200
+ # :nocov:
201
+ super
202
+ # :nocov:
203
+ end
204
+ end
205
+ end
206
+
207
+ def test_has_many_association_pagination
208
+ post_resource = PostResource.new(Post.find(1))
209
+ comments = post_resource.comments
210
+ assert_equal 2, comments.size
211
+
212
+ # define apply_filters method on post resource to not respect filters
213
+ PostResource.instance_eval do
214
+ def apply_pagination(records, criteria, order_options)
215
+ # :nocov:
216
+ records
217
+ # :nocov:
218
+ end
219
+ end
220
+
221
+ paginator_class = Class.new(JSONAPI::Paginator) do
222
+ def initialize(params)
223
+ # param parsing and validation here
224
+ @page = params.to_i
225
+ end
226
+
227
+ def apply(relation, order_options)
228
+ relation.offset(@page).limit(1)
229
+ end
230
+ end
231
+
232
+ paged_comments = post_resource.comments(paginator: paginator_class.new(1))
233
+ assert_equal 1, paged_comments.size
234
+
235
+ # reset method to original implementation
236
+ PostResource.instance_eval do
237
+ def apply_pagination(records, criteria, order_options)
238
+ # :nocov:
239
+ super
240
+ # :nocov:
241
+ end
242
+ end
137
243
  end
138
244
  end
@@ -0,0 +1,108 @@
1
+ require File.expand_path('../../../test_helper', __FILE__)
2
+ require 'jsonapi-resources'
3
+
4
+ class IncludeDirectivesTest < ActiveSupport::TestCase
5
+
6
+ def test_one_level_one_include
7
+ directives = JSONAPI::IncludeDirectives.new(['posts']).include_directives
8
+
9
+ assert_hash_equals(
10
+ {
11
+ include_related: {
12
+ posts: {
13
+ include: true,
14
+ include_related:{}
15
+ }
16
+ }
17
+ },
18
+ directives)
19
+ end
20
+
21
+ def test_one_level_multiple_includes
22
+ directives = JSONAPI::IncludeDirectives.new(['posts', 'comments', 'tags']).include_directives
23
+
24
+ assert_hash_equals(
25
+ {
26
+ include_related: {
27
+ posts: {
28
+ include: true,
29
+ include_related:{}
30
+ },
31
+ comments: {
32
+ include: true,
33
+ include_related:{}
34
+ },
35
+ tags: {
36
+ include: true,
37
+ include_related:{}
38
+ }
39
+ }
40
+ },
41
+ directives)
42
+ end
43
+
44
+ def test_two_levels_include_full_path
45
+ directives = JSONAPI::IncludeDirectives.new(['posts.comments']).include_directives
46
+
47
+ assert_hash_equals(
48
+ {
49
+ include_related: {
50
+ posts: {
51
+ include: true,
52
+ include_related:{
53
+ comments: {
54
+ include: true,
55
+ include_related:{}
56
+ }
57
+ }
58
+ }
59
+ }
60
+ },
61
+ directives)
62
+ end
63
+
64
+ def test_two_levels_include_full_path_redundant
65
+ directives = JSONAPI::IncludeDirectives.new(['posts','posts.comments']).include_directives
66
+
67
+ assert_hash_equals(
68
+ {
69
+ include_related: {
70
+ posts: {
71
+ include: true,
72
+ include_related:{
73
+ comments: {
74
+ include: true,
75
+ include_related:{}
76
+ }
77
+ }
78
+ }
79
+ }
80
+ },
81
+ directives)
82
+ end
83
+
84
+ def test_three_levels_include_full
85
+ directives = JSONAPI::IncludeDirectives.new(['posts.comments.tags']).include_directives
86
+
87
+ assert_hash_equals(
88
+ {
89
+ include_related: {
90
+ posts: {
91
+ include: true,
92
+ include_related:{
93
+ comments: {
94
+ include: true,
95
+ include_related:{
96
+ tags: {
97
+ include: true,
98
+ include_related:{}
99
+ }
100
+ }
101
+ }
102
+ }
103
+ }
104
+ }
105
+ },
106
+ directives)
107
+ end
108
+ end
@@ -0,0 +1,61 @@
1
+ require File.expand_path('../../../test_helper', __FILE__)
2
+ require 'jsonapi-resources'
3
+ require 'json'
4
+
5
+ class ResponseDocumentTest < ActionDispatch::IntegrationTest
6
+ def setup
7
+ JSONAPI.configuration.json_key_format = :dasherized_key
8
+ JSONAPI.configuration.route_format = :dasherized_route
9
+ end
10
+
11
+ def create_response_document(operation_results, resource_klass)
12
+ JSONAPI::ResponseDocument.new(
13
+ operation_results,
14
+ {
15
+ primary_resource_klass: resource_klass
16
+ }
17
+ )
18
+ end
19
+
20
+ def test_response_document
21
+ operations = [
22
+ JSONAPI::CreateResourceOperation.new(PlanetResource, data: {attributes: {'name' => 'Earth 2.0'}}),
23
+ JSONAPI::CreateResourceOperation.new(PlanetResource, data: {attributes: {'name' => 'Vulcan'}})
24
+ ]
25
+
26
+ request = JSONAPI::Request.new
27
+ request.operations = operations
28
+
29
+ op = BasicOperationsProcessor.new()
30
+ operation_results = op.process(request)
31
+
32
+ response_doc = create_response_document(operation_results, PlanetResource)
33
+
34
+ assert_equal :created, response_doc.status
35
+ contents = response_doc.contents
36
+ assert contents.is_a?(Hash)
37
+ assert contents[:data].is_a?(Array)
38
+ assert_equal 2, contents[:data].size
39
+ end
40
+
41
+ def test_response_document_multiple_find
42
+ operations = [
43
+ JSONAPI::FindOperation.new(PostResource, filters: {id: '1'}),
44
+ JSONAPI::FindOperation.new(PostResource, filters: {id: '2'})
45
+ ]
46
+
47
+ request = JSONAPI::Request.new
48
+ request.operations = operations
49
+
50
+ op = ActiveRecordOperationsProcessor.new()
51
+ operation_results = op.process(request)
52
+
53
+ response_doc = create_response_document(operation_results, PostResource)
54
+
55
+ assert_equal :ok, response_doc.status
56
+ contents = response_doc.contents
57
+ assert contents.is_a?(Hash)
58
+ assert contents[:data].is_a?(Array)
59
+ assert_equal 2, contents[:data].size
60
+ end
61
+ end
@@ -19,42 +19,58 @@ class SerializerTest < ActionDispatch::IntegrationTest
19
19
 
20
20
  def test_serializer
21
21
 
22
+ serialized = JSONAPI::ResourceSerializer.new(
23
+ PostResource,
24
+ base_url: 'http://example.com').serialize_to_hash(PostResource.new(@post)
25
+ )
26
+
22
27
  assert_hash_equals(
23
28
  {
24
29
  data: {
25
30
  type: 'posts',
26
31
  id: '1',
27
- title: 'New post',
28
- body: 'A body!!!',
29
- subject: 'New post',
30
32
  links: {
31
33
  self: 'http://example.com/posts/1',
34
+ },
35
+ attributes: {
36
+ title: 'New post',
37
+ body: 'A body!!!',
38
+ subject: 'New post'
39
+ },
40
+ relationships: {
32
41
  section: {
33
- self: 'http://example.com/posts/1/links/section',
34
- related: 'http://example.com/posts/1/section',
35
- linkage: nil
42
+ links: {
43
+ self: 'http://example.com/posts/1/relationships/section',
44
+ related: 'http://example.com/posts/1/section'
45
+ },
46
+ data: nil
36
47
  },
37
48
  author: {
38
- self: 'http://example.com/posts/1/links/author',
39
- related: 'http://example.com/posts/1/author',
40
- linkage: {
49
+ links: {
50
+ self: 'http://example.com/posts/1/relationships/author',
51
+ related: 'http://example.com/posts/1/author'
52
+ },
53
+ data: {
41
54
  type: 'people',
42
55
  id: '1'
43
56
  }
44
57
  },
45
58
  tags: {
46
- self: 'http://example.com/posts/1/links/tags',
47
- related: 'http://example.com/posts/1/tags'
59
+ links: {
60
+ self: 'http://example.com/posts/1/relationships/tags',
61
+ related: 'http://example.com/posts/1/tags'
62
+ }
48
63
  },
49
64
  comments: {
50
- self: 'http://example.com/posts/1/links/comments',
51
- related: 'http://example.com/posts/1/comments'
65
+ links: {
66
+ self: 'http://example.com/posts/1/relationships/comments',
67
+ related: 'http://example.com/posts/1/comments'
68
+ }
52
69
  }
53
70
  }
54
71
  }
55
72
  },
56
- JSONAPI::ResourceSerializer.new(PostResource,
57
- base_url: 'http://example.com').serialize_to_hash(PostResource.new(@post))
73
+ serialized
58
74
  )
59
75
  end
60
76
 
@@ -73,27 +89,37 @@ class SerializerTest < ActionDispatch::IntegrationTest
73
89
  data: {
74
90
  type: 'posts',
75
91
  id: '1',
76
- title: 'New post',
77
- body: 'A body!!!',
78
- subject: 'New post',
79
92
  links: {
80
- self: 'http://example.com/api/v1/posts/1',
93
+ self: 'http://example.com/api/v1/posts/1'
94
+ },
95
+ attributes: {
96
+ title: 'New post',
97
+ body: 'A body!!!',
98
+ subject: 'New post'
99
+ },
100
+ relationships: {
81
101
  section: {
82
- self: 'http://example.com/api/v1/posts/1/links/section',
83
- related: 'http://example.com/api/v1/posts/1/section',
84
- linkage: nil
102
+ links:{
103
+ self: 'http://example.com/api/v1/posts/1/relationships/section',
104
+ related: 'http://example.com/api/v1/posts/1/section'
105
+ },
106
+ data: nil
85
107
  },
86
108
  writer: {
87
- self: 'http://example.com/api/v1/posts/1/links/writer',
88
- related: 'http://example.com/api/v1/posts/1/writer',
89
- linkage: {
109
+ links:{
110
+ self: 'http://example.com/api/v1/posts/1/relationships/writer',
111
+ related: 'http://example.com/api/v1/posts/1/writer'
112
+ },
113
+ data: {
90
114
  type: 'writers',
91
115
  id: '1'
92
116
  }
93
117
  },
94
118
  comments: {
95
- self: 'http://example.com/api/v1/posts/1/links/comments',
96
- related: 'http://example.com/api/v1/posts/1/comments'
119
+ links:{
120
+ self: 'http://example.com/api/v1/posts/1/relationships/comments',
121
+ related: 'http://example.com/api/v1/posts/1/comments'
122
+ }
97
123
  }
98
124
  }
99
125
  }
@@ -111,13 +137,19 @@ class SerializerTest < ActionDispatch::IntegrationTest
111
137
  data: {
112
138
  type: 'posts',
113
139
  id: '1',
114
- title: 'New post',
115
140
  links: {
116
- self: '/posts/1',
141
+ self: '/posts/1'
142
+ },
143
+ attributes: {
144
+ title: 'New post'
145
+ },
146
+ relationships: {
117
147
  author: {
118
- self: '/posts/1/links/author',
119
- related: '/posts/1/author',
120
- linkage: {
148
+ links: {
149
+ self: '/posts/1/relationships/author',
150
+ related: '/posts/1/author'
151
+ },
152
+ data: {
121
153
  type: 'people',
122
154
  id: '1'
123
155
  }
@@ -133,7 +165,7 @@ class SerializerTest < ActionDispatch::IntegrationTest
133
165
  def test_serializer_include
134
166
  serialized = JSONAPI::ResourceSerializer.new(
135
167
  PostResource,
136
- include: [:author]
168
+ include: ['author']
137
169
  ).serialize_to_hash(PostResource.new(@post))
138
170
 
139
171
  assert_hash_equals(
@@ -141,31 +173,43 @@ class SerializerTest < ActionDispatch::IntegrationTest
141
173
  data: {
142
174
  type: 'posts',
143
175
  id: '1',
144
- title: 'New post',
145
- body: 'A body!!!',
146
- subject: 'New post',
147
176
  links: {
148
- self: '/posts/1',
177
+ self: '/posts/1'
178
+ },
179
+ attributes: {
180
+ title: 'New post',
181
+ body: 'A body!!!',
182
+ subject: 'New post'
183
+ },
184
+ relationships: {
149
185
  section: {
150
- self: '/posts/1/links/section',
151
- related: '/posts/1/section',
152
- linkage: nil
186
+ links: {
187
+ self: '/posts/1/relationships/section',
188
+ related: '/posts/1/section'
189
+ },
190
+ data: nil
153
191
  },
154
192
  author: {
155
- self: '/posts/1/links/author',
156
- related: '/posts/1/author',
157
- linkage: {
193
+ links: {
194
+ self: '/posts/1/relationships/author',
195
+ related: '/posts/1/author'
196
+ },
197
+ data: {
158
198
  type: 'people',
159
199
  id: '1'
160
200
  }
161
201
  },
162
202
  tags: {
163
- self: '/posts/1/links/tags',
164
- related: '/posts/1/tags'
203
+ links: {
204
+ self: '/posts/1/relationships/tags',
205
+ related: '/posts/1/tags'
206
+ }
165
207
  },
166
208
  comments: {
167
- self: '/posts/1/links/comments',
168
- related: '/posts/1/comments'
209
+ links: {
210
+ self: '/posts/1/relationships/comments',
211
+ related: '/posts/1/comments'
212
+ }
169
213
  }
170
214
  }
171
215
  },
@@ -173,31 +217,43 @@ class SerializerTest < ActionDispatch::IntegrationTest
173
217
  {
174
218
  type: 'people',
175
219
  id: '1',
176
- name: 'Joe Author',
177
- email: 'joe@xyz.fake',
178
- dateJoined: '2013-08-07 16:25:00 -0400',
220
+ attributes: {
221
+ name: 'Joe Author',
222
+ email: 'joe@xyz.fake',
223
+ dateJoined: '2013-08-07 16:25:00 -0400'
224
+ },
179
225
  links: {
180
- self: '/people/1',
226
+ self: '/people/1'
227
+ },
228
+ relationships: {
181
229
  comments: {
182
- self: '/people/1/links/comments',
183
- related: '/people/1/comments'
230
+ links: {
231
+ self: '/people/1/relationships/comments',
232
+ related: '/people/1/comments'
233
+ }
184
234
  },
185
235
  posts: {
186
- self: '/people/1/links/posts',
187
- related: '/people/1/posts'
236
+ links: {
237
+ self: '/people/1/relationships/posts',
238
+ related: '/people/1/posts'
239
+ }
188
240
  },
189
241
  preferences: {
190
- self: '/people/1/links/preferences',
191
- related: '/people/1/preferences',
192
- linkage: {
242
+ links: {
243
+ self: '/people/1/relationships/preferences',
244
+ related: '/people/1/preferences'
245
+ },
246
+ data: {
193
247
  type: 'preferences',
194
248
  id: '1'
195
249
  }
196
250
  },
197
251
  hairCut: {
198
- self: "/people/1/links/hairCut",
199
- related: "/people/1/hairCut",
200
- linkage: nil
252
+ links: {
253
+ self: "/people/1/relationships/hairCut",
254
+ related: "/people/1/hairCut"
255
+ },
256
+ data: nil
201
257
  }
202
258
  }
203
259
  }
@@ -210,7 +266,7 @@ class SerializerTest < ActionDispatch::IntegrationTest
210
266
  def test_serializer_key_format
211
267
  serialized = JSONAPI::ResourceSerializer.new(
212
268
  PostResource,
213
- include: [:author],
269
+ include: ['author'],
214
270
  key_formatter: UnderscoredKeyFormatter
215
271
  ).serialize_to_hash(PostResource.new(@post))
216
272
 
@@ -219,31 +275,43 @@ class SerializerTest < ActionDispatch::IntegrationTest
219
275
  data: {
220
276
  type: 'posts',
221
277
  id: '1',
222
- title: 'New post',
223
- body: 'A body!!!',
224
- subject: 'New post',
278
+ attributes: {
279
+ title: 'New post',
280
+ body: 'A body!!!',
281
+ subject: 'New post'
282
+ },
225
283
  links: {
226
- self: '/posts/1',
284
+ self: '/posts/1'
285
+ },
286
+ relationships: {
227
287
  section: {
228
- self: '/posts/1/links/section',
229
- related: '/posts/1/section',
230
- linkage: nil
288
+ links: {
289
+ self: '/posts/1/relationships/section',
290
+ related: '/posts/1/section'
291
+ },
292
+ data: nil
231
293
  },
232
294
  author: {
233
- self: '/posts/1/links/author',
234
- related: '/posts/1/author',
235
- linkage: {
295
+ links: {
296
+ self: '/posts/1/relationships/author',
297
+ related: '/posts/1/author'
298
+ },
299
+ data: {
236
300
  type: 'people',
237
301
  id: '1'
238
302
  }
239
303
  },
240
304
  tags: {
241
- self: '/posts/1/links/tags',
242
- related: '/posts/1/tags'
305
+ links: {
306
+ self: '/posts/1/relationships/tags',
307
+ related: '/posts/1/tags'
308
+ }
243
309
  },
244
310
  comments: {
245
- self: '/posts/1/links/comments',
246
- related: '/posts/1/comments'
311
+ links: {
312
+ self: '/posts/1/relationships/comments',
313
+ related: '/posts/1/comments'
314
+ }
247
315
  }
248
316
  }
249
317
  },
@@ -251,31 +319,43 @@ class SerializerTest < ActionDispatch::IntegrationTest
251
319
  {
252
320
  type: 'people',
253
321
  id: '1',
254
- name: 'Joe Author',
255
- email: 'joe@xyz.fake',
256
- date_joined: '2013-08-07 16:25:00 -0400',
322
+ attributes: {
323
+ name: 'Joe Author',
324
+ email: 'joe@xyz.fake',
325
+ date_joined: '2013-08-07 16:25:00 -0400'
326
+ },
257
327
  links: {
258
- self: '/people/1',
328
+ self: '/people/1'
329
+ },
330
+ relationships: {
259
331
  comments: {
260
- self: '/people/1/links/comments',
261
- related: '/people/1/comments'
332
+ links: {
333
+ self: '/people/1/relationships/comments',
334
+ related: '/people/1/comments'
335
+ }
262
336
  },
263
337
  posts: {
264
- self: '/people/1/links/posts',
265
- related: '/people/1/posts'
338
+ links: {
339
+ self: '/people/1/relationships/posts',
340
+ related: '/people/1/posts'
341
+ }
266
342
  },
267
343
  preferences: {
268
- self: '/people/1/links/preferences',
269
- related: '/people/1/preferences',
270
- linkage: {
344
+ links: {
345
+ self: '/people/1/relationships/preferences',
346
+ related: '/people/1/preferences'
347
+ },
348
+ data: {
271
349
  type: 'preferences',
272
350
  id: '1'
273
351
  }
274
352
  },
275
353
  hair_cut: {
276
- self: '/people/1/links/hairCut',
277
- related: '/people/1/hairCut',
278
- linkage: nil
354
+ links: {
355
+ self: '/people/1/relationships/hairCut',
356
+ related: '/people/1/hairCut'
357
+ },
358
+ data: nil
279
359
  }
280
360
  }
281
361
  }
@@ -292,32 +372,44 @@ class SerializerTest < ActionDispatch::IntegrationTest
292
372
  data: {
293
373
  type: 'posts',
294
374
  id: '1',
295
- title: 'New post',
296
- body: 'A body!!!',
297
- subject: 'New post',
375
+ attributes: {
376
+ title: 'New post',
377
+ body: 'A body!!!',
378
+ subject: 'New post'
379
+ },
298
380
  links: {
299
- self: '/posts/1',
381
+ self: '/posts/1'
382
+ },
383
+ relationships: {
300
384
  section: {
301
- self: '/posts/1/links/section',
302
- related: '/posts/1/section',
303
- linkage: nil
385
+ links: {
386
+ self: '/posts/1/relationships/section',
387
+ related: '/posts/1/section'
388
+ },
389
+ data: nil
304
390
  },
305
391
  author: {
306
- self: '/posts/1/links/author',
307
- related: '/posts/1/author',
308
- linkage: {
392
+ links: {
393
+ self: '/posts/1/relationships/author',
394
+ related: '/posts/1/author'
395
+ },
396
+ data: {
309
397
  type: 'people',
310
398
  id: '1'
311
399
  }
312
400
  },
313
401
  tags: {
314
- self: '/posts/1/links/tags',
315
- related: '/posts/1/tags'
402
+ links: {
403
+ self: '/posts/1/relationships/tags',
404
+ related: '/posts/1/tags'
405
+ }
316
406
  },
317
407
  comments: {
318
- self: '/posts/1/links/comments',
319
- related: '/posts/1/comments',
320
- linkage: [
408
+ links: {
409
+ self: '/posts/1/relationships/comments',
410
+ related: '/posts/1/comments'
411
+ },
412
+ data: [
321
413
  {type: 'comments', id: '1'},
322
414
  {type: 'comments', id: '2'}
323
415
  ]
@@ -328,65 +420,93 @@ class SerializerTest < ActionDispatch::IntegrationTest
328
420
  {
329
421
  type: 'tags',
330
422
  id: '1',
331
- name: 'short',
423
+ attributes: {
424
+ name: 'short'
425
+ },
332
426
  links: {
333
- self: '/tags/1',
427
+ self: '/tags/1'
428
+ },
429
+ relationships: {
334
430
  posts: {
335
- self: '/tags/1/links/posts',
336
- related: '/tags/1/posts'
431
+ links: {
432
+ self: '/tags/1/relationships/posts',
433
+ related: '/tags/1/posts'
434
+ }
337
435
  }
338
436
  }
339
437
  },
340
438
  {
341
439
  type: 'tags',
342
440
  id: '2',
343
- name: 'whiny',
441
+ attributes: {
442
+ name: 'whiny'
443
+ },
344
444
  links: {
345
- self: '/tags/2',
445
+ self: '/tags/2'
446
+ },
447
+ relationships: {
346
448
  posts: {
347
- self: '/tags/2/links/posts',
348
- related: '/tags/2/posts'
449
+ links: {
450
+ self: '/tags/2/relationships/posts',
451
+ related: '/tags/2/posts'
452
+ }
349
453
  }
350
454
  }
351
455
  },
352
456
  {
353
457
  type: 'tags',
354
458
  id: '4',
355
- name: 'happy',
459
+ attributes: {
460
+ name: 'happy'
461
+ },
356
462
  links: {
357
- self: '/tags/4',
463
+ self: '/tags/4'
464
+ },
465
+ relationships: {
358
466
  posts: {
359
- self: '/tags/4/links/posts',
360
- related: '/tags/4/posts',
467
+ links: {
468
+ self: '/tags/4/relationships/posts',
469
+ related: '/tags/4/posts'
470
+ },
361
471
  }
362
472
  }
363
473
  },
364
474
  {
365
475
  type: 'comments',
366
476
  id: '1',
367
- body: 'what a dumb post',
477
+ attributes: {
478
+ body: 'what a dumb post'
479
+ },
368
480
  links: {
369
- self: '/comments/1',
481
+ self: '/comments/1'
482
+ },
483
+ relationships: {
370
484
  author: {
371
- self: '/comments/1/links/author',
372
- related: '/comments/1/author',
373
- linkage: {
485
+ links: {
486
+ self: '/comments/1/relationships/author',
487
+ related: '/comments/1/author'
488
+ },
489
+ data: {
374
490
  type: 'people',
375
491
  id: '1'
376
492
  }
377
493
  },
378
494
  post: {
379
- self: '/comments/1/links/post',
380
- related: '/comments/1/post',
381
- linkage: {
495
+ links: {
496
+ self: '/comments/1/relationships/post',
497
+ related: '/comments/1/post'
498
+ },
499
+ data: {
382
500
  type: 'posts',
383
501
  id: '1'
384
502
  }
385
503
  },
386
504
  tags: {
387
- self: '/comments/1/links/tags',
388
- related: '/comments/1/tags',
389
- linkage: [
505
+ links: {
506
+ self: '/comments/1/relationships/tags',
507
+ related: '/comments/1/tags'
508
+ },
509
+ data: [
390
510
  {type: 'tags', id: '1'},
391
511
  {type: 'tags', id: '2'}
392
512
  ]
@@ -396,29 +516,39 @@ class SerializerTest < ActionDispatch::IntegrationTest
396
516
  {
397
517
  type: 'comments',
398
518
  id: '2',
399
- body: 'i liked it',
519
+ attributes: {
520
+ body: 'i liked it'
521
+ },
400
522
  links: {
401
- self: '/comments/2',
523
+ self: '/comments/2'
524
+ },
525
+ relationships: {
402
526
  author: {
403
- self: '/comments/2/links/author',
404
- related: '/comments/2/author',
405
- linkage: {
527
+ links: {
528
+ self: '/comments/2/relationships/author',
529
+ related: '/comments/2/author'
530
+ },
531
+ data: {
406
532
  type: 'people',
407
533
  id: '2'
408
534
  }
409
535
  },
410
536
  post: {
411
- self: '/comments/2/links/post',
412
- related: '/comments/2/post',
413
- linkage: {
537
+ links: {
538
+ self: '/comments/2/relationships/post',
539
+ related: '/comments/2/post'
540
+ },
541
+ data: {
414
542
  type: 'posts',
415
543
  id: '1'
416
544
  }
417
545
  },
418
546
  tags: {
419
- self: '/comments/2/links/tags',
420
- related: '/comments/2/tags',
421
- linkage: [
547
+ links: {
548
+ self: '/comments/2/relationships/tags',
549
+ related: '/comments/2/tags'
550
+ },
551
+ data: [
422
552
  {type: 'tags', id: '1'},
423
553
  {type: 'tags', id: '4'}
424
554
  ]
@@ -428,156 +558,7 @@ class SerializerTest < ActionDispatch::IntegrationTest
428
558
  ]
429
559
  },
430
560
  JSONAPI::ResourceSerializer.new(PostResource,
431
- include: [:comments, 'comments.tags']).serialize_to_hash(PostResource.new(@post))
432
- )
433
- end
434
-
435
- def test_serializer_include_has_many_sub_objects_only
436
-
437
- assert_hash_equals(
438
- {
439
- data: {
440
- type: 'posts',
441
- id: '1',
442
- title: 'New post',
443
- body: 'A body!!!',
444
- subject: 'New post',
445
- links: {
446
- self: '/posts/1',
447
- section: {
448
- self: '/posts/1/links/section',
449
- related: '/posts/1/section',
450
- linkage: nil
451
- },
452
- author: {
453
- self: '/posts/1/links/author',
454
- related: '/posts/1/author',
455
- linkage: {
456
- type: 'people',
457
- id: '1'
458
- }
459
- },
460
- tags: {
461
- self: '/posts/1/links/tags',
462
- related: '/posts/1/tags'
463
- },
464
- comments: {
465
- self: '/posts/1/links/comments',
466
- related: '/posts/1/comments'
467
- }
468
- }
469
- },
470
- included: [
471
- {
472
- type: 'tags',
473
- id: '1',
474
- name: 'short',
475
- links: {
476
- self: '/tags/1',
477
- posts: {
478
- self: '/tags/1/links/posts',
479
- related: '/tags/1/posts'
480
- }
481
- }
482
- },
483
- {
484
- type: 'tags',
485
- id: '2',
486
- name: 'whiny',
487
- links: {
488
- self: '/tags/2',
489
- posts: {
490
- self: '/tags/2/links/posts',
491
- related: '/tags/2/posts'
492
- }
493
- }
494
- },
495
- {
496
- type: 'tags',
497
- id: '4',
498
- name: 'happy',
499
- links: {
500
- self: '/tags/4',
501
- posts: {
502
- self: '/tags/4/links/posts',
503
- related: '/tags/4/posts',
504
- }
505
- }
506
- }
507
- ]
508
- },
509
- JSONAPI::ResourceSerializer.new(PostResource, include: ['comments.tags']).serialize_to_hash(PostResource.new(@post))
510
- )
511
- end
512
-
513
- def test_serializer_include_has_one_sub_objects_only
514
-
515
- assert_hash_equals(
516
- {
517
- data: {
518
- type: 'posts',
519
- id: '1',
520
- title: 'New post',
521
- body: 'A body!!!',
522
- subject: 'New post',
523
- links: {
524
- self: '/posts/1',
525
- section: {
526
- self: '/posts/1/links/section',
527
- related: '/posts/1/section',
528
- linkage: nil
529
- },
530
- author: {
531
- self: '/posts/1/links/author',
532
- related: '/posts/1/author',
533
- linkage: {
534
- type: 'people',
535
- id: '1'
536
- }
537
- },
538
- tags: {
539
- self: '/posts/1/links/tags',
540
- related: '/posts/1/tags'
541
- },
542
- comments: {
543
- self: '/posts/1/links/comments',
544
- related: '/posts/1/comments'
545
- }
546
- }
547
- },
548
- included: [
549
- {
550
- type: 'comments',
551
- id: '1',
552
- body: 'what a dumb post',
553
- links: {
554
- self: '/comments/1',
555
- author: {
556
- self: '/comments/1/links/author',
557
- related: '/comments/1/author',
558
- linkage: {
559
- type: 'people',
560
- id: '1'
561
- }
562
- },
563
- post: {
564
- self: '/comments/1/links/post',
565
- related: '/comments/1/post',
566
- linkage: {
567
- type: 'posts',
568
- id: '1'
569
- }
570
- },
571
- tags: {
572
- self: '/comments/1/links/tags',
573
- related: '/comments/1/tags'
574
- }
575
- }
576
- }
577
- ]
578
- },
579
- JSONAPI::ResourceSerializer.new(PostResource,
580
- include: ['author.comments']).serialize_to_hash(PostResource.new(@post))
561
+ include: ['comments', 'comments.tags']).serialize_to_hash(PostResource.new(@post))
581
562
  )
582
563
  end
583
564
 
@@ -592,32 +573,44 @@ class SerializerTest < ActionDispatch::IntegrationTest
592
573
  data: {
593
574
  type: 'people',
594
575
  id: '2',
595
- name: 'Fred Reader',
596
- email: 'fred@xyz.fake',
597
- dateJoined: '2013-10-31 16:25:00 -0400',
576
+ attributes: {
577
+ name: 'Fred Reader',
578
+ email: 'fred@xyz.fake',
579
+ dateJoined: '2013-10-31 16:25:00 -0400'
580
+ },
598
581
  links: {
599
- self: '/people/2',
582
+ self: '/people/2'
583
+ },
584
+ relationships: {
600
585
  posts: {
601
- self: '/people/2/links/posts',
602
- related: '/people/2/posts'
586
+ links: {
587
+ self: '/people/2/relationships/posts',
588
+ related: '/people/2/posts'
589
+ }
603
590
  },
604
591
  comments: {
605
- self: '/people/2/links/comments',
606
- related: '/people/2/comments',
607
- linkage: [
592
+ links: {
593
+ self: '/people/2/relationships/comments',
594
+ related: '/people/2/comments'
595
+ },
596
+ data: [
608
597
  {type: 'comments', id: '2'},
609
598
  {type: 'comments', id: '3'}
610
599
  ]
611
600
  },
612
601
  preferences: {
613
- self: "/people/2/links/preferences",
614
- related: "/people/2/preferences",
615
- linkage: nil
602
+ links: {
603
+ self: "/people/2/relationships/preferences",
604
+ related: "/people/2/preferences"
605
+ },
606
+ data: nil
616
607
  },
617
608
  hairCut: {
618
- self: "/people/2/links/hairCut",
619
- related: "/people/2/hairCut",
620
- linkage: nil
609
+ links: {
610
+ self: "/people/2/relationships/hairCut",
611
+ related: "/people/2/hairCut"
612
+ },
613
+ data: nil
621
614
  }
622
615
  }
623
616
  },
@@ -625,56 +618,76 @@ class SerializerTest < ActionDispatch::IntegrationTest
625
618
  {
626
619
  type: 'comments',
627
620
  id: '2',
628
- body: 'i liked it',
621
+ attributes: {
622
+ body: 'i liked it'
623
+ },
629
624
  links: {
630
- self: '/comments/2',
625
+ self: '/comments/2'
626
+ },
627
+ relationships: {
631
628
  author: {
632
- self: '/comments/2/links/author',
633
- related: '/comments/2/author',
634
- linkage: {
629
+ links: {
630
+ self: '/comments/2/relationships/author',
631
+ related: '/comments/2/author'
632
+ },
633
+ data: {
635
634
  type: 'people',
636
635
  id: '2'
637
636
  }
638
637
  },
639
638
  post: {
640
- self: '/comments/2/links/post',
641
- related: '/comments/2/post',
642
- linkage: {
639
+ links: {
640
+ self: '/comments/2/relationships/post',
641
+ related: '/comments/2/post'
642
+ },
643
+ data: {
643
644
  type: 'posts',
644
645
  id: '1'
645
646
  }
646
647
  },
647
648
  tags: {
648
- self: '/comments/2/links/tags',
649
- related: '/comments/2/tags'
649
+ links: {
650
+ self: '/comments/2/relationships/tags',
651
+ related: '/comments/2/tags'
652
+ }
650
653
  }
651
654
  }
652
655
  },
653
656
  {
654
657
  type: 'comments',
655
658
  id: '3',
656
- body: 'Thanks man. Great post. But what is JR?',
659
+ attributes: {
660
+ body: 'Thanks man. Great post. But what is JR?'
661
+ },
657
662
  links: {
658
- self: '/comments/3',
663
+ self: '/comments/3'
664
+ },
665
+ relationships: {
659
666
  author: {
660
- self: '/comments/3/links/author',
661
- related: '/comments/3/author',
662
- linkage: {
667
+ links: {
668
+ self: '/comments/3/relationships/author',
669
+ related: '/comments/3/author'
670
+ },
671
+ data: {
663
672
  type: 'people',
664
673
  id: '2'
665
674
  }
666
675
  },
667
676
  post: {
668
- self: '/comments/3/links/post',
669
- related: '/comments/3/post',
670
- linkage: {
677
+ links: {
678
+ self: '/comments/3/relationships/post',
679
+ related: '/comments/3/post'
680
+ },
681
+ data: {
671
682
  type: 'posts',
672
683
  id: '2'
673
684
  }
674
685
  },
675
686
  tags: {
676
- self: '/comments/3/links/tags',
677
- related: '/comments/3/tags'
687
+ links: {
688
+ self: '/comments/3/relationships/tags',
689
+ related: '/comments/3/tags'
690
+ }
678
691
  }
679
692
  }
680
693
  }
@@ -697,32 +710,44 @@ class SerializerTest < ActionDispatch::IntegrationTest
697
710
  {
698
711
  type: 'posts',
699
712
  id: '1',
700
- title: 'New post',
701
- body: 'A body!!!',
702
- subject: 'New post',
713
+ attributes: {
714
+ title: 'New post',
715
+ body: 'A body!!!',
716
+ subject: 'New post'
717
+ },
703
718
  links: {
704
- self: '/posts/1',
719
+ self: '/posts/1'
720
+ },
721
+ relationships: {
705
722
  section: {
706
- self: '/posts/1/links/section',
707
- related: '/posts/1/section',
708
- linkage: nil
723
+ links: {
724
+ self: '/posts/1/relationships/section',
725
+ related: '/posts/1/section'
726
+ },
727
+ data: nil
709
728
  },
710
729
  author: {
711
- self: '/posts/1/links/author',
712
- related: '/posts/1/author',
713
- linkage: {
730
+ links: {
731
+ self: '/posts/1/relationships/author',
732
+ related: '/posts/1/author'
733
+ },
734
+ data: {
714
735
  type: 'people',
715
736
  id: '1'
716
737
  }
717
738
  },
718
739
  tags: {
719
- self: '/posts/1/links/tags',
720
- related: '/posts/1/tags'
740
+ links: {
741
+ self: '/posts/1/relationships/tags',
742
+ related: '/posts/1/tags'
743
+ }
721
744
  },
722
745
  comments: {
723
- self: '/posts/1/links/comments',
724
- related: '/posts/1/comments',
725
- linkage: [
746
+ links: {
747
+ self: '/posts/1/relationships/comments',
748
+ related: '/posts/1/comments'
749
+ },
750
+ data: [
726
751
  {type: 'comments', id: '1'},
727
752
  {type: 'comments', id: '2'}
728
753
  ]
@@ -732,35 +757,47 @@ class SerializerTest < ActionDispatch::IntegrationTest
732
757
  {
733
758
  type: 'posts',
734
759
  id: '2',
735
- title: 'JR Solves your serialization woes!',
736
- body: 'Use JR',
737
- subject: 'JR Solves your serialization woes!',
760
+ attributes: {
761
+ title: 'JR Solves your serialization woes!',
762
+ body: 'Use JR',
763
+ subject: 'JR Solves your serialization woes!'
764
+ },
738
765
  links: {
739
- self: '/posts/2',
766
+ self: '/posts/2'
767
+ },
768
+ relationships: {
740
769
  section: {
741
- self: '/posts/2/links/section',
742
- related: '/posts/2/section',
743
- linkage: {
770
+ links: {
771
+ self: '/posts/2/relationships/section',
772
+ related: '/posts/2/section'
773
+ },
774
+ data: {
744
775
  type: 'sections',
745
776
  id: '2'
746
777
  }
747
778
  },
748
779
  author: {
749
- self: '/posts/2/links/author',
750
- related: '/posts/2/author',
751
- linkage: {
780
+ links: {
781
+ self: '/posts/2/relationships/author',
782
+ related: '/posts/2/author'
783
+ },
784
+ data: {
752
785
  type: 'people',
753
786
  id: '1'
754
787
  }
755
788
  },
756
789
  tags: {
757
- self: '/posts/2/links/tags',
758
- related: '/posts/2/tags'
790
+ links: {
791
+ self: '/posts/2/relationships/tags',
792
+ related: '/posts/2/tags'
793
+ }
759
794
  },
760
795
  comments: {
761
- self: '/posts/2/links/comments',
762
- related: '/posts/2/comments',
763
- linkage: [
796
+ links: {
797
+ self: '/posts/2/relationships/comments',
798
+ related: '/posts/2/comments'
799
+ },
800
+ data: [
764
801
  {type: 'comments', id: '3'}
765
802
  ]
766
803
  }
@@ -771,77 +808,111 @@ class SerializerTest < ActionDispatch::IntegrationTest
771
808
  {
772
809
  type: 'tags',
773
810
  id: '1',
774
- name: 'short',
811
+ attributes: {
812
+ name: 'short'
813
+ },
775
814
  links: {
776
- self: '/tags/1',
815
+ self: '/tags/1'
816
+ },
817
+ relationships: {
777
818
  posts: {
778
- self: '/tags/1/links/posts',
779
- related: '/tags/1/posts'
819
+ links: {
820
+ self: '/tags/1/relationships/posts',
821
+ related: '/tags/1/posts'
822
+ }
780
823
  }
781
824
  }
782
825
  },
783
826
  {
784
827
  type: 'tags',
785
828
  id: '2',
786
- name: 'whiny',
829
+ attributes: {
830
+ name: 'whiny'
831
+ },
787
832
  links: {
788
- self: '/tags/2',
833
+ self: '/tags/2'
834
+ },
835
+ relationships: {
789
836
  posts: {
790
- self: '/tags/2/links/posts',
791
- related: '/tags/2/posts'
837
+ links: {
838
+ self: '/tags/2/relationships/posts',
839
+ related: '/tags/2/posts'
840
+ }
792
841
  }
793
842
  }
794
843
  },
795
844
  {
796
845
  type: 'tags',
797
846
  id: '4',
798
- name: 'happy',
847
+ attributes: {
848
+ name: 'happy'
849
+ },
799
850
  links: {
800
- self: '/tags/4',
851
+ self: '/tags/4'
852
+ },
853
+ relationships: {
801
854
  posts: {
802
- self: '/tags/4/links/posts',
803
- related: '/tags/4/posts',
855
+ links: {
856
+ self: '/tags/4/relationships/posts',
857
+ related: '/tags/4/posts'
858
+ }
804
859
  }
805
860
  }
806
861
  },
807
862
  {
808
863
  type: 'tags',
809
864
  id: '5',
810
- name: 'JR',
865
+ attributes: {
866
+ name: 'JR'
867
+ },
811
868
  links: {
812
- self: '/tags/5',
869
+ self: '/tags/5'
870
+ },
871
+ relationships: {
813
872
  posts: {
814
- self: '/tags/5/links/posts',
815
- related: '/tags/5/posts',
873
+ links: {
874
+ self: '/tags/5/relationships/posts',
875
+ related: '/tags/5/posts'
876
+ }
816
877
  }
817
878
  }
818
879
  },
819
880
  {
820
881
  type: 'comments',
821
882
  id: '1',
822
- body: 'what a dumb post',
883
+ attributes: {
884
+ body: 'what a dumb post'
885
+ },
823
886
  links: {
824
- self: '/comments/1',
887
+ self: '/comments/1'
888
+ },
889
+ relationships: {
825
890
  author: {
826
- self: '/comments/1/links/author',
827
- related: '/comments/1/author',
828
- linkage: {
891
+ links: {
892
+ self: '/comments/1/relationships/author',
893
+ related: '/comments/1/author'
894
+ },
895
+ data: {
829
896
  type: 'people',
830
897
  id: '1'
831
898
  }
832
899
  },
833
900
  post: {
834
- self: '/comments/1/links/post',
835
- related: '/comments/1/post',
836
- linkage: {
901
+ links: {
902
+ self: '/comments/1/relationships/post',
903
+ related: '/comments/1/post'
904
+ },
905
+ data: {
837
906
  type: 'posts',
838
907
  id: '1'
839
908
  }
840
909
  },
841
910
  tags: {
842
- self: '/comments/1/links/tags',
843
- related: '/comments/1/tags',
844
- linkage: [
911
+ links: {
912
+ self: '/comments/1/relationships/tags',
913
+ related: '/comments/1/tags'
914
+ },
915
+ data: [
845
916
  {type: 'tags', id: '1'},
846
917
  {type: 'tags', id: '2'}
847
918
  ]
@@ -851,29 +922,39 @@ class SerializerTest < ActionDispatch::IntegrationTest
851
922
  {
852
923
  type: 'comments',
853
924
  id: '2',
854
- body: 'i liked it',
925
+ attributes: {
926
+ body: 'i liked it'
927
+ },
855
928
  links: {
856
- self: '/comments/2',
929
+ self: '/comments/2'
930
+ },
931
+ relationships: {
857
932
  author: {
858
- self: '/comments/2/links/author',
859
- related: '/comments/2/author',
860
- linkage: {
933
+ links: {
934
+ self: '/comments/2/relationships/author',
935
+ related: '/comments/2/author'
936
+ },
937
+ data: {
861
938
  type: 'people',
862
939
  id: '2'
863
940
  }
864
941
  },
865
942
  post: {
866
- self: '/comments/2/links/post',
867
- related: '/comments/2/post',
868
- linkage: {
943
+ links: {
944
+ self: '/comments/2/relationships/post',
945
+ related: '/comments/2/post'
946
+ },
947
+ data: {
869
948
  type: 'posts',
870
949
  id: '1'
871
950
  }
872
951
  },
873
952
  tags: {
874
- self: '/comments/2/links/tags',
875
- related: '/comments/2/tags',
876
- linkage: [
953
+ links: {
954
+ self: '/comments/2/relationships/tags',
955
+ related: '/comments/2/tags'
956
+ },
957
+ data: [
877
958
  {type: 'tags', id: '4'},
878
959
  {type: 'tags', id: '1'}
879
960
  ]
@@ -883,29 +964,39 @@ class SerializerTest < ActionDispatch::IntegrationTest
883
964
  {
884
965
  type: 'comments',
885
966
  id: '3',
886
- body: 'Thanks man. Great post. But what is JR?',
967
+ attributes: {
968
+ body: 'Thanks man. Great post. But what is JR?'
969
+ },
887
970
  links: {
888
- self: '/comments/3',
971
+ self: '/comments/3'
972
+ },
973
+ relationships: {
889
974
  author: {
890
- self: '/comments/3/links/author',
891
- related: '/comments/3/author',
892
- linkage: {
975
+ links: {
976
+ self: '/comments/3/relationships/author',
977
+ related: '/comments/3/author'
978
+ },
979
+ data: {
893
980
  type: 'people',
894
981
  id: '2'
895
982
  }
896
983
  },
897
984
  post: {
898
- self: '/comments/3/links/post',
899
- related: '/comments/3/post',
900
- linkage: {
985
+ links: {
986
+ self: '/comments/3/relationships/post',
987
+ related: '/comments/3/post'
988
+ },
989
+ data: {
901
990
  type: 'posts',
902
991
  id: '2'
903
992
  }
904
993
  },
905
994
  tags: {
906
- self: '/comments/3/links/tags',
907
- related: '/comments/3/tags',
908
- linkage: [
995
+ links: {
996
+ self: '/comments/3/relationships/tags',
997
+ related: '/comments/3/tags'
998
+ },
999
+ data: [
909
1000
  {type: 'tags', id: '5'}
910
1001
  ]
911
1002
  }
@@ -931,33 +1022,21 @@ class SerializerTest < ActionDispatch::IntegrationTest
931
1022
  {
932
1023
  type: 'posts',
933
1024
  id: '1',
934
- title: 'New post',
1025
+ attributes: {
1026
+ title: 'New post'
1027
+ },
935
1028
  links: {
936
- self: '/posts/1',
937
- author: {
938
- self: '/posts/1/links/author',
939
- related: '/posts/1/author',
940
- linkage: {
941
- type: 'people',
942
- id: '1'
943
- }
944
- }
1029
+ self: '/posts/1'
945
1030
  }
946
1031
  },
947
1032
  {
948
1033
  type: 'posts',
949
1034
  id: '2',
950
- title: 'JR Solves your serialization woes!',
1035
+ attributes: {
1036
+ title: 'JR Solves your serialization woes!'
1037
+ },
951
1038
  links: {
952
- self: '/posts/2',
953
- author: {
954
- self: '/posts/2/links/author',
955
- related: '/posts/2/author',
956
- linkage: {
957
- type: 'people',
958
- id: '1'
959
- }
960
- }
1039
+ self: '/posts/2'
961
1040
  }
962
1041
  }
963
1042
  ],
@@ -965,35 +1044,37 @@ class SerializerTest < ActionDispatch::IntegrationTest
965
1044
  {
966
1045
  type: 'posts',
967
1046
  id: '11',
968
- title: 'JR How To',
1047
+ attributes: {
1048
+ title: 'JR How To'
1049
+ },
969
1050
  links: {
970
- self: '/posts/11',
971
- author: {
972
- self: '/posts/11/links/author',
973
- related: '/posts/11/author',
974
- linkage: {
975
- type: 'people',
976
- id: '1'
977
- }
978
- }
1051
+ self: '/posts/11'
979
1052
  }
980
1053
  },
981
1054
  {
982
1055
  type: 'people',
983
1056
  id: '1',
984
- email: 'joe@xyz.fake',
1057
+ attributes: {
1058
+ email: 'joe@xyz.fake'
1059
+ },
985
1060
  links: {
986
- self: '/people/1',
1061
+ self: '/people/1'
1062
+ },
1063
+ relationships: {
987
1064
  comments: {
988
- self: '/people/1/links/comments',
989
- related: '/people/1/comments'
1065
+ links: {
1066
+ self: '/people/1/relationships/comments',
1067
+ related: '/people/1/comments'
1068
+ }
990
1069
  }
991
1070
  }
992
1071
  },
993
1072
  {
994
1073
  id: '1',
995
1074
  type: 'tags',
996
- name: 'short',
1075
+ attributes: {
1076
+ name: 'short'
1077
+ },
997
1078
  links: {
998
1079
  self: '/tags/1'
999
1080
  }
@@ -1001,7 +1082,9 @@ class SerializerTest < ActionDispatch::IntegrationTest
1001
1082
  {
1002
1083
  id: '2',
1003
1084
  type: 'tags',
1004
- name: 'whiny',
1085
+ attributes: {
1086
+ name: 'whiny'
1087
+ },
1005
1088
  links: {
1006
1089
  self: '/tags/2'
1007
1090
  }
@@ -1009,7 +1092,9 @@ class SerializerTest < ActionDispatch::IntegrationTest
1009
1092
  {
1010
1093
  id: '4',
1011
1094
  type: 'tags',
1012
- name: 'happy',
1095
+ attributes: {
1096
+ name: 'happy'
1097
+ },
1013
1098
  links: {
1014
1099
  self: '/tags/4'
1015
1100
  }
@@ -1017,7 +1102,9 @@ class SerializerTest < ActionDispatch::IntegrationTest
1017
1102
  {
1018
1103
  id: '5',
1019
1104
  type: 'tags',
1020
- name: 'JR',
1105
+ attributes: {
1106
+ name: 'JR'
1107
+ },
1021
1108
  links: {
1022
1109
  self: '/tags/5'
1023
1110
  }
@@ -1025,13 +1112,19 @@ class SerializerTest < ActionDispatch::IntegrationTest
1025
1112
  {
1026
1113
  type: 'comments',
1027
1114
  id: '1',
1028
- body: 'what a dumb post',
1115
+ attributes: {
1116
+ body: 'what a dumb post'
1117
+ },
1029
1118
  links: {
1030
- self: '/comments/1',
1119
+ self: '/comments/1'
1120
+ },
1121
+ relationships: {
1031
1122
  post: {
1032
- self: '/comments/1/links/post',
1033
- related: '/comments/1/post',
1034
- linkage: {
1123
+ links: {
1124
+ self: '/comments/1/relationships/post',
1125
+ related: '/comments/1/post'
1126
+ },
1127
+ data: {
1035
1128
  type: 'posts',
1036
1129
  id: '1'
1037
1130
  }
@@ -1041,13 +1134,19 @@ class SerializerTest < ActionDispatch::IntegrationTest
1041
1134
  {
1042
1135
  type: 'comments',
1043
1136
  id: '2',
1044
- body: 'i liked it',
1137
+ attributes: {
1138
+ body: 'i liked it'
1139
+ },
1045
1140
  links: {
1046
- self: '/comments/2',
1141
+ self: '/comments/2'
1142
+ },
1143
+ relationships: {
1047
1144
  post: {
1048
- self: '/comments/2/links/post',
1049
- related: '/comments/2/post',
1050
- linkage: {
1145
+ links: {
1146
+ self: '/comments/2/relationships/post',
1147
+ related: '/comments/2/post'
1148
+ },
1149
+ data: {
1051
1150
  type: 'posts',
1052
1151
  id: '1'
1053
1152
  }
@@ -1057,13 +1156,19 @@ class SerializerTest < ActionDispatch::IntegrationTest
1057
1156
  {
1058
1157
  type: 'comments',
1059
1158
  id: '3',
1060
- body: 'Thanks man. Great post. But what is JR?',
1159
+ attributes: {
1160
+ body: 'Thanks man. Great post. But what is JR?'
1161
+ },
1061
1162
  links: {
1062
- self: '/comments/3',
1163
+ self: '/comments/3'
1164
+ },
1165
+ relationships: {
1063
1166
  post: {
1064
- self: '/comments/3/links/post',
1065
- related: '/comments/3/post',
1066
- linkage: {
1167
+ links: {
1168
+ self: '/comments/3/relationships/post',
1169
+ related: '/comments/3/post'
1170
+ },
1171
+ data: {
1067
1172
  type: 'posts',
1068
1173
  id: '2'
1069
1174
  }
@@ -1076,7 +1181,7 @@ class SerializerTest < ActionDispatch::IntegrationTest
1076
1181
  include: ['comments', 'author', 'comments.tags', 'author.posts'],
1077
1182
  fields: {
1078
1183
  people: [:id, :email, :comments],
1079
- posts: [:id, :title, :author],
1184
+ posts: [:id, :title],
1080
1185
  tags: [:name],
1081
1186
  comments: [:id, :body, :post]
1082
1187
  }).serialize_to_hash(posts)
@@ -1092,22 +1197,30 @@ class SerializerTest < ActionDispatch::IntegrationTest
1092
1197
  data: {
1093
1198
  type: 'expenseEntries',
1094
1199
  id: '1',
1095
- transactionDate: '04/15/2014',
1096
- cost: 12.05,
1200
+ attributes: {
1201
+ transactionDate: '04/15/2014',
1202
+ cost: 12.05
1203
+ },
1097
1204
  links: {
1098
- self: '/expenseEntries/1',
1205
+ self: '/expenseEntries/1'
1206
+ },
1207
+ relationships: {
1099
1208
  isoCurrency: {
1100
- self: '/expenseEntries/1/links/isoCurrency',
1101
- related: '/expenseEntries/1/isoCurrency',
1102
- linkage: {
1209
+ links: {
1210
+ self: '/expenseEntries/1/relationships/isoCurrency',
1211
+ related: '/expenseEntries/1/isoCurrency'
1212
+ },
1213
+ data: {
1103
1214
  type: 'isoCurrencies',
1104
1215
  id: 'USD'
1105
1216
  }
1106
1217
  },
1107
1218
  employee: {
1108
- self: '/expenseEntries/1/links/employee',
1109
- related: '/expenseEntries/1/employee',
1110
- linkage: {
1219
+ links: {
1220
+ self: '/expenseEntries/1/relationships/employee',
1221
+ related: '/expenseEntries/1/employee'
1222
+ },
1223
+ data: {
1111
1224
  type: 'people',
1112
1225
  id: '3'
1113
1226
  }
@@ -1118,9 +1231,11 @@ class SerializerTest < ActionDispatch::IntegrationTest
1118
1231
  {
1119
1232
  type: 'isoCurrencies',
1120
1233
  id: 'USD',
1121
- countryName: 'United States',
1122
- name: 'United States Dollar',
1123
- minorUnit: 'cent',
1234
+ attributes: {
1235
+ countryName: 'United States',
1236
+ name: 'United States Dollar',
1237
+ minorUnit: 'cent'
1238
+ },
1124
1239
  links: {
1125
1240
  self: '/isoCurrencies/USD'
1126
1241
  }
@@ -1128,9 +1243,11 @@ class SerializerTest < ActionDispatch::IntegrationTest
1128
1243
  {
1129
1244
  type: 'people',
1130
1245
  id: '3',
1131
- email: 'lazy@xyz.fake',
1132
- name: 'Lazy Author',
1133
- dateJoined: '2013-10-31 17:25:00 -0400',
1246
+ attributes: {
1247
+ email: 'lazy@xyz.fake',
1248
+ name: 'Lazy Author',
1249
+ dateJoined: '2013-10-31 17:25:00 -0400'
1250
+ },
1134
1251
  links: {
1135
1252
  self: '/people/3',
1136
1253
  }
@@ -1138,7 +1255,7 @@ class SerializerTest < ActionDispatch::IntegrationTest
1138
1255
  ]
1139
1256
  },
1140
1257
  JSONAPI::ResourceSerializer.new(ExpenseEntryResource,
1141
- include: ['isoCurrency', 'employee'],
1258
+ include: ['iso_currency', 'employee'],
1142
1259
  fields: {people: [:id, :name, :email, :date_joined]}).serialize_to_hash(
1143
1260
  ExpenseEntryResource.new(@expense_entry))
1144
1261
  )
@@ -1153,22 +1270,32 @@ class SerializerTest < ActionDispatch::IntegrationTest
1153
1270
  data: {
1154
1271
  type: 'planets',
1155
1272
  id: '8',
1156
- name: 'Beta W',
1157
- description: 'Newly discovered Planet W',
1273
+ attributes: {
1274
+ name: 'Beta W',
1275
+ description: 'Newly discovered Planet W'
1276
+ },
1158
1277
  links: {
1159
- self: '/planets/8',
1278
+ self: '/planets/8'
1279
+ },
1280
+ relationships: {
1160
1281
  planetType: {
1161
- self: '/planets/8/links/planetType',
1162
- related: '/planets/8/planetType',
1163
- linkage: nil
1282
+ links: {
1283
+ self: '/planets/8/relationships/planetType',
1284
+ related: '/planets/8/planetType'
1285
+ },
1286
+ data: nil
1164
1287
  },
1165
1288
  tags: {
1166
- self: '/planets/8/links/tags',
1167
- related: '/planets/8/tags'
1289
+ links: {
1290
+ self: '/planets/8/relationships/tags',
1291
+ related: '/planets/8/tags'
1292
+ }
1168
1293
  },
1169
1294
  moons: {
1170
- self: '/planets/8/links/moons',
1171
- related: '/planets/8/moons'
1295
+ links: {
1296
+ self: '/planets/8/relationships/moons',
1297
+ related: '/planets/8/moons'
1298
+ }
1172
1299
  }
1173
1300
  }
1174
1301
  }
@@ -1190,47 +1317,67 @@ class SerializerTest < ActionDispatch::IntegrationTest
1190
1317
  data: [{
1191
1318
  type: 'planets',
1192
1319
  id: '7',
1193
- name: 'Beta X',
1194
- description: 'Newly discovered Planet Z',
1320
+ attributes: {
1321
+ name: 'Beta X',
1322
+ description: 'Newly discovered Planet Z'
1323
+ },
1195
1324
  links: {
1196
- self: '/planets/7',
1325
+ self: '/planets/7'
1326
+ },
1327
+ relationships: {
1197
1328
  planetType: {
1198
- self: '/planets/7/links/planetType',
1199
- related: '/planets/7/planetType',
1200
- linkage: {
1329
+ links: {
1330
+ self: '/planets/7/relationships/planetType',
1331
+ related: '/planets/7/planetType'
1332
+ },
1333
+ data: {
1201
1334
  type: 'planetTypes',
1202
1335
  id: '5'
1203
1336
  }
1204
1337
  },
1205
1338
  tags: {
1206
- self: '/planets/7/links/tags',
1207
- related: '/planets/7/tags'
1339
+ links: {
1340
+ self: '/planets/7/relationships/tags',
1341
+ related: '/planets/7/tags'
1342
+ }
1208
1343
  },
1209
1344
  moons: {
1210
- self: '/planets/7/links/moons',
1211
- related: '/planets/7/moons'
1345
+ links: {
1346
+ self: '/planets/7/relationships/moons',
1347
+ related: '/planets/7/moons'
1348
+ }
1212
1349
  }
1213
1350
  }
1214
1351
  },
1215
1352
  {
1216
1353
  type: 'planets',
1217
1354
  id: '8',
1218
- name: 'Beta W',
1219
- description: 'Newly discovered Planet W',
1355
+ attributes: {
1356
+ name: 'Beta W',
1357
+ description: 'Newly discovered Planet W'
1358
+ },
1220
1359
  links: {
1221
- self: '/planets/8',
1360
+ self: '/planets/8'
1361
+ },
1362
+ relationships: {
1222
1363
  planetType: {
1223
- self: '/planets/8/links/planetType',
1224
- related: '/planets/8/planetType',
1225
- linkage: nil
1364
+ links: {
1365
+ self: '/planets/8/relationships/planetType',
1366
+ related: '/planets/8/planetType'
1367
+ },
1368
+ data: nil
1226
1369
  },
1227
1370
  tags: {
1228
- self: '/planets/8/links/tags',
1229
- related: '/planets/8/tags'
1371
+ links: {
1372
+ self: '/planets/8/relationships/tags',
1373
+ related: '/planets/8/tags'
1374
+ }
1230
1375
  },
1231
1376
  moons: {
1232
- self: '/planets/8/links/moons',
1233
- related: '/planets/8/moons'
1377
+ links: {
1378
+ self: '/planets/8/relationships/moons',
1379
+ related: '/planets/8/moons'
1380
+ }
1234
1381
  }
1235
1382
  }
1236
1383
  }
@@ -1239,7 +1386,9 @@ class SerializerTest < ActionDispatch::IntegrationTest
1239
1386
  {
1240
1387
  type: 'planetTypes',
1241
1388
  id: '5',
1242
- name: 'unknown',
1389
+ attributes: {
1390
+ name: 'unknown'
1391
+ },
1243
1392
  links: {
1244
1393
  self: '/planetTypes/5'
1245
1394
  }
@@ -1258,17 +1407,25 @@ class SerializerTest < ActionDispatch::IntegrationTest
1258
1407
  data: {
1259
1408
  type: 'preferences',
1260
1409
  id: '1',
1261
- advanced_mode: false,
1410
+ attributes: {
1411
+ advanced_mode: false
1412
+ },
1262
1413
  links: {
1263
- self: '/preferences/1',
1414
+ self: '/preferences/1'
1415
+ },
1416
+ relationships: {
1264
1417
  author: {
1265
- self: '/preferences/1/links/author',
1266
- related: '/preferences/1/author',
1267
- linkage: nil
1418
+ links: {
1419
+ self: '/preferences/1/relationships/author',
1420
+ related: '/preferences/1/author'
1421
+ },
1422
+ data: nil
1268
1423
  },
1269
1424
  friends: {
1270
- self: '/preferences/1/links/friends',
1271
- related: '/preferences/1/friends'
1425
+ links: {
1426
+ self: '/preferences/1/relationships/friends',
1427
+ related: '/preferences/1/friends'
1428
+ }
1272
1429
  }
1273
1430
  }
1274
1431
  }
@@ -1287,15 +1444,17 @@ class SerializerTest < ActionDispatch::IntegrationTest
1287
1444
  data: {
1288
1445
  type: 'facts',
1289
1446
  id: '1',
1290
- spouse_name: 'Jane Author',
1291
- bio: 'First man to run across Antartica.',
1292
- quality_rating: 23.89/45.6,
1293
- salary: BigDecimal('47000.56', 30),
1294
- date_time_joined: DateTime.parse('2013-08-07 20:25:00 UTC +00:00'),
1295
- birthday: Date.parse('1965-06-30'),
1296
- bedtime: Time.parse('2000-01-01 20:00:00 UTC +00:00'), #DB seems to set the date to 2001-01-01 for time types
1297
- photo: "abc",
1298
- cool: false,
1447
+ attributes: {
1448
+ spouse_name: 'Jane Author',
1449
+ bio: 'First man to run across Antartica.',
1450
+ quality_rating: 23.89/45.6,
1451
+ salary: BigDecimal('47000.56', 30),
1452
+ date_time_joined: DateTime.parse('2013-08-07 20:25:00 UTC +00:00'),
1453
+ birthday: Date.parse('1965-06-30'),
1454
+ bedtime: Time.parse('2000-01-01 20:00:00 UTC +00:00'), #DB seems to set the date to 2001-01-01 for time types
1455
+ photo: "abc",
1456
+ cool: false
1457
+ },
1299
1458
  links: {
1300
1459
  self: '/facts/1'
1301
1460
  }