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.
- checksums.yaml +4 -4
- data/LICENSE.txt +1 -1
- data/README.md +34 -11
- data/lib/bug_report_templates/rails_5_latest.rb +125 -0
- data/lib/bug_report_templates/rails_5_master.rb +140 -0
- data/lib/jsonapi/active_relation/adapters/join_left_active_record_adapter.rb +27 -0
- data/lib/jsonapi/active_relation/join_manager.rb +303 -0
- data/lib/jsonapi/active_relation_resource.rb +884 -0
- data/lib/jsonapi/acts_as_resource_controller.rb +122 -106
- data/lib/jsonapi/basic_resource.rb +1162 -0
- data/lib/jsonapi/cached_response_fragment.rb +127 -0
- data/lib/jsonapi/compiled_json.rb +11 -1
- data/lib/jsonapi/configuration.rb +57 -8
- data/lib/jsonapi/error.rb +27 -0
- data/lib/jsonapi/error_codes.rb +2 -0
- data/lib/jsonapi/exceptions.rb +63 -40
- data/lib/jsonapi/formatter.rb +3 -3
- data/lib/jsonapi/include_directives.rb +18 -75
- data/lib/jsonapi/link_builder.rb +16 -19
- data/lib/jsonapi/operation.rb +16 -5
- data/lib/jsonapi/operation_result.rb +73 -15
- data/lib/jsonapi/paginator.rb +17 -0
- data/lib/jsonapi/path.rb +43 -0
- data/lib/jsonapi/path_segment.rb +76 -0
- data/lib/jsonapi/processor.rb +246 -111
- data/lib/jsonapi/relationship.rb +117 -18
- data/lib/jsonapi/request_parser.rb +383 -396
- data/lib/jsonapi/resource.rb +3 -1376
- data/lib/jsonapi/resource_controller_metal.rb +5 -2
- data/lib/jsonapi/resource_fragment.rb +47 -0
- data/lib/jsonapi/resource_id_tree.rb +112 -0
- data/lib/jsonapi/resource_identity.rb +42 -0
- data/lib/jsonapi/resource_serializer.rb +124 -286
- data/lib/jsonapi/resource_set.rb +176 -0
- data/lib/jsonapi/resources/railtie.rb +9 -0
- data/lib/jsonapi/resources/version.rb +1 -1
- data/lib/jsonapi/response_document.rb +104 -87
- data/lib/jsonapi/routing_ext.rb +19 -21
- data/lib/jsonapi-resources.rb +20 -4
- data/lib/tasks/check_upgrade.rake +52 -0
- metadata +36 -33
- data/lib/jsonapi/cached_resource_fragment.rb +0 -127
- data/lib/jsonapi/operation_dispatcher.rb +0 -88
- data/lib/jsonapi/operation_results.rb +0 -35
- 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,
|
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
|
46
|
-
def
|
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
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
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
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
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
|
86
|
+
primary_hash = { 'data' => primary_objects }
|
88
87
|
|
89
|
-
primary_hash[
|
88
|
+
primary_hash['included'] = included_objects if included_objects.size > 0
|
90
89
|
primary_hash
|
91
90
|
end
|
92
91
|
|
93
|
-
def
|
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(
|
98
|
+
data = to_one_linkage(resource_ids[0])
|
96
99
|
else
|
97
|
-
data = to_many_linkage(
|
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
|
-
|
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
|
-
|
142
|
-
def object_hash(source, include_directives = {})
|
144
|
+
def object_hash(source, relationship_data)
|
143
145
|
obj_hash = {}
|
144
146
|
|
145
|
-
if source.
|
146
|
-
|
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,
|
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,
|
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
|
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
|
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
|
264
|
-
|
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
|
-
|
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
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
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,
|
320
|
-
|
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
|
-
|
324
|
-
|
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
|
-
|
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
|
-
|
333
|
-
if
|
334
|
-
|
335
|
-
|
336
|
-
|
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
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
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
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
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
|
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
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
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
|
441
|
-
|
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
|
-
|
447
|
-
|
448
|
-
|
347
|
+
{
|
348
|
+
'type' => format_key(rid.resource_klass._type),
|
349
|
+
'id' => @id_formatter.format(rid.id),
|
350
|
+
}
|
449
351
|
end
|
450
352
|
|
451
|
-
def
|
452
|
-
|
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
|
-
|
462
|
-
|
463
|
-
|
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
|
-
|
470
|
-
|
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
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
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
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
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
|
|