jsonapi-resources 0.10.7 → 0.11.0.beta2

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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +39 -2
  4. data/lib/generators/jsonapi/controller_generator.rb +2 -0
  5. data/lib/generators/jsonapi/resource_generator.rb +2 -0
  6. data/lib/jsonapi/active_relation/adapters/join_left_active_record_adapter.rb +3 -2
  7. data/lib/jsonapi/active_relation/join_manager.rb +30 -18
  8. data/lib/jsonapi/active_relation/join_manager_v10.rb +305 -0
  9. data/lib/jsonapi/active_relation_retrieval.rb +885 -0
  10. data/lib/jsonapi/active_relation_retrieval_v09.rb +715 -0
  11. data/lib/jsonapi/{active_relation_resource.rb → active_relation_retrieval_v10.rb} +113 -135
  12. data/lib/jsonapi/acts_as_resource_controller.rb +49 -49
  13. data/lib/jsonapi/cached_response_fragment.rb +4 -2
  14. data/lib/jsonapi/callbacks.rb +2 -0
  15. data/lib/jsonapi/compiled_json.rb +2 -0
  16. data/lib/jsonapi/configuration.rb +35 -15
  17. data/lib/jsonapi/error.rb +2 -0
  18. data/lib/jsonapi/error_codes.rb +2 -0
  19. data/lib/jsonapi/exceptions.rb +2 -0
  20. data/lib/jsonapi/formatter.rb +2 -0
  21. data/lib/jsonapi/include_directives.rb +77 -19
  22. data/lib/jsonapi/link_builder.rb +2 -0
  23. data/lib/jsonapi/mime_types.rb +6 -10
  24. data/lib/jsonapi/naive_cache.rb +2 -0
  25. data/lib/jsonapi/operation.rb +2 -0
  26. data/lib/jsonapi/operation_result.rb +2 -0
  27. data/lib/jsonapi/paginator.rb +2 -17
  28. data/lib/jsonapi/path.rb +2 -0
  29. data/lib/jsonapi/path_segment.rb +4 -2
  30. data/lib/jsonapi/processor.rb +100 -153
  31. data/lib/jsonapi/relationship.rb +89 -35
  32. data/lib/jsonapi/{request_parser.rb → request.rb} +157 -164
  33. data/lib/jsonapi/resource.rb +7 -2
  34. data/lib/jsonapi/{basic_resource.rb → resource_common.rb} +187 -88
  35. data/lib/jsonapi/resource_controller.rb +2 -0
  36. data/lib/jsonapi/resource_controller_metal.rb +2 -0
  37. data/lib/jsonapi/resource_fragment.rb +17 -15
  38. data/lib/jsonapi/resource_identity.rb +6 -0
  39. data/lib/jsonapi/resource_serializer.rb +20 -4
  40. data/lib/jsonapi/resource_set.rb +36 -16
  41. data/lib/jsonapi/resource_tree.rb +191 -0
  42. data/lib/jsonapi/resources/railtie.rb +3 -1
  43. data/lib/jsonapi/resources/version.rb +3 -1
  44. data/lib/jsonapi/response_document.rb +4 -2
  45. data/lib/jsonapi/routing_ext.rb +4 -2
  46. data/lib/jsonapi/simple_resource.rb +13 -0
  47. data/lib/jsonapi-resources.rb +10 -4
  48. data/lib/tasks/check_upgrade.rake +3 -1
  49. metadata +47 -15
  50. data/lib/jsonapi/resource_id_tree.rb +0 -112
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module JSONAPI
2
4
  class Processor
3
5
  include Callbacks
@@ -49,7 +51,7 @@ module JSONAPI
49
51
 
50
52
  verified_filters = resource_klass.verify_filters(filters, context)
51
53
 
52
- find_options = {
54
+ options = {
53
55
  context: context,
54
56
  sort_criteria: sort_criteria,
55
57
  paginator: paginator,
@@ -58,20 +60,18 @@ module JSONAPI
58
60
  include_directives: include_directives
59
61
  }
60
62
 
61
- resource_set = find_resource_set(resource_klass,
62
- include_directives,
63
- find_options)
63
+ resource_set = find_resource_set(include_directives, options)
64
64
 
65
- resource_set.populate!(serializer, context, find_options)
65
+ resource_set.populate!(serializer, context, options)
66
66
 
67
67
  page_options = result_options
68
- if (top_level_meta_include_record_count || (paginator && paginator.requires_record_count))
68
+ if (JSONAPI.configuration.top_level_meta_include_record_count || (paginator && paginator.class.requires_record_count))
69
69
  page_options[:record_count] = resource_klass.count(verified_filters,
70
70
  context: context,
71
71
  include_directives: include_directives)
72
72
  end
73
73
 
74
- if (top_level_meta_include_page_count && paginator && page_options[:record_count])
74
+ if (JSONAPI.configuration.top_level_meta_include_page_count && paginator && page_options[:record_count])
75
75
  page_options[:page_count] = paginator ? paginator.calculate_page_count(page_options[:record_count]) : 1
76
76
  end
77
77
 
@@ -79,7 +79,7 @@ module JSONAPI
79
79
  page_options[:pagination_params] = paginator.links_page_params(page_options.merge(fetched_resources: resource_set))
80
80
  end
81
81
 
82
- return JSONAPI::ResourcesSetOperationResult.new(:ok, resource_set, page_options)
82
+ JSONAPI::ResourcesSetOperationResult.new(:ok, resource_set, page_options)
83
83
  end
84
84
 
85
85
  def show
@@ -90,26 +90,25 @@ module JSONAPI
90
90
 
91
91
  key = resource_klass.verify_key(id, context)
92
92
 
93
- find_options = {
93
+ options = {
94
94
  context: context,
95
95
  fields: fields,
96
96
  filters: { resource_klass._primary_key => key },
97
97
  include_directives: include_directives
98
98
  }
99
99
 
100
- resource_set = find_resource_set(resource_klass,
101
- include_directives,
102
- find_options)
100
+ resource_set = find_resource_set(include_directives, options)
103
101
 
104
102
  fail JSONAPI::Exceptions::RecordNotFound.new(id) if resource_set.resource_klasses.empty?
105
- resource_set.populate!(serializer, context, find_options)
103
+ resource_set.populate!(serializer, context, options)
106
104
 
107
- return JSONAPI::ResourceSetOperationResult.new(:ok, resource_set, result_options)
105
+ JSONAPI::ResourceSetOperationResult.new(:ok, resource_set, result_options)
108
106
  end
109
107
 
110
108
  def show_relationship
111
109
  parent_key = params[:parent_key]
112
110
  relationship_type = params[:relationship_type].to_sym
111
+ relationship = resource_klass._relationship(relationship_type)
113
112
  paginator = params[:paginator]
114
113
  sort_criteria = params[:sort_criteria]
115
114
  include_directives = params[:include_directives]
@@ -117,25 +116,26 @@ module JSONAPI
117
116
 
118
117
  parent_resource = resource_klass.find_by_key(parent_key, context: context)
119
118
 
120
- find_options = {
121
- context: context,
122
- sort_criteria: sort_criteria,
123
- paginator: paginator,
124
- fields: fields,
125
- include_directives: include_directives
119
+ options = {
120
+ context: context,
121
+ sort_criteria: sort_criteria,
122
+ paginator: paginator,
123
+ fields: fields,
124
+ include_directives: include_directives
126
125
  }
127
126
 
128
- resource_id_tree = find_related_resource_id_tree(resource_klass,
129
- JSONAPI::ResourceIdentity.new(resource_klass, parent_key),
130
- relationship_type,
131
- find_options,
132
- nil)
133
-
134
- return JSONAPI::RelationshipOperationResult.new(:ok,
135
- parent_resource,
136
- resource_klass._relationship(relationship_type),
137
- resource_id_tree.fragments.keys,
138
- result_options)
127
+ resource_tree = find_related_resource_tree(
128
+ parent_resource,
129
+ relationship,
130
+ options,
131
+ nil
132
+ )
133
+
134
+ JSONAPI::RelationshipOperationResult.new(:ok,
135
+ parent_resource,
136
+ relationship,
137
+ resource_tree.fragments.keys,
138
+ result_options)
139
139
  end
140
140
 
141
141
  def show_related_resource
@@ -146,11 +146,11 @@ module JSONAPI
146
146
  serializer = params[:serializer]
147
147
  fields = params[:fields]
148
148
 
149
- find_options = {
150
- context: context,
151
- fields: fields,
152
- filters: {},
153
- include_directives: include_directives
149
+ options = {
150
+ context: context,
151
+ fields: fields,
152
+ filters: {},
153
+ include_directives: include_directives
154
154
  }
155
155
 
156
156
  source_resource = source_klass.find_by_key(source_id, context: context, fields: fields)
@@ -158,11 +158,11 @@ module JSONAPI
158
158
  resource_set = find_related_resource_set(source_resource,
159
159
  relationship_type,
160
160
  include_directives,
161
- find_options)
161
+ options)
162
162
 
163
- resource_set.populate!(serializer, context, find_options)
163
+ resource_set.populate!(serializer, context, options)
164
164
 
165
- return JSONAPI::ResourceSetOperationResult.new(:ok, resource_set, result_options)
165
+ JSONAPI::ResourceSetOperationResult.new(:ok, resource_set, result_options)
166
166
  end
167
167
 
168
168
  def show_related_resources
@@ -178,8 +178,8 @@ module JSONAPI
178
178
 
179
179
  verified_filters = resource_klass.verify_filters(filters, context)
180
180
 
181
- find_options = {
182
- filters: verified_filters,
181
+ options = {
182
+ filters: verified_filters,
183
183
  sort_criteria: sort_criteria,
184
184
  paginator: paginator,
185
185
  fields: fields,
@@ -192,38 +192,40 @@ module JSONAPI
192
192
  resource_set = find_related_resource_set(source_resource,
193
193
  relationship_type,
194
194
  include_directives,
195
- find_options)
195
+ options)
196
196
 
197
- resource_set.populate!(serializer, context, find_options)
197
+ resource_set.populate!(serializer, context, options)
198
198
 
199
199
  opts = result_options
200
- if ((top_level_meta_include_record_count) ||
201
- (paginator && paginator.requires_record_count) ||
202
- (top_level_meta_include_page_count))
200
+ if ((JSONAPI.configuration.top_level_meta_include_record_count) ||
201
+ (paginator && paginator.class.requires_record_count) ||
202
+ (JSONAPI.configuration.top_level_meta_include_page_count))
203
+
204
+ relationship = source_resource.class._relationship(relationship_type)
203
205
 
204
206
  opts[:record_count] = source_resource.class.count_related(
205
- source_resource.identity,
206
- relationship_type,
207
- find_options)
207
+ source_resource,
208
+ relationship,
209
+ options)
208
210
  end
209
211
 
210
- if (top_level_meta_include_page_count && opts[:record_count])
212
+ if (JSONAPI.configuration.top_level_meta_include_page_count && opts[:record_count])
211
213
  opts[:page_count] = paginator.calculate_page_count(opts[:record_count])
212
214
  end
213
215
 
214
216
  opts[:pagination_params] = if paginator && JSONAPI.configuration.top_level_links_include_pagination
215
217
  page_options = {}
216
- page_options[:record_count] = opts[:record_count] if paginator.requires_record_count
218
+ page_options[:record_count] = opts[:record_count] if paginator.class.requires_record_count
217
219
  paginator.links_page_params(page_options.merge(fetched_resources: resource_set))
218
220
  else
219
221
  {}
220
222
  end
221
223
 
222
- return JSONAPI::RelatedResourcesSetOperationResult.new(:ok,
223
- source_resource,
224
- relationship_type,
225
- resource_set,
226
- opts)
224
+ JSONAPI::RelatedResourcesSetOperationResult.new(:ok,
225
+ source_resource,
226
+ relationship_type,
227
+ resource_set,
228
+ opts)
227
229
  end
228
230
 
229
231
  def create_resource
@@ -235,20 +237,18 @@ module JSONAPI
235
237
  resource = resource_klass.create(context)
236
238
  result = resource.replace_fields(data)
237
239
 
238
- find_options = {
239
- context: context,
240
- fields: fields,
241
- filters: { resource_klass._primary_key => resource.id },
242
- include_directives: include_directives
240
+ options = {
241
+ context: context,
242
+ fields: fields,
243
+ filters: { resource_klass._primary_key => resource.id },
244
+ include_directives: include_directives
243
245
  }
244
246
 
245
- resource_set = find_resource_set(resource_klass,
246
- include_directives,
247
- find_options)
247
+ resource_set = find_resource_set(include_directives, options)
248
248
 
249
- resource_set.populate!(serializer, context, find_options)
249
+ resource_set.populate!(serializer, context, options)
250
250
 
251
- return JSONAPI::ResourceSetOperationResult.new((result == :completed ? :created : :accepted), resource_set, result_options)
251
+ JSONAPI::ResourceSetOperationResult.new((result == :completed ? :created : :accepted), resource_set, result_options)
252
252
  end
253
253
 
254
254
  def remove_resource
@@ -257,7 +257,7 @@ module JSONAPI
257
257
  resource = resource_klass.find_by_key(resource_id, context: context)
258
258
  result = resource.remove
259
259
 
260
- return JSONAPI::OperationResult.new(result == :completed ? :no_content : :accepted, result_options)
260
+ JSONAPI::OperationResult.new(result == :completed ? :no_content : :accepted, result_options)
261
261
  end
262
262
 
263
263
  def replace_fields
@@ -272,20 +272,18 @@ module JSONAPI
272
272
 
273
273
  result = resource.replace_fields(data)
274
274
 
275
- find_options = {
276
- context: context,
277
- fields: fields,
278
- filters: { resource_klass._primary_key => resource.id },
279
- include_directives: include_directives
275
+ options = {
276
+ context: context,
277
+ fields: fields,
278
+ filters: { resource_klass._primary_key => resource.id },
279
+ include_directives: include_directives
280
280
  }
281
281
 
282
- resource_set = find_resource_set(resource_klass,
283
- include_directives,
284
- find_options)
282
+ resource_set = find_resource_set(include_directives, options)
285
283
 
286
- resource_set.populate!(serializer, context, find_options)
284
+ resource_set.populate!(serializer, context, options)
287
285
 
288
- return JSONAPI::ResourceSetOperationResult.new((result == :completed ? :ok : :accepted), resource_set, result_options)
286
+ JSONAPI::ResourceSetOperationResult.new((result == :completed ? :ok : :accepted), resource_set, result_options)
289
287
  end
290
288
 
291
289
  def replace_to_one_relationship
@@ -296,7 +294,7 @@ module JSONAPI
296
294
  resource = resource_klass.find_by_key(resource_id, context: context)
297
295
  result = resource.replace_to_one_link(relationship_type, key_value)
298
296
 
299
- return JSONAPI::OperationResult.new(result == :completed ? :no_content : :accepted, result_options)
297
+ JSONAPI::OperationResult.new(result == :completed ? :no_content : :accepted, result_options)
300
298
  end
301
299
 
302
300
  def replace_polymorphic_to_one_relationship
@@ -308,7 +306,7 @@ module JSONAPI
308
306
  resource = resource_klass.find_by_key(resource_id, context: context)
309
307
  result = resource.replace_polymorphic_to_one_link(relationship_type, key_value, key_type)
310
308
 
311
- return JSONAPI::OperationResult.new(result == :completed ? :no_content : :accepted, result_options)
309
+ JSONAPI::OperationResult.new(result == :completed ? :no_content : :accepted, result_options)
312
310
  end
313
311
 
314
312
  def create_to_many_relationships
@@ -319,7 +317,7 @@ module JSONAPI
319
317
  resource = resource_klass.find_by_key(resource_id, context: context)
320
318
  result = resource.create_to_many_links(relationship_type, data)
321
319
 
322
- return JSONAPI::OperationResult.new(result == :completed ? :no_content : :accepted, result_options)
320
+ JSONAPI::OperationResult.new(result == :completed ? :no_content : :accepted, result_options)
323
321
  end
324
322
 
325
323
  def replace_to_many_relationships
@@ -330,7 +328,7 @@ module JSONAPI
330
328
  resource = resource_klass.find_by_key(resource_id, context: context)
331
329
  result = resource.replace_to_many_links(relationship_type, data)
332
330
 
333
- return JSONAPI::OperationResult.new(result == :completed ? :no_content : :accepted, result_options)
331
+ JSONAPI::OperationResult.new(result == :completed ? :no_content : :accepted, result_options)
334
332
  end
335
333
 
336
334
  def remove_to_many_relationships
@@ -347,7 +345,7 @@ module JSONAPI
347
345
  complete = false
348
346
  end
349
347
  end
350
- return JSONAPI::OperationResult.new(complete ? :no_content : :accepted, result_options)
348
+ JSONAPI::OperationResult.new(complete ? :no_content : :accepted, result_options)
351
349
  end
352
350
 
353
351
  def remove_to_one_relationship
@@ -357,7 +355,7 @@ module JSONAPI
357
355
  resource = resource_klass.find_by_key(resource_id, context: context)
358
356
  result = resource.remove_to_one_link(relationship_type)
359
357
 
360
- return JSONAPI::OperationResult.new(result == :completed ? :no_content : :accepted, result_options)
358
+ JSONAPI::OperationResult.new(result == :completed ? :no_content : :accepted, result_options)
361
359
  end
362
360
 
363
361
  def result_options
@@ -366,99 +364,48 @@ module JSONAPI
366
364
  options
367
365
  end
368
366
 
369
- def find_resource_set(resource_klass, include_directives, options)
370
- include_related = include_directives.include_directives[:include_related] if include_directives
367
+ def find_resource_set(include_directives, options)
368
+ include_related = include_directives[:include_related] if include_directives
371
369
 
372
- resource_id_tree = find_resource_id_tree(resource_klass, options, include_related)
370
+ resource_tree = find_resource_tree(options, include_related)
373
371
 
374
- JSONAPI::ResourceSet.new(resource_id_tree)
372
+ JSONAPI::ResourceSet.new(resource_tree)
375
373
  end
376
374
 
377
375
  def find_related_resource_set(resource, relationship_name, include_directives, options)
378
- include_related = include_directives.include_directives[:include_related] if include_directives
379
-
380
- resource_id_tree = find_resource_id_tree_from_resource_relationship(resource, relationship_name, options, include_related)
376
+ include_related = include_directives[:include_related] if include_directives
381
377
 
382
- JSONAPI::ResourceSet.new(resource_id_tree)
383
- end
384
-
385
- def top_level_meta_include_record_count
386
- JSONAPI.configuration.top_level_meta_include_record_count
387
- end
378
+ resource_tree = find_resource_tree_from_relationship(resource, relationship_name, options, include_related)
388
379
 
389
- def top_level_meta_include_page_count
390
- JSONAPI.configuration.top_level_meta_include_page_count
380
+ JSONAPI::ResourceSet.new(resource_tree)
391
381
  end
392
382
 
393
- private
394
- def find_related_resource_id_tree(resource_klass, source_id, relationship_name, find_options, include_related)
395
- options = find_options.except(:include_directives)
383
+ def find_resource_tree(options, include_related)
396
384
  options[:cache] = resource_klass.caching?
397
385
 
398
- fragments = resource_klass.find_included_fragments([source_id], relationship_name, options)
399
-
400
- primary_resource_id_tree = PrimaryResourceIdTree.new
401
- primary_resource_id_tree.add_resource_fragments(fragments, include_related)
402
-
403
- load_included(resource_klass, primary_resource_id_tree, include_related, options)
404
-
405
- primary_resource_id_tree
386
+ fragments = resource_klass.find_fragments(options[:filters], options)
387
+ PrimaryResourceTree.new(fragments: fragments, include_related: include_related, options: options)
406
388
  end
407
389
 
408
- def find_resource_id_tree(resource_klass, find_options, include_related)
409
- options = find_options
390
+ def find_related_resource_tree(parent_resource, relationship, options, include_related)
391
+ options = options.except(:include_directives)
410
392
  options[:cache] = resource_klass.caching?
411
393
 
412
- fragments = resource_klass.find_fragments(find_options[:filters], options)
413
-
414
- primary_resource_id_tree = PrimaryResourceIdTree.new
415
- primary_resource_id_tree.add_resource_fragments(fragments, include_related)
394
+ parent_resource_fragment = parent_resource.fragment(primary: true)
416
395
 
417
- load_included(resource_klass, primary_resource_id_tree, include_related, options)
418
-
419
- primary_resource_id_tree
396
+ fragments = resource_klass.find_related_fragments(parent_resource_fragment, relationship, options)
397
+ PrimaryResourceTree.new(fragments: fragments, include_related: include_related, options: options)
420
398
  end
421
399
 
422
- def find_resource_id_tree_from_resource_relationship(resource, relationship_name, find_options, include_related)
400
+ def find_resource_tree_from_relationship(resource, relationship_name, options, include_related)
423
401
  relationship = resource.class._relationship(relationship_name)
424
402
 
425
- options = find_options.except(:include_directives)
403
+ options = options.except(:include_directives)
426
404
  options[:cache] = relationship.resource_klass.caching?
427
405
 
428
- fragments = resource.class.find_related_fragments([resource.identity], relationship_name, options)
429
-
430
- primary_resource_id_tree = PrimaryResourceIdTree.new
431
- primary_resource_id_tree.add_resource_fragments(fragments, include_related)
406
+ fragments = resource.class.find_related_fragments(resource.fragment, relationship, options)
432
407
 
433
- load_included(resource_klass, primary_resource_id_tree, include_related, options)
434
-
435
- primary_resource_id_tree
436
- end
437
-
438
- def load_included(resource_klass, source_resource_id_tree, include_related, options)
439
- source_rids = source_resource_id_tree.fragments.keys
440
-
441
- include_related.try(:each_key) do |key|
442
- relationship = resource_klass._relationship(key)
443
- relationship_name = relationship.name.to_sym
444
-
445
- find_related_resource_options = options.except(:filters, :sort_criteria, :paginator)
446
- find_related_resource_options[:sort_criteria] = relationship.resource_klass.default_sort
447
- find_related_resource_options[:cache] = resource_klass.caching?
448
-
449
- related_fragments = resource_klass.find_included_fragments(
450
- source_rids, relationship_name, find_related_resource_options
451
- )
452
-
453
- related_resource_id_tree = source_resource_id_tree.fetch_related_resource_id_tree(relationship)
454
- related_resource_id_tree.add_resource_fragments(related_fragments, include_related[key][include_related])
455
-
456
- # Now recursively get the related resources for the currently found resources
457
- load_included(relationship.resource_klass,
458
- related_resource_id_tree,
459
- include_related[relationship_name][:include_related],
460
- options)
461
- end
408
+ PrimaryResourceTree.new(fragments: fragments, include_related: include_related, options: options)
462
409
  end
463
410
  end
464
411
  end