jsonapi-resources 0.9.0 → 0.10.6
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 +5 -5
- 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 -105
- 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 +71 -8
- data/lib/jsonapi/error.rb +27 -0
- data/lib/jsonapi/error_codes.rb +2 -0
- data/lib/jsonapi/exceptions.rb +80 -50
- data/lib/jsonapi/formatter.rb +3 -3
- data/lib/jsonapi/include_directives.rb +18 -65
- data/lib/jsonapi/link_builder.rb +74 -80
- data/lib/jsonapi/operation.rb +16 -5
- data/lib/jsonapi/operation_result.rb +74 -16
- data/lib/jsonapi/path.rb +43 -0
- data/lib/jsonapi/path_segment.rb +76 -0
- data/lib/jsonapi/processor.rb +239 -111
- data/lib/jsonapi/relationship.rb +153 -15
- data/lib/jsonapi/request_parser.rb +430 -367
- data/lib/jsonapi/resource.rb +3 -1253
- 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 +143 -285
- 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 +105 -83
- data/lib/jsonapi/routing_ext.rb +48 -26
- data/lib/jsonapi-resources.rb +20 -4
- data/lib/tasks/check_upgrade.rake +52 -0
- metadata +50 -20
- 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,78 +41,81 @@ 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
|
-
{
|
101
|
-
links: {
|
102
|
-
self: self_link(source, requested_relationship),
|
103
|
-
related: related_link(source, requested_relationship)
|
104
|
-
},
|
105
|
-
data: data
|
106
|
-
}
|
107
|
-
end
|
103
|
+
rel_hash = { 'data': data }
|
108
104
|
|
109
|
-
|
110
|
-
|
105
|
+
links = default_relationship_links(source, requested_relationship)
|
106
|
+
rel_hash['links'] = links unless links.blank?
|
107
|
+
|
108
|
+
rel_hash
|
111
109
|
end
|
112
110
|
|
113
111
|
def format_key(key)
|
114
112
|
@key_formatter.format(key)
|
115
113
|
end
|
116
114
|
|
115
|
+
def unformat_key(key)
|
116
|
+
@key_formatter.unformat(key)
|
117
|
+
end
|
118
|
+
|
117
119
|
def format_value(value, format)
|
118
120
|
@value_formatter_type_cache.get(format).format(value)
|
119
121
|
end
|
@@ -129,35 +131,38 @@ module JSONAPI
|
|
129
131
|
def config_description(resource_klass)
|
130
132
|
{
|
131
133
|
class_name: self.class.name,
|
132
|
-
|
134
|
+
serialization_options: serialization_options.sort.map(&:as_json),
|
133
135
|
supplying_attribute_fields: supplying_attribute_fields(resource_klass).sort,
|
134
136
|
supplying_relationship_fields: supplying_relationship_fields(resource_klass).sort,
|
135
137
|
link_builder_base_url: link_builder.base_url,
|
136
|
-
route_formatter_class: link_builder.route_formatter.uncached.class.name,
|
137
138
|
key_formatter_class: key_formatter.uncached.class.name,
|
138
139
|
always_include_to_one_linkage_data: always_include_to_one_linkage_data,
|
139
140
|
always_include_to_many_linkage_data: always_include_to_many_linkage_data
|
140
141
|
}
|
141
142
|
end
|
142
143
|
|
143
|
-
|
144
|
-
def object_hash(source, include_directives = {})
|
144
|
+
def object_hash(source, relationship_data)
|
145
145
|
obj_hash = {}
|
146
146
|
|
147
|
-
if source.
|
148
|
-
|
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)
|
149
156
|
obj_hash['type'] = source.type
|
150
157
|
|
151
158
|
obj_hash['links'] = source.links_json if source.links_json
|
152
159
|
obj_hash['attributes'] = source.attributes_json if source.attributes_json
|
153
160
|
|
154
|
-
relationships = cached_relationships_hash(source,
|
155
|
-
obj_hash['relationships'] = relationships unless relationships.
|
161
|
+
relationships = cached_relationships_hash(source, fetchable_fields, relationship_data)
|
162
|
+
obj_hash['relationships'] = relationships unless relationships.blank?
|
156
163
|
|
157
164
|
obj_hash['meta'] = source.meta_json if source.meta_json
|
158
165
|
else
|
159
|
-
fetchable_fields = Set.new(source.fetchable_fields)
|
160
|
-
|
161
166
|
# TODO Should this maybe be using @id_formatter instead, for consistency?
|
162
167
|
id_format = source.class._attribute_options(:id)[:format]
|
163
168
|
# protect against ids that were declared as an attribute, but did not have a format set.
|
@@ -172,8 +177,8 @@ module JSONAPI
|
|
172
177
|
attributes = attributes_hash(source, fetchable_fields)
|
173
178
|
obj_hash['attributes'] = attributes unless attributes.empty?
|
174
179
|
|
175
|
-
relationships = relationships_hash(source, fetchable_fields,
|
176
|
-
obj_hash['relationships'] = relationships unless relationships.
|
180
|
+
relationships = relationships_hash(source, fetchable_fields, relationship_data)
|
181
|
+
obj_hash['relationships'] = relationships unless relationships.blank?
|
177
182
|
|
178
183
|
meta = meta_hash(source)
|
179
184
|
obj_hash['meta'] = meta unless meta.empty?
|
@@ -184,24 +189,11 @@ module JSONAPI
|
|
184
189
|
|
185
190
|
private
|
186
191
|
|
187
|
-
# Process the primary source object(s). This will then serialize associated object recursively based on the
|
188
|
-
# requested includes. Fields are controlled fields option for each resource type, such
|
189
|
-
# as fields: { people: [:id, :email, :comments], posts: [:id, :title, :author], comments: [:id, :body, :post]}
|
190
|
-
# The fields options controls both fields and included links references.
|
191
|
-
def process_source_objects(source, include_directives)
|
192
|
-
if source.respond_to?(:to_ary)
|
193
|
-
source.each { |resource| process_source_objects(resource, include_directives) }
|
194
|
-
else
|
195
|
-
return {} if source.nil?
|
196
|
-
add_resource(source, include_directives, true)
|
197
|
-
end
|
198
|
-
end
|
199
|
-
|
200
192
|
def supplying_attribute_fields(resource_klass)
|
201
193
|
@_supplying_attribute_fields.fetch resource_klass do
|
202
194
|
attrs = Set.new(resource_klass._attributes.keys.map(&:to_sym))
|
203
195
|
cur = resource_klass
|
204
|
-
while cur
|
196
|
+
while !cur.root? # do not traverse beyond the first root resource
|
205
197
|
if @fields.has_key?(cur._type)
|
206
198
|
attrs &= @fields[cur._type]
|
207
199
|
break
|
@@ -216,7 +208,7 @@ module JSONAPI
|
|
216
208
|
@_supplying_relationship_fields.fetch resource_klass do
|
217
209
|
relationships = Set.new(resource_klass._relationships.keys.map(&:to_sym))
|
218
210
|
cur = resource_klass
|
219
|
-
while cur
|
211
|
+
while !cur.root? # do not traverse beyond the first root resource
|
220
212
|
if @fields.has_key?(cur._type)
|
221
213
|
relationships &= @fields[cur._type]
|
222
214
|
break
|
@@ -238,7 +230,7 @@ module JSONAPI
|
|
238
230
|
end
|
239
231
|
|
240
232
|
def custom_generation_options
|
241
|
-
{
|
233
|
+
@_custom_generation_options ||= {
|
242
234
|
serializer: self,
|
243
235
|
serialization_options: @serialization_options
|
244
236
|
}
|
@@ -251,7 +243,9 @@ module JSONAPI
|
|
251
243
|
|
252
244
|
def links_hash(source)
|
253
245
|
links = custom_links_hash(source)
|
254
|
-
links
|
246
|
+
if !links.key?('self') && !source.class.exclude_link?(:self)
|
247
|
+
links['self'] = link_builder.self_link(source)
|
248
|
+
end
|
255
249
|
links.compact
|
256
250
|
end
|
257
251
|
|
@@ -260,116 +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
|
-
if include_linkage || include_linked_children
|
298
|
-
resources = if source.preloaded_fragments.has_key?(format_key(name))
|
299
|
-
source.preloaded_fragments[format_key(name)].values
|
300
|
-
else
|
301
|
-
[source.public_send(name)].flatten(1).compact
|
302
|
-
end
|
303
|
-
resources.each do |resource|
|
304
|
-
next if self_referential_and_already_in_source(resource)
|
305
|
-
id = resource.id
|
306
|
-
relationships_only = already_serialized?(relationship.type, id)
|
307
|
-
if include_linkage && !relationships_only
|
308
|
-
add_resource(resource, ia)
|
309
|
-
elsif include_linked_children || relationships_only
|
310
|
-
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]
|
311
270
|
end
|
312
271
|
end
|
272
|
+
|
273
|
+
ro = relationship_object(source, relationship, rids, include_data)
|
274
|
+
hash[format_key(name)] = ro unless ro.blank?
|
313
275
|
end
|
314
276
|
end
|
315
277
|
end
|
316
278
|
|
317
|
-
def cached_relationships_hash(source,
|
318
|
-
|
319
|
-
return h unless include_directives.has_key?(:include_related)
|
279
|
+
def cached_relationships_hash(source, fetchable_fields, relationship_data)
|
280
|
+
relationships = {}
|
320
281
|
|
321
|
-
|
322
|
-
|
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
|
323
286
|
end
|
324
287
|
|
325
|
-
|
326
|
-
relationships.each do |rel_name, relationship|
|
327
|
-
key = @key_formatter.format(rel_name)
|
328
|
-
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
|
329
289
|
|
330
|
-
|
331
|
-
if
|
332
|
-
|
333
|
-
|
334
|
-
|
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]
|
335
295
|
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
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)
|
342
301
|
end
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
if h.has_key?(key)
|
350
|
-
# The hash already has everything we need except the :data field
|
351
|
-
data = {
|
352
|
-
type: format_key(f.is_a?(Resource) ? f.class._type : f.type),
|
353
|
-
id: @id_formatter.format(id)
|
354
|
-
}
|
355
|
-
|
356
|
-
if to_many
|
357
|
-
h[key][:data] << data
|
358
|
-
else
|
359
|
-
h[key][:data] = data
|
360
|
-
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)
|
361
307
|
end
|
362
308
|
end
|
309
|
+
|
310
|
+
hash[format_key(name)] = relationship
|
363
311
|
end
|
364
312
|
end
|
365
|
-
|
366
|
-
return h
|
367
|
-
end
|
368
|
-
|
369
|
-
def already_serialized?(type, id)
|
370
|
-
type = format_key(type)
|
371
|
-
id = @id_formatter.format(id)
|
372
|
-
@included_objects.key?(type) && @included_objects[type].key?(id)
|
373
313
|
end
|
374
314
|
|
375
315
|
def self_link(source, relationship)
|
@@ -380,151 +320,69 @@ module JSONAPI
|
|
380
320
|
link_builder.relationships_related_link(source, relationship)
|
381
321
|
end
|
382
322
|
|
383
|
-
def
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
{
|
389
|
-
type: linkage_type,
|
390
|
-
id: linkage_id,
|
391
|
-
}
|
323
|
+
def default_relationship_links(source, relationship)
|
324
|
+
links = {}
|
325
|
+
links['self'] = self_link(source, relationship) unless relationship.exclude_link?(:self)
|
326
|
+
links['related'] = related_link(source, relationship) unless relationship.exclude_link?(:related)
|
327
|
+
links.compact
|
392
328
|
end
|
393
329
|
|
394
|
-
def to_many_linkage(
|
330
|
+
def to_many_linkage(rids)
|
395
331
|
linkage = []
|
396
|
-
linkage_types_and_values = if source.preloaded_fragments.has_key?(format_key(relationship.name))
|
397
|
-
source.preloaded_fragments[format_key(relationship.name)].map do |_, resource|
|
398
|
-
[relationship.type, resource.id]
|
399
|
-
end
|
400
|
-
elsif relationship.polymorphic?
|
401
|
-
assoc = source._model.public_send(relationship.name)
|
402
|
-
# Avoid hitting the database again for values already pre-loaded
|
403
|
-
if assoc.respond_to?(:loaded?) and assoc.loaded?
|
404
|
-
assoc.map do |obj|
|
405
|
-
[obj.type.underscore.pluralize, obj.id]
|
406
|
-
end
|
407
|
-
else
|
408
|
-
assoc.pluck(:type, :id).map do |type, id|
|
409
|
-
[type.underscore.pluralize, id]
|
410
|
-
end
|
411
|
-
end
|
412
|
-
else
|
413
|
-
source.public_send(relationship.name).map do |value|
|
414
|
-
[relationship.type, value.id]
|
415
|
-
end
|
416
|
-
end
|
417
332
|
|
418
|
-
|
419
|
-
|
420
|
-
|
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)})
|
421
338
|
end
|
422
339
|
end
|
340
|
+
|
423
341
|
linkage
|
424
342
|
end
|
425
343
|
|
426
|
-
def
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
link_object_hash
|
344
|
+
def to_one_linkage(rid)
|
345
|
+
return unless rid
|
346
|
+
|
347
|
+
{
|
348
|
+
'type' => format_key(rid.resource_klass._type),
|
349
|
+
'id' => @id_formatter.format(rid.id),
|
350
|
+
}
|
434
351
|
end
|
435
352
|
|
436
|
-
def
|
437
|
-
include_linkage = include_linkage | relationship.always_include_linkage_data
|
353
|
+
def relationship_object_to_one(source, relationship, rid, include_data)
|
438
354
|
link_object_hash = {}
|
439
|
-
link_object_hash[:links] = {}
|
440
|
-
link_object_hash[:links][:self] = self_link(source, relationship)
|
441
|
-
link_object_hash[:links][:related] = related_link(source, relationship)
|
442
|
-
link_object_hash[:data] = to_many_linkage(source, relationship) if include_linkage
|
443
|
-
link_object_hash
|
444
|
-
end
|
445
355
|
|
446
|
-
|
447
|
-
if relationship.is_a?(JSONAPI::Relationship::ToOne)
|
448
|
-
link_object_to_one(source, relationship, include_linkage)
|
449
|
-
elsif relationship.is_a?(JSONAPI::Relationship::ToMany)
|
450
|
-
link_object_to_many(source, relationship, include_linkage)
|
451
|
-
end
|
452
|
-
end
|
356
|
+
links = default_relationship_links(source, relationship)
|
453
357
|
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
source.preloaded_fragments[format_key(relationship.name)].values.first.try(:id)
|
458
|
-
elsif source.respond_to?("#{relationship.name}_id")
|
459
|
-
# If you have direct access to the underlying id, you don't have to load the relationship
|
460
|
-
# which can save quite a lot of time when loading a lot of data.
|
461
|
-
# This does not apply to e.g. has_one :through relationships.
|
462
|
-
source.public_send("#{relationship.name}_id")
|
463
|
-
else
|
464
|
-
source.public_send(relationship.name).try(:id)
|
465
|
-
end
|
466
|
-
return nil unless related_resource_id
|
467
|
-
@id_formatter.format(related_resource_id)
|
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
|
468
361
|
end
|
469
362
|
|
470
|
-
def
|
471
|
-
|
472
|
-
if relationship.polymorphic?
|
473
|
-
assoc = source._model.public_send(relationship.name)
|
474
|
-
# Avoid hitting the database again for values already pre-loaded
|
475
|
-
if assoc.respond_to?(:loaded?) and assoc.loaded?
|
476
|
-
assoc.map do |obj|
|
477
|
-
[obj.type.underscore.pluralize, @id_formatter.format(obj.id)]
|
478
|
-
end
|
479
|
-
else
|
480
|
-
assoc.pluck(:type, :id).map do |type, id|
|
481
|
-
[type.underscore.pluralize, @id_formatter.format(id)]
|
482
|
-
end
|
483
|
-
end
|
484
|
-
else
|
485
|
-
source.public_send(relationship.name).map do |value|
|
486
|
-
[relationship.type, @id_formatter.format(value.id)]
|
487
|
-
end
|
488
|
-
end
|
489
|
-
end
|
490
|
-
end
|
363
|
+
def relationship_object_to_many(source, relationship, rids, include_data)
|
364
|
+
link_object_hash = {}
|
491
365
|
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
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
|
496
370
|
end
|
497
371
|
|
498
|
-
def
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
existing = @included_objects[type][id]
|
504
|
-
|
505
|
-
if existing.nil?
|
506
|
-
obj_hash = object_hash(source, include_directives)
|
507
|
-
@included_objects[type][id] = {
|
508
|
-
primary: primary,
|
509
|
-
object_hash: obj_hash,
|
510
|
-
includes: Set.new(include_directives[:include_related].keys)
|
511
|
-
}
|
512
|
-
else
|
513
|
-
include_related = Set.new(include_directives[:include_related].keys)
|
514
|
-
unless existing[:includes].superset?(include_related)
|
515
|
-
obj_hash = object_hash(source, include_directives)
|
516
|
-
@included_objects[type][id][:object_hash].deep_merge!(obj_hash)
|
517
|
-
@included_objects[type][id][:includes].add(include_related)
|
518
|
-
@included_objects[type][id][:primary] = existing[:primary] | primary
|
519
|
-
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)
|
520
377
|
end
|
521
378
|
end
|
522
379
|
|
523
380
|
def generate_link_builder(primary_resource_klass, options)
|
524
381
|
LinkBuilder.new(
|
525
382
|
base_url: options.fetch(:base_url, ''),
|
526
|
-
route_formatter: options.fetch(:route_formatter, JSONAPI.configuration.route_formatter),
|
527
383
|
primary_resource_klass: primary_resource_klass,
|
384
|
+
route_formatter: options.fetch(:route_formatter, JSONAPI.configuration.route_formatter),
|
385
|
+
url_helpers: options.fetch(:url_helpers, options[:controller]),
|
528
386
|
)
|
529
387
|
end
|
530
388
|
end
|