jsonapi-resources 0.9.12 → 0.10.7

Sign up to get free protection for your applications and to get access to all the features.
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 +18 -25
  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 -24
  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 +3 -0
  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 +34 -31
  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