jsonapi-resources 0.9.11 → 0.10.7

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +34 -11
  4. data/lib/bug_report_templates/rails_5_latest.rb +125 -0
  5. data/lib/bug_report_templates/rails_5_master.rb +140 -0
  6. data/lib/jsonapi/active_relation/adapters/join_left_active_record_adapter.rb +27 -0
  7. data/lib/jsonapi/active_relation/join_manager.rb +303 -0
  8. data/lib/jsonapi/active_relation_resource.rb +884 -0
  9. data/lib/jsonapi/acts_as_resource_controller.rb +122 -106
  10. data/lib/jsonapi/basic_resource.rb +1162 -0
  11. data/lib/jsonapi/cached_response_fragment.rb +127 -0
  12. data/lib/jsonapi/compiled_json.rb +11 -1
  13. data/lib/jsonapi/configuration.rb +57 -8
  14. data/lib/jsonapi/error.rb +27 -0
  15. data/lib/jsonapi/error_codes.rb +2 -0
  16. data/lib/jsonapi/exceptions.rb +63 -40
  17. data/lib/jsonapi/formatter.rb +3 -3
  18. data/lib/jsonapi/include_directives.rb +18 -75
  19. data/lib/jsonapi/link_builder.rb +16 -19
  20. data/lib/jsonapi/operation.rb +16 -5
  21. data/lib/jsonapi/operation_result.rb +73 -15
  22. data/lib/jsonapi/paginator.rb +17 -0
  23. data/lib/jsonapi/path.rb +43 -0
  24. data/lib/jsonapi/path_segment.rb +76 -0
  25. data/lib/jsonapi/processor.rb +246 -111
  26. data/lib/jsonapi/relationship.rb +117 -18
  27. data/lib/jsonapi/request_parser.rb +383 -396
  28. data/lib/jsonapi/resource.rb +3 -1376
  29. data/lib/jsonapi/resource_controller_metal.rb +5 -2
  30. data/lib/jsonapi/resource_fragment.rb +47 -0
  31. data/lib/jsonapi/resource_id_tree.rb +112 -0
  32. data/lib/jsonapi/resource_identity.rb +42 -0
  33. data/lib/jsonapi/resource_serializer.rb +124 -286
  34. data/lib/jsonapi/resource_set.rb +176 -0
  35. data/lib/jsonapi/resources/railtie.rb +9 -0
  36. data/lib/jsonapi/resources/version.rb +1 -1
  37. data/lib/jsonapi/response_document.rb +104 -87
  38. data/lib/jsonapi/routing_ext.rb +19 -21
  39. data/lib/jsonapi-resources.rb +20 -4
  40. data/lib/tasks/check_upgrade.rake +52 -0
  41. metadata +36 -33
  42. data/lib/jsonapi/cached_resource_fragment.rb +0 -127
  43. data/lib/jsonapi/operation_dispatcher.rb +0 -88
  44. data/lib/jsonapi/operation_results.rb +0 -35
  45. data/lib/jsonapi/relationship_builder.rb +0 -167
@@ -1,7 +1,7 @@
1
1
  module JSONAPI
2
2
  class ResourceSerializer
3
3
 
4
- attr_reader :link_builder, :key_formatter, :serialization_options, :primary_class_name,
4
+ attr_reader :link_builder, :key_formatter, :serialization_options,
5
5
  :fields, :include_directives, :always_include_to_one_linkage_data,
6
6
  :always_include_to_many_linkage_data
7
7
 
@@ -19,7 +19,6 @@ module JSONAPI
19
19
 
20
20
  def initialize(primary_resource_klass, options = {})
21
21
  @primary_resource_klass = primary_resource_klass
22
- @primary_class_name = primary_resource_klass._type
23
22
  @fields = options.fetch(:fields, {})
24
23
  @include = options.fetch(:include, [])
25
24
  @include_directives = options[:include_directives]
@@ -42,59 +41,63 @@ module JSONAPI
42
41
  @_supplying_relationship_fields = {}
43
42
  end
44
43
 
45
- # Converts a single resource, or an array of resources to a hash, conforming to the JSONAPI structure
46
- def serialize_to_hash(source)
47
- @top_level_sources = Set.new([source].flatten(1).compact.map {|s| top_level_source_key(s) })
48
-
49
- is_resource_collection = source.respond_to?(:to_ary)
50
-
51
- @included_objects = {}
52
-
53
- process_source_objects(source, @include_directives.include_directives)
44
+ # Converts a resource_set to a hash, conforming to the JSONAPI structure
45
+ def serialize_resource_set_to_hash_single(resource_set)
54
46
 
55
47
  primary_objects = []
48
+ included_objects = []
56
49
 
57
- # pull the processed objects corresponding to the source objects. Ensures we preserve order.
58
- if is_resource_collection
59
- source.each do |primary|
60
- if primary.id
61
- case primary
62
- when CachedResourceFragment then primary_objects.push(@included_objects[primary.type][primary.id][:object_hash])
63
- when Resource then primary_objects.push(@included_objects[primary.class._type][primary.id][:object_hash])
64
- else raise "Unknown source type #{primary.inspect}"
65
- end
66
- end
67
- end
68
- else
69
- if source.try(:id)
70
- case source
71
- when CachedResourceFragment then primary_objects.push(@included_objects[source.type][source.id][:object_hash])
72
- when Resource then primary_objects.push(@included_objects[source.class._type][source.id][:object_hash])
73
- else raise "Unknown source type #{source.inspect}"
50
+ resource_set.resource_klasses.each_value do |resource_klass|
51
+ resource_klass.each_value do |resource|
52
+ serialized_resource = object_hash(resource[:resource], resource[:relationships])
53
+
54
+ if resource[:primary]
55
+ primary_objects.push(serialized_resource)
56
+ else
57
+ included_objects.push(serialized_resource)
74
58
  end
75
59
  end
76
60
  end
77
61
 
62
+ fail "Too many primary objects for show" if (primary_objects.count > 1)
63
+ primary_hash = { 'data' => primary_objects[0] }
64
+
65
+ primary_hash['included'] = included_objects if included_objects.size > 0
66
+ primary_hash
67
+ end
68
+
69
+ def serialize_resource_set_to_hash_plural(resource_set)
70
+
71
+ primary_objects = []
78
72
  included_objects = []
79
- @included_objects.each_value do |objects|
80
- objects.each_value do |object|
81
- unless object[:primary]
82
- included_objects.push(object[:object_hash])
73
+
74
+ resource_set.resource_klasses.each_value do |resource_klass|
75
+ resource_klass.each_value do |resource|
76
+ serialized_resource = object_hash(resource[:resource], resource[:relationships])
77
+
78
+ if resource[:primary]
79
+ primary_objects.push(serialized_resource)
80
+ else
81
+ included_objects.push(serialized_resource)
83
82
  end
84
83
  end
85
84
  end
86
85
 
87
- primary_hash = { data: is_resource_collection ? primary_objects : primary_objects[0] }
86
+ primary_hash = { 'data' => primary_objects }
88
87
 
89
- primary_hash[:included] = included_objects if included_objects.size > 0
88
+ primary_hash['included'] = included_objects if included_objects.size > 0
90
89
  primary_hash
91
90
  end
92
91
 
93
- def serialize_to_relationship_hash(source, requested_relationship)
92
+ def serialize_related_resource_set_to_hash_plural(resource_set, _source_resource)
93
+ return serialize_resource_set_to_hash_plural(resource_set)
94
+ end
95
+
96
+ def serialize_to_relationship_hash(source, requested_relationship, resource_ids)
94
97
  if requested_relationship.is_a?(JSONAPI::Relationship::ToOne)
95
- data = to_one_linkage(source, requested_relationship)
98
+ data = to_one_linkage(resource_ids[0])
96
99
  else
97
- data = to_many_linkage(source, requested_relationship)
100
+ data = to_many_linkage(resource_ids)
98
101
  end
99
102
 
100
103
  rel_hash = { 'data': data }
@@ -105,14 +108,14 @@ module JSONAPI
105
108
  rel_hash
106
109
  end
107
110
 
108
- def query_link(query_params)
109
- link_builder.query_link(query_params)
110
- end
111
-
112
111
  def format_key(key)
113
112
  @key_formatter.format(key)
114
113
  end
115
114
 
115
+ def unformat_key(key)
116
+ @key_formatter.unformat(key)
117
+ end
118
+
116
119
  def format_value(value, format)
117
120
  @value_formatter_type_cache.get(format).format(value)
118
121
  end
@@ -128,7 +131,7 @@ module JSONAPI
128
131
  def config_description(resource_klass)
129
132
  {
130
133
  class_name: self.class.name,
131
- seriserialization_options: serialization_options.sort.map(&:as_json),
134
+ serialization_options: serialization_options.sort.map(&:as_json),
132
135
  supplying_attribute_fields: supplying_attribute_fields(resource_klass).sort,
133
136
  supplying_relationship_fields: supplying_relationship_fields(resource_klass).sort,
134
137
  link_builder_base_url: link_builder.base_url,
@@ -138,24 +141,28 @@ module JSONAPI
138
141
  }
139
142
  end
140
143
 
141
- # Returns a serialized hash for the source model
142
- def object_hash(source, include_directives = {})
144
+ def object_hash(source, relationship_data)
143
145
  obj_hash = {}
144
146
 
145
- if source.is_a?(JSONAPI::CachedResourceFragment)
146
- obj_hash['id'] = source.id
147
+ return obj_hash if source.nil?
148
+
149
+ fetchable_fields = Set.new(source.fetchable_fields)
150
+
151
+ if source.is_a?(JSONAPI::CachedResponseFragment)
152
+ id_format = source.resource_klass._attribute_options(:id)[:format]
153
+
154
+ id_format = 'id' if id_format == :default
155
+ obj_hash['id'] = format_value(source.id, id_format)
147
156
  obj_hash['type'] = source.type
148
157
 
149
158
  obj_hash['links'] = source.links_json if source.links_json
150
159
  obj_hash['attributes'] = source.attributes_json if source.attributes_json
151
160
 
152
- relationships = cached_relationships_hash(source, include_directives)
161
+ relationships = cached_relationships_hash(source, fetchable_fields, relationship_data)
153
162
  obj_hash['relationships'] = relationships unless relationships.blank?
154
163
 
155
164
  obj_hash['meta'] = source.meta_json if source.meta_json
156
165
  else
157
- fetchable_fields = Set.new(source.fetchable_fields)
158
-
159
166
  # TODO Should this maybe be using @id_formatter instead, for consistency?
160
167
  id_format = source.class._attribute_options(:id)[:format]
161
168
  # protect against ids that were declared as an attribute, but did not have a format set.
@@ -170,7 +177,7 @@ module JSONAPI
170
177
  attributes = attributes_hash(source, fetchable_fields)
171
178
  obj_hash['attributes'] = attributes unless attributes.empty?
172
179
 
173
- relationships = relationships_hash(source, fetchable_fields, include_directives)
180
+ relationships = relationships_hash(source, fetchable_fields, relationship_data)
174
181
  obj_hash['relationships'] = relationships unless relationships.blank?
175
182
 
176
183
  meta = meta_hash(source)
@@ -182,24 +189,11 @@ module JSONAPI
182
189
 
183
190
  private
184
191
 
185
- # Process the primary source object(s). This will then serialize associated object recursively based on the
186
- # requested includes. Fields are controlled fields option for each resource type, such
187
- # as fields: { people: [:id, :email, :comments], posts: [:id, :title, :author], comments: [:id, :body, :post]}
188
- # The fields options controls both fields and included links references.
189
- def process_source_objects(source, include_directives)
190
- if source.respond_to?(:to_ary)
191
- source.each { |resource| process_source_objects(resource, include_directives) }
192
- else
193
- return {} if source.nil?
194
- add_resource(source, include_directives, true)
195
- end
196
- end
197
-
198
192
  def supplying_attribute_fields(resource_klass)
199
193
  @_supplying_attribute_fields.fetch resource_klass do
200
194
  attrs = Set.new(resource_klass._attributes.keys.map(&:to_sym))
201
195
  cur = resource_klass
202
- while cur != JSONAPI::Resource
196
+ while !cur.root? # do not traverse beyond the first root resource
203
197
  if @fields.has_key?(cur._type)
204
198
  attrs &= @fields[cur._type]
205
199
  break
@@ -214,7 +208,7 @@ module JSONAPI
214
208
  @_supplying_relationship_fields.fetch resource_klass do
215
209
  relationships = Set.new(resource_klass._relationships.keys.map(&:to_sym))
216
210
  cur = resource_klass
217
- while cur != JSONAPI::Resource
211
+ while !cur.root? # do not traverse beyond the first root resource
218
212
  if @fields.has_key?(cur._type)
219
213
  relationships &= @fields[cur._type]
220
214
  break
@@ -236,7 +230,7 @@ module JSONAPI
236
230
  end
237
231
 
238
232
  def custom_generation_options
239
- {
233
+ @_custom_generation_options ||= {
240
234
  serializer: self,
241
235
  serialization_options: @serialization_options
242
236
  }
@@ -260,118 +254,62 @@ module JSONAPI
260
254
  (custom_links.is_a?(Hash) && custom_links) || {}
261
255
  end
262
256
 
263
- def top_level_source_key(source)
264
- case source
265
- when CachedResourceFragment then "#{source.resource_klass}_#{source.id}"
266
- when Resource then "#{source.class}_#{@id_formatter.format(source.id)}"
267
- else raise "Unknown source type #{source.inspect}"
268
- end
269
- end
270
-
271
- def self_referential_and_already_in_source(resource)
272
- resource && @top_level_sources.include?(top_level_source_key(resource))
273
- end
274
-
275
- def relationships_hash(source, fetchable_fields, include_directives = {})
276
- if source.is_a?(CachedResourceFragment)
277
- return cached_relationships_hash(source, include_directives)
278
- end
279
-
280
- include_directives[:include_related] ||= {}
281
-
282
- relationships = source.class._relationships.select{|k,v| fetchable_fields.include?(k) }
257
+ def relationships_hash(source, fetchable_fields, relationship_data)
258
+ relationships = source.class._relationships.select{|k,_v| fetchable_fields.include?(k) }
283
259
  field_set = supplying_relationship_fields(source.class) & relationships.keys
284
260
 
285
261
  relationships.each_with_object({}) do |(name, relationship), hash|
286
- ia = include_directives[:include_related][name]
287
- include_linkage = ia && ia[:include]
288
- include_linked_children = ia && !ia[:include_related].empty?
289
-
262
+ include_data = false
290
263
  if field_set.include?(name)
291
- ro = relationship_object(source, relationship, include_linkage)
292
- hash[format_key(name)] = ro unless ro.blank?
293
- end
294
-
295
- # If the object has been serialized once it will be in the related objects list,
296
- # but it's possible all children won't have been captured. So we must still go
297
- # through the relationships.
298
- if include_linkage || include_linked_children
299
- resources = if source.preloaded_fragments.has_key?(format_key(name))
300
- source.preloaded_fragments[format_key(name)].values
301
- else
302
- options = { filters: ia && ia[:include_filters] || {} }
303
- [source.public_send(name, options)].flatten(1).compact
304
- end
305
- resources.each do |resource|
306
- next if self_referential_and_already_in_source(resource)
307
- id = resource.id
308
- relationships_only = already_serialized?(relationship.type, id)
309
- if include_linkage && !relationships_only
310
- add_resource(resource, ia)
311
- elsif include_linked_children || relationships_only
312
- relationships_hash(resource, fetchable_fields, ia)
264
+ if relationship_data[name]
265
+ include_data = true
266
+ if relationship.is_a?(JSONAPI::Relationship::ToOne)
267
+ rids = relationship_data[name].first
268
+ else
269
+ rids = relationship_data[name]
313
270
  end
314
271
  end
272
+
273
+ ro = relationship_object(source, relationship, rids, include_data)
274
+ hash[format_key(name)] = ro unless ro.blank?
315
275
  end
316
276
  end
317
277
  end
318
278
 
319
- def cached_relationships_hash(source, include_directives)
320
- h = source.relationships || {}
321
- return h unless include_directives.has_key?(:include_related)
279
+ def cached_relationships_hash(source, fetchable_fields, relationship_data)
280
+ relationships = {}
322
281
 
323
- relationships = source.resource_klass._relationships.select do |k,v|
324
- source.fetchable_fields.include?(k)
282
+ source.relationships.try(:each_pair) do |k,v|
283
+ if fetchable_fields.include?(unformat_key(k).to_sym)
284
+ relationships[k.to_sym] = v
285
+ end
325
286
  end
326
287
 
327
- real_res = nil
328
- relationships.each do |rel_name, relationship|
329
- key = @key_formatter.format(rel_name)
330
- to_many = relationship.is_a? JSONAPI::Relationship::ToMany
288
+ field_set = supplying_relationship_fields(source.resource_klass).collect {|k| format_key(k).to_sym } & relationships.keys
331
289
 
332
- ia = include_directives[:include_related][rel_name]
333
- if ia
334
- if h.has_key?(key)
335
- h[key][:data] = to_many ? [] : nil
336
- end
290
+ relationships.each_with_object({}) do |(name, relationship), hash|
291
+ if field_set.include?(name)
292
+
293
+ relationship_name = unformat_key(name).to_sym
294
+ relationship_klass = source.resource_klass._relationships[relationship_name]
337
295
 
338
- fragments = source.preloaded_fragments[key]
339
- if fragments.nil?
340
- # The resources we want were not preloaded, we'll have to bypass the cache.
341
- # This happens when including through belongs_to polymorphic relationships
342
- if real_res.nil?
343
- real_res = source.to_real_resource
296
+ if relationship_klass.is_a?(JSONAPI::Relationship::ToOne)
297
+ # include_linkage = @always_include_to_one_linkage_data | relationship_klass.always_include_linkage_data
298
+ if relationship_data[relationship_name]
299
+ rids = relationship_data[relationship_name].first
300
+ relationship['data'] = to_one_linkage(rids)
344
301
  end
345
- relation_resources = [real_res.public_send(rel_name)].flatten(1).compact
346
- fragments = relation_resources.map{|r| [r.id, r]}.to_h
347
- end
348
- fragments.each do |id, f|
349
- add_resource(f, ia)
350
-
351
- if h.has_key?(key)
352
- # The hash already has everything we need except the :data field
353
- data = {
354
- type: format_key(f.is_a?(Resource) ? f.class._type : f.type),
355
- id: @id_formatter.format(id)
356
- }
357
-
358
- if to_many
359
- h[key][:data] << data
360
- else
361
- h[key][:data] = data
362
- end
302
+ else
303
+ # include_linkage = relationship_klass.always_include_linkage_data
304
+ if relationship_data[relationship_name]
305
+ rids = relationship_data[relationship_name]
306
+ relationship['data'] = to_many_linkage(rids)
363
307
  end
364
308
  end
309
+
310
+ hash[format_key(name)] = relationship
365
311
  end
366
312
  end
367
-
368
- return h
369
- end
370
-
371
- def already_serialized?(type, id)
372
- type = format_key(type)
373
- id = @id_formatter.format(id)
374
- @included_objects.key?(type) && @included_objects[type].key?(id)
375
313
  end
376
314
 
377
315
  def self_link(source, relationship)
@@ -389,153 +327,53 @@ module JSONAPI
389
327
  links.compact
390
328
  end
391
329
 
392
- def to_one_linkage(source, relationship)
393
- linkage_id = foreign_key_value(source, relationship)
394
- linkage_type = format_key(relationship.type_for_source(source))
395
- return unless linkage_id.present? && linkage_type.present?
396
-
397
- {
398
- type: linkage_type,
399
- id: linkage_id,
400
- }
401
- end
402
-
403
- def to_many_linkage(source, relationship)
330
+ def to_many_linkage(rids)
404
331
  linkage = []
405
- include_config = include_directives.include_config(relationship.name.to_sym) if include_directives
406
- include_filters = include_config[:include_filters] if include_config
407
- options = { filters: include_filters || {} }
408
332
 
409
- linkage_types_and_values = if source.preloaded_fragments.has_key?(format_key(relationship.name))
410
- source.preloaded_fragments[format_key(relationship.name)].map do |_, resource|
411
- [relationship.type, resource.id]
412
- end
413
- elsif relationship.polymorphic?
414
- assoc = source.public_send("records_for_#{relationship.name}", options)
415
- # Avoid hitting the database again for values already pre-loaded
416
- if assoc.respond_to?(:loaded?) and assoc.loaded?
417
- assoc.map do |obj|
418
- [obj.type.underscore.pluralize, obj.id]
419
- end
420
- else
421
- assoc = assoc.unscope(:includes) if assoc.is_a?(ActiveRecord::Relation)
422
- assoc.pluck(:type, :id).map do |type, id|
423
- [type.underscore.pluralize, id]
424
- end
425
- end
426
- else
427
- source.public_send(relationship.name, options).map do |value|
428
- [relationship.type, value.id]
333
+ rids && rids.each do |details|
334
+ id = details.id
335
+ type = details.resource_klass.try(:_type)
336
+ if type && id
337
+ linkage.append({'type' => format_key(type), 'id' => @id_formatter.format(id)})
429
338
  end
430
339
  end
431
340
 
432
- linkage_types_and_values.each do |type, value|
433
- if type && value
434
- linkage.append({type: format_key(type), id: @id_formatter.format(value)})
435
- end
436
- end
437
341
  linkage
438
342
  end
439
343
 
440
- def relationship_object_to_one(source, relationship, include_linkage)
441
- include_linkage = include_linkage | @always_include_to_one_linkage_data | relationship.always_include_linkage_data
442
- relationship_object_hash = {}
443
-
444
- links = default_relationship_links(source, relationship)
344
+ def to_one_linkage(rid)
345
+ return unless rid
445
346
 
446
- relationship_object_hash['links'] = links unless links.blank?
447
- relationship_object_hash[:data] = to_one_linkage(source, relationship) if include_linkage
448
- relationship_object_hash
347
+ {
348
+ 'type' => format_key(rid.resource_klass._type),
349
+ 'id' => @id_formatter.format(rid.id),
350
+ }
449
351
  end
450
352
 
451
- def relationship_object_to_many(source, relationship, include_linkage)
452
- include_linkage = include_linkage | relationship.always_include_linkage_data
453
- relationship_object_hash = {}
353
+ def relationship_object_to_one(source, relationship, rid, include_data)
354
+ link_object_hash = {}
454
355
 
455
356
  links = default_relationship_links(source, relationship)
456
- relationship_object_hash['links'] = links unless links.blank?
457
- relationship_object_hash[:data] = to_many_linkage(source, relationship) if include_linkage
458
- relationship_object_hash
459
- end
460
357
 
461
- def relationship_object(source, relationship, include_linkage = false)
462
- if relationship.is_a?(JSONAPI::Relationship::ToOne)
463
- relationship_object_to_one(source, relationship, include_linkage)
464
- elsif relationship.is_a?(JSONAPI::Relationship::ToMany)
465
- relationship_object_to_many(source, relationship, include_linkage)
466
- end
358
+ link_object_hash['links'] = links unless links.blank?
359
+ link_object_hash['data'] = to_one_linkage(rid) if include_data
360
+ link_object_hash
467
361
  end
468
362
 
469
- # Extracts the foreign key value for a to_one relationship.
470
- def foreign_key_value(source, relationship)
471
- # If you have changed the key_name, don't even try to look at `"#{relationship.name}_id"`
472
- # just load the association and call the custom key_name
473
- foreign_key_type_changed = relationship.options[:foreign_key_type_changed] || false
474
- related_resource_id =
475
- if source.preloaded_fragments.has_key?(format_key(relationship.name))
476
- source.preloaded_fragments[format_key(relationship.name)].values.first.try(:id)
477
- elsif !foreign_key_type_changed && source.respond_to?("#{relationship.name}_id")
478
- # If you have direct access to the underlying id, you don't have to load the relationship
479
- # which can save quite a lot of time when loading a lot of data.
480
- # This does not apply to e.g. has_one :through relationships.
481
- source.public_send("#{relationship.name}_id")
482
- else
483
- source.public_send(relationship.name).try(:id)
484
- end
485
- return nil unless related_resource_id
486
- @id_formatter.format(related_resource_id)
487
- end
363
+ def relationship_object_to_many(source, relationship, rids, include_data)
364
+ link_object_hash = {}
488
365
 
489
- def foreign_key_types_and_values(source, relationship)
490
- if relationship.is_a?(JSONAPI::Relationship::ToMany)
491
- if relationship.polymorphic?
492
- assoc = source._model.public_send(relationship.name)
493
- # Avoid hitting the database again for values already pre-loaded
494
- if assoc.respond_to?(:loaded?) and assoc.loaded?
495
- assoc.map do |obj|
496
- [obj.type.underscore.pluralize, @id_formatter.format(obj.id)]
497
- end
498
- else
499
- assoc.pluck(:type, :id).map do |type, id|
500
- [type.underscore.pluralize, @id_formatter.format(id)]
501
- end
502
- end
503
- else
504
- source.public_send(relationship.name).map do |value|
505
- [relationship.type, @id_formatter.format(value.id)]
506
- end
507
- end
508
- end
509
- end
510
-
511
- # Sets that an object should be included in the primary document of the response.
512
- def set_primary(type, id)
513
- type = format_key(type)
514
- @included_objects[type][id][:primary] = true
366
+ links = default_relationship_links(source, relationship)
367
+ link_object_hash['links'] = links unless links.blank?
368
+ link_object_hash['data'] = to_many_linkage(rids) if include_data
369
+ link_object_hash
515
370
  end
516
371
 
517
- def add_resource(source, include_directives, primary = false)
518
- type = source.is_a?(JSONAPI::CachedResourceFragment) ? source.type : source.class._type
519
- id = source.id
520
-
521
- @included_objects[type] ||= {}
522
- existing = @included_objects[type][id]
523
-
524
- if existing.nil?
525
- obj_hash = object_hash(source, include_directives)
526
- @included_objects[type][id] = {
527
- primary: primary,
528
- object_hash: obj_hash,
529
- includes: Set.new(include_directives[:include_related].keys)
530
- }
531
- else
532
- include_related = Set.new(include_directives[:include_related].keys)
533
- unless existing[:includes].superset?(include_related)
534
- obj_hash = object_hash(source, include_directives)
535
- @included_objects[type][id][:object_hash].deep_merge!(obj_hash)
536
- @included_objects[type][id][:includes].add(include_related)
537
- @included_objects[type][id][:primary] = existing[:primary] | primary
538
- end
372
+ def relationship_object(source, relationship, rid, include_data)
373
+ if relationship.is_a?(JSONAPI::Relationship::ToOne)
374
+ relationship_object_to_one(source, relationship, rid, include_data)
375
+ elsif relationship.is_a?(JSONAPI::Relationship::ToMany)
376
+ relationship_object_to_many(source, relationship, rid, include_data)
539
377
  end
540
378
  end
541
379