jsonapi-resources 0.10.6 → 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 -0
  28. data/lib/jsonapi/path.rb +2 -0
  29. data/lib/jsonapi/path_segment.rb +4 -2
  30. data/lib/jsonapi/processor.rb +95 -140
  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,11 +60,9 @@ 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
68
  if (JSONAPI.configuration.top_level_meta_include_record_count || (paginator && paginator.class.requires_record_count))
@@ -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,19 +192,21 @@ 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
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))
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
212
  if (JSONAPI.configuration.top_level_meta_include_page_count && opts[:record_count])
@@ -219,11 +221,11 @@ module JSONAPI
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,91 +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
376
+ include_related = include_directives[:include_related] if include_directives
379
377
 
380
- resource_id_tree = find_resource_id_tree_from_resource_relationship(resource, relationship_name, options, include_related)
378
+ resource_tree = find_resource_tree_from_relationship(resource, relationship_name, options, include_related)
381
379
 
382
- JSONAPI::ResourceSet.new(resource_id_tree)
380
+ JSONAPI::ResourceSet.new(resource_tree)
383
381
  end
384
382
 
385
- private
386
- def find_related_resource_id_tree(resource_klass, source_id, relationship_name, find_options, include_related)
387
- options = find_options.except(:include_directives)
383
+ def find_resource_tree(options, include_related)
388
384
  options[:cache] = resource_klass.caching?
389
385
 
390
- fragments = resource_klass.find_included_fragments([source_id], relationship_name, options)
391
-
392
- primary_resource_id_tree = PrimaryResourceIdTree.new
393
- primary_resource_id_tree.add_resource_fragments(fragments, include_related)
394
-
395
- load_included(resource_klass, primary_resource_id_tree, include_related, options)
396
-
397
- primary_resource_id_tree
386
+ fragments = resource_klass.find_fragments(options[:filters], options)
387
+ PrimaryResourceTree.new(fragments: fragments, include_related: include_related, options: options)
398
388
  end
399
389
 
400
- def find_resource_id_tree(resource_klass, find_options, include_related)
401
- options = find_options
390
+ def find_related_resource_tree(parent_resource, relationship, options, include_related)
391
+ options = options.except(:include_directives)
402
392
  options[:cache] = resource_klass.caching?
403
393
 
404
- fragments = resource_klass.find_fragments(find_options[:filters], options)
394
+ parent_resource_fragment = parent_resource.fragment(primary: true)
405
395
 
406
- primary_resource_id_tree = PrimaryResourceIdTree.new
407
- primary_resource_id_tree.add_resource_fragments(fragments, include_related)
408
-
409
- load_included(resource_klass, primary_resource_id_tree, include_related, options)
410
-
411
- 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)
412
398
  end
413
399
 
414
- 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)
415
401
  relationship = resource.class._relationship(relationship_name)
416
402
 
417
- options = find_options.except(:include_directives)
403
+ options = options.except(:include_directives)
418
404
  options[:cache] = relationship.resource_klass.caching?
419
405
 
420
- fragments = resource.class.find_related_fragments([resource.identity], relationship_name, options)
421
-
422
- primary_resource_id_tree = PrimaryResourceIdTree.new
423
- primary_resource_id_tree.add_resource_fragments(fragments, include_related)
424
-
425
- load_included(resource_klass, primary_resource_id_tree, include_related, options)
406
+ fragments = resource.class.find_related_fragments(resource.fragment, relationship, options)
426
407
 
427
- primary_resource_id_tree
428
- end
429
-
430
- def load_included(resource_klass, source_resource_id_tree, include_related, options)
431
- source_rids = source_resource_id_tree.fragments.keys
432
-
433
- include_related.try(:each_key) do |key|
434
- relationship = resource_klass._relationship(key)
435
- relationship_name = relationship.name.to_sym
436
-
437
- find_related_resource_options = options.except(:filters, :sort_criteria, :paginator)
438
- find_related_resource_options[:sort_criteria] = relationship.resource_klass.default_sort
439
- find_related_resource_options[:cache] = resource_klass.caching?
440
-
441
- related_fragments = resource_klass.find_included_fragments(
442
- source_rids, relationship_name, find_related_resource_options
443
- )
444
-
445
- related_resource_id_tree = source_resource_id_tree.fetch_related_resource_id_tree(relationship)
446
- related_resource_id_tree.add_resource_fragments(related_fragments, include_related[key][include_related])
447
-
448
- # Now recursively get the related resources for the currently found resources
449
- load_included(relationship.resource_klass,
450
- related_resource_id_tree,
451
- include_related[relationship_name][:include_related],
452
- options)
453
- end
408
+ PrimaryResourceTree.new(fragments: fragments, include_related: include_related, options: options)
454
409
  end
455
410
  end
456
411
  end
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module JSONAPI
2
4
  class Relationship
3
5
  attr_reader :acts_as_set, :foreign_key, :options, :name,
4
- :class_name, :polymorphic, :always_include_optional_linkage_data,
6
+ :class_name, :polymorphic, :always_include_optional_linkage_data, :exclude_linkage_data,
5
7
  :parent_resource, :eager_load_on_include, :custom_methods,
6
- :inverse_relationship, :allow_include, :use_related_resource_records_for_joins
8
+ :inverse_relationship, :allow_include, :hidden, :use_related_resource_records_for_joins
7
9
 
8
10
  attr_writer :allow_include
9
11
 
@@ -15,7 +17,7 @@ module JSONAPI
15
17
  @acts_as_set = options.fetch(:acts_as_set, false) == true
16
18
  @foreign_key = options[:foreign_key] ? options[:foreign_key].to_sym : nil
17
19
  @parent_resource = options[:parent_resource]
18
- @relation_name = options.fetch(:relation_name, @name)
20
+ @relation_name = options[:relation_name]
19
21
  @polymorphic = options.fetch(:polymorphic, false) == true
20
22
  @polymorphic_types = options[:polymorphic_types]
21
23
  if options[:polymorphic_relations]
@@ -28,15 +30,19 @@ module JSONAPI
28
30
  else
29
31
  JSONAPI.configuration.use_related_resource_records_for_joins
30
32
  end
31
-
33
+
32
34
  @use_related_resource_records_for_joins = options.fetch(:use_related_resource_records_for_joins,
33
35
  use_related_resource_records_for_joins_default) == true
34
36
 
37
+ @hidden = options.fetch(:hidden, false) == true
38
+
39
+ @exclude_linkage_data = options[:exclude_linkage_data]
35
40
  @always_include_optional_linkage_data = options.fetch(:always_include_optional_linkage_data, false) == true
36
- @eager_load_on_include = options.fetch(:eager_load_on_include, false) == true
41
+ @eager_load_on_include = options.fetch(:eager_load_on_include, true) == true
37
42
  @allow_include = options[:allow_include]
38
43
  @class_name = nil
39
- @inverse_relationship = nil
44
+
45
+ @inverse_relationship = options[:inverse_relationship]&.to_sym
40
46
 
41
47
  @_routed = false
42
48
  @_warned_missing_route = false
@@ -66,12 +72,26 @@ module JSONAPI
66
72
  # :nocov:
67
73
  end
68
74
 
75
+ def inverse_relationship
76
+ unless @inverse_relationship
77
+ @inverse_relationship ||= if resource_klass._relationship(@parent_resource._type.to_s.singularize).present?
78
+ @parent_resource._type.to_s.singularize.to_sym
79
+ elsif resource_klass._relationship(@parent_resource._type).present?
80
+ @parent_resource._type.to_sym
81
+ else
82
+ nil
83
+ end
84
+ end
85
+
86
+ @inverse_relationship
87
+ end
88
+
69
89
  def self.polymorphic_types(name)
70
90
  @poly_hash ||= {}.tap do |hash|
71
91
  ObjectSpace.each_object do |klass|
72
92
  next unless Module === klass
73
93
  if ActiveRecord::Base > klass
74
- klass.reflect_on_all_associations(:has_many).select{|r| r.options[:as] }.each do |reflection|
94
+ klass.reflect_on_all_associations(:has_many).select { |r| r.options[:as] }.each do |reflection|
75
95
  (hash[reflection.options[:as]] ||= []) << klass.name.underscore
76
96
  end
77
97
  end
@@ -82,7 +102,7 @@ module JSONAPI
82
102
 
83
103
  def resource_types
84
104
  if polymorphic? && belongs_to?
85
- @polymorphic_types ||= self.class.polymorphic_types(@relation_name).collect {|t| t.pluralize}
105
+ @polymorphic_types ||= self.class.polymorphic_types(_relation_name).collect { |t| t.pluralize }
86
106
  else
87
107
  [resource_klass._type.to_s.pluralize]
88
108
  end
@@ -93,15 +113,15 @@ module JSONAPI
93
113
  end
94
114
 
95
115
  def relation_name(options)
96
- case @relation_name
97
- when Symbol
98
- # :nocov:
99
- @relation_name
100
- # :nocov:
101
- when String
102
- @relation_name.to_sym
103
- when Proc
104
- @relation_name.call(options)
116
+ case _relation_name
117
+ when Symbol
118
+ # :nocov:
119
+ _relation_name
120
+ # :nocov:
121
+ when String
122
+ _relation_name.to_sym
123
+ when Proc
124
+ _relation_name.call(options)
105
125
  end
106
126
  end
107
127
 
@@ -117,14 +137,14 @@ module JSONAPI
117
137
 
118
138
  def exclude_links(exclude)
119
139
  case exclude
120
- when :default, "default"
121
- @_exclude_links = [:self, :related]
122
- when :none, "none"
123
- @_exclude_links = []
124
- when Array
125
- @_exclude_links = exclude.collect {|link| link.to_sym}
126
- else
127
- fail "Invalid exclude_links"
140
+ when :default, "default"
141
+ @_exclude_links = [:self, :related]
142
+ when :none, "none"
143
+ @_exclude_links = []
144
+ when Array
145
+ @_exclude_links = exclude.collect { |link| link.to_sym }
146
+ else
147
+ fail "Invalid exclude_links"
128
148
  end
129
149
  end
130
150
 
@@ -136,6 +156,10 @@ module JSONAPI
136
156
  _exclude_links.include?(link.to_sym)
137
157
  end
138
158
 
159
+ def _relation_name
160
+ @relation_name || @name
161
+ end
162
+
139
163
  class ToOne < Relationship
140
164
  attr_reader :foreign_key_on
141
165
 
@@ -144,9 +168,16 @@ module JSONAPI
144
168
  @class_name = options.fetch(:class_name, name.to_s.camelize)
145
169
  @foreign_key ||= "#{name}_id".to_sym
146
170
  @foreign_key_on = options.fetch(:foreign_key_on, :self)
147
- if parent_resource
148
- @inverse_relationship = options.fetch(:inverse_relationship, parent_resource._type)
171
+ # if parent_resource
172
+ # @inverse_relationship = options.fetch(:inverse_relationship, parent_resource._type)
173
+ # end
174
+
175
+ if options.fetch(:create_implicit_polymorphic_type_relationships, true) == true && polymorphic?
176
+ # Setup the implicit relationships for the polymorphic types and exclude linkage data
177
+ setup_implicit_relationships_for_polymorphic_types
149
178
  end
179
+
180
+ @polymorphic_type_relationship_for = options[:polymorphic_type_relationship_for]
150
181
  end
151
182
 
152
183
  def to_s
@@ -161,11 +192,30 @@ module JSONAPI
161
192
  # :nocov:
162
193
  end
163
194
 
195
+ def hidden?
196
+ @hidden || @polymorphic_type_relationship_for.present?
197
+ end
198
+
164
199
  def polymorphic_type
165
200
  "#{name}_type" if polymorphic?
166
201
  end
167
202
 
203
+ def setup_implicit_relationships_for_polymorphic_types(exclude_linkage_data: true)
204
+ types = self.class.polymorphic_types(_relation_name)
205
+ unless types.present?
206
+ warn "No polymorphic types found for #{parent_resource.name} #{_relation_name}"
207
+ return
208
+ end
209
+
210
+ types.each do |type|
211
+ parent_resource.has_one(type.to_s.underscore.singularize,
212
+ exclude_linkage_data: exclude_linkage_data,
213
+ polymorphic_type_relationship_for: name)
214
+ end
215
+ end
216
+
168
217
  def include_optional_linkage_data?
218
+ return false if @exclude_linkage_data
169
219
  @always_include_optional_linkage_data || JSONAPI::configuration.always_include_to_one_linkage_data
170
220
  end
171
221
 
@@ -176,10 +226,10 @@ module JSONAPI
176
226
  @allow_include
177
227
  end
178
228
 
179
- if !!strategy == strategy #check for boolean
229
+ if !!strategy == strategy # check for boolean
180
230
  return strategy
181
231
  elsif strategy.is_a?(Symbol) || strategy.is_a?(String)
182
- parent_resource.send(strategy, context)
232
+ parent_resource_klass.send(strategy, context)
183
233
  else
184
234
  strategy.call(context)
185
235
  end
@@ -194,17 +244,21 @@ module JSONAPI
194
244
  @class_name = options.fetch(:class_name, name.to_s.camelize.singularize)
195
245
  @foreign_key ||= "#{name.to_s.singularize}_ids".to_sym
196
246
  @reflect = options.fetch(:reflect, true) == true
197
- if parent_resource
198
- @inverse_relationship = options.fetch(:inverse_relationship, parent_resource._type.to_s.singularize.to_sym)
199
- end
247
+ # if parent_resource
248
+ # @inverse_relationship = options.fetch(:inverse_relationship, parent_resource._type.to_s.singularize.to_sym)
249
+ # end
200
250
  end
201
251
 
202
252
  def to_s
203
253
  # :nocov: useful for debugging
204
- "#{parent_resource}.#{name}(ToMany)"
254
+ "#{parent_resource_klass}.#{name}(ToMany)"
205
255
  # :nocov:
206
256
  end
207
257
 
258
+ def hidden?
259
+ @hidden
260
+ end
261
+
208
262
  def include_optional_linkage_data?
209
263
  # :nocov:
210
264
  @always_include_optional_linkage_data || JSONAPI::configuration.always_include_to_many_linkage_data
@@ -218,10 +272,10 @@ module JSONAPI
218
272
  @allow_include
219
273
  end
220
274
 
221
- if !!strategy == strategy #check for boolean
275
+ if !!strategy == strategy # check for boolean
222
276
  return strategy
223
277
  elsif strategy.is_a?(Symbol) || strategy.is_a?(String)
224
- parent_resource.send(strategy, context)
278
+ parent_resource_klass.send(strategy, context)
225
279
  else
226
280
  strategy.call(context)
227
281
  end