jsonapi-resources 0.10.6 → 0.11.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
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