sanger-jsonapi-resources 0.1.1 → 0.2.0
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 +35 -12
- 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 +26 -0
- data/lib/jsonapi/active_relation/join_manager.rb +297 -0
- data/lib/jsonapi/active_relation_resource.rb +898 -0
- data/lib/jsonapi/acts_as_resource_controller.rb +130 -113
- data/lib/jsonapi/basic_resource.rb +1164 -0
- data/lib/jsonapi/cached_response_fragment.rb +129 -0
- data/lib/jsonapi/callbacks.rb +2 -0
- data/lib/jsonapi/compatibility_helper.rb +29 -0
- data/lib/jsonapi/compiled_json.rb +13 -1
- data/lib/jsonapi/configuration.rb +88 -21
- data/lib/jsonapi/error.rb +29 -0
- data/lib/jsonapi/error_codes.rb +4 -0
- data/lib/jsonapi/exceptions.rb +82 -50
- data/lib/jsonapi/formatter.rb +5 -3
- data/lib/jsonapi/include_directives.rb +22 -67
- data/lib/jsonapi/link_builder.rb +76 -80
- data/lib/jsonapi/mime_types.rb +6 -10
- data/lib/jsonapi/naive_cache.rb +2 -0
- data/lib/jsonapi/operation.rb +18 -5
- data/lib/jsonapi/operation_result.rb +76 -16
- data/lib/jsonapi/paginator.rb +2 -0
- data/lib/jsonapi/path.rb +45 -0
- data/lib/jsonapi/path_segment.rb +78 -0
- data/lib/jsonapi/processor.rb +193 -115
- data/lib/jsonapi/relationship.rb +145 -14
- data/lib/jsonapi/request.rb +734 -0
- data/lib/jsonapi/resource.rb +3 -1251
- data/lib/jsonapi/resource_controller.rb +2 -0
- data/lib/jsonapi/resource_controller_metal.rb +7 -1
- data/lib/jsonapi/resource_fragment.rb +56 -0
- data/lib/jsonapi/resource_identity.rb +44 -0
- data/lib/jsonapi/resource_serializer.rb +158 -284
- data/lib/jsonapi/resource_set.rb +196 -0
- data/lib/jsonapi/resource_tree.rb +236 -0
- data/lib/jsonapi/resources/railtie.rb +9 -0
- data/lib/jsonapi/resources/version.rb +1 -1
- data/lib/jsonapi/response_document.rb +107 -83
- data/lib/jsonapi/routing_ext.rb +50 -26
- data/lib/jsonapi-resources.rb +23 -5
- data/lib/tasks/check_upgrade.rake +52 -0
- metadata +43 -31
- 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
- data/lib/jsonapi/request_parser.rb +0 -678
data/lib/jsonapi/processor.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module JSONAPI
|
2
4
|
class Processor
|
3
5
|
include Callbacks
|
@@ -17,30 +19,6 @@ module JSONAPI
|
|
17
19
|
:remove_to_one_relationship,
|
18
20
|
:operation
|
19
21
|
|
20
|
-
class << self
|
21
|
-
def processor_instance_for(resource_klass, operation_type, params)
|
22
|
-
_processor_from_resource_type(resource_klass).new(resource_klass, operation_type, params)
|
23
|
-
end
|
24
|
-
|
25
|
-
def _processor_from_resource_type(resource_klass)
|
26
|
-
processor = resource_klass.name.gsub(/Resource$/,'Processor').safe_constantize
|
27
|
-
if processor.nil?
|
28
|
-
processor = JSONAPI.configuration.default_processor_klass
|
29
|
-
end
|
30
|
-
|
31
|
-
return processor
|
32
|
-
end
|
33
|
-
|
34
|
-
def transactional_operation_type?(operation_type)
|
35
|
-
case operation_type
|
36
|
-
when :find, :show, :show_related_resource, :show_related_resources
|
37
|
-
return false
|
38
|
-
else
|
39
|
-
return true
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
22
|
attr_reader :resource_klass, :operation_type, :params, :context, :result, :result_options
|
45
23
|
|
46
24
|
def initialize(resource_klass, operation_type, params)
|
@@ -66,93 +44,124 @@ module JSONAPI
|
|
66
44
|
def find
|
67
45
|
filters = params[:filters]
|
68
46
|
include_directives = params[:include_directives]
|
69
|
-
sort_criteria = params
|
47
|
+
sort_criteria = params[:sort_criteria]
|
70
48
|
paginator = params[:paginator]
|
71
49
|
fields = params[:fields]
|
50
|
+
serializer = params[:serializer]
|
72
51
|
|
73
52
|
verified_filters = resource_klass.verify_filters(filters, context)
|
74
|
-
|
53
|
+
|
54
|
+
options = {
|
75
55
|
context: context,
|
76
|
-
include_directives: include_directives,
|
77
56
|
sort_criteria: sort_criteria,
|
78
57
|
paginator: paginator,
|
79
|
-
fields: fields
|
58
|
+
fields: fields,
|
59
|
+
filters: verified_filters,
|
60
|
+
include_directives: include_directives
|
80
61
|
}
|
81
62
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
find_options)
|
86
|
-
else
|
87
|
-
resource_klass.find(verified_filters, find_options)
|
88
|
-
end
|
63
|
+
resource_set = find_resource_set(include_directives, options)
|
64
|
+
|
65
|
+
resource_set.populate!(serializer, context, options)
|
89
66
|
|
90
|
-
page_options =
|
91
|
-
if (JSONAPI.configuration.top_level_meta_include_record_count ||
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
include_directives: include_directives)
|
67
|
+
page_options = result_options
|
68
|
+
if (JSONAPI.configuration.top_level_meta_include_record_count || (paginator && paginator.class.requires_record_count))
|
69
|
+
page_options[:record_count] = resource_klass.count(verified_filters,
|
70
|
+
context: context,
|
71
|
+
include_directives: include_directives)
|
96
72
|
end
|
97
73
|
|
98
|
-
if (JSONAPI.configuration.top_level_meta_include_page_count && page_options[:record_count])
|
74
|
+
if (JSONAPI.configuration.top_level_meta_include_page_count && paginator && page_options[:record_count])
|
99
75
|
page_options[:page_count] = paginator ? paginator.calculate_page_count(page_options[:record_count]) : 1
|
100
76
|
end
|
101
77
|
|
102
78
|
if JSONAPI.configuration.top_level_links_include_pagination && paginator
|
103
|
-
page_options[:pagination_params] = paginator.links_page_params(page_options)
|
79
|
+
page_options[:pagination_params] = paginator.links_page_params(page_options.merge(fetched_resources: resource_set))
|
104
80
|
end
|
105
81
|
|
106
|
-
|
82
|
+
JSONAPI::ResourcesSetOperationResult.new(:ok, resource_set, page_options)
|
107
83
|
end
|
108
84
|
|
109
85
|
def show
|
110
86
|
include_directives = params[:include_directives]
|
111
87
|
fields = params[:fields]
|
112
88
|
id = params[:id]
|
89
|
+
serializer = params[:serializer]
|
113
90
|
|
114
91
|
key = resource_klass.verify_key(id, context)
|
115
92
|
|
116
|
-
|
93
|
+
options = {
|
117
94
|
context: context,
|
118
|
-
|
119
|
-
|
95
|
+
fields: fields,
|
96
|
+
filters: { resource_klass._primary_key => key },
|
97
|
+
include_directives: include_directives
|
120
98
|
}
|
121
99
|
|
122
|
-
|
123
|
-
resource_klass.find_by_key_serialized_with_caching(key,
|
124
|
-
params[:cache_serializer],
|
125
|
-
find_options)
|
126
|
-
else
|
127
|
-
resource_klass.find_by_key(key, find_options)
|
128
|
-
end
|
100
|
+
resource_set = find_resource_set(include_directives, options)
|
129
101
|
|
130
|
-
|
102
|
+
fail JSONAPI::Exceptions::RecordNotFound.new(id) if resource_set.resource_klasses.empty?
|
103
|
+
resource_set.populate!(serializer, context, options)
|
104
|
+
|
105
|
+
JSONAPI::ResourceSetOperationResult.new(:ok, resource_set, result_options)
|
131
106
|
end
|
132
107
|
|
133
108
|
def show_relationship
|
134
109
|
parent_key = params[:parent_key]
|
135
110
|
relationship_type = params[:relationship_type].to_sym
|
111
|
+
paginator = params[:paginator]
|
112
|
+
sort_criteria = params[:sort_criteria]
|
113
|
+
include_directives = params[:include_directives]
|
114
|
+
fields = params[:fields]
|
136
115
|
|
137
116
|
parent_resource = resource_klass.find_by_key(parent_key, context: context)
|
138
117
|
|
139
|
-
|
140
|
-
|
141
|
-
|
118
|
+
options = {
|
119
|
+
context: context,
|
120
|
+
sort_criteria: sort_criteria,
|
121
|
+
paginator: paginator,
|
122
|
+
fields: fields,
|
123
|
+
include_directives: include_directives
|
124
|
+
}
|
125
|
+
|
126
|
+
resource_tree = find_related_resource_tree(
|
127
|
+
parent_resource,
|
128
|
+
relationship_type,
|
129
|
+
options,
|
130
|
+
nil
|
131
|
+
)
|
132
|
+
|
133
|
+
JSONAPI::RelationshipOperationResult.new(:ok,
|
134
|
+
parent_resource,
|
135
|
+
resource_klass._relationship(relationship_type),
|
136
|
+
resource_tree.fragments.keys,
|
137
|
+
result_options)
|
142
138
|
end
|
143
139
|
|
144
140
|
def show_related_resource
|
141
|
+
include_directives = params[:include_directives]
|
145
142
|
source_klass = params[:source_klass]
|
146
143
|
source_id = params[:source_id]
|
147
|
-
relationship_type = params[:relationship_type]
|
144
|
+
relationship_type = params[:relationship_type]
|
145
|
+
serializer = params[:serializer]
|
148
146
|
fields = params[:fields]
|
149
147
|
|
150
|
-
|
148
|
+
options = {
|
149
|
+
context: context,
|
150
|
+
fields: fields,
|
151
|
+
filters: {},
|
152
|
+
include_directives: include_directives
|
153
|
+
}
|
154
|
+
|
151
155
|
source_resource = source_klass.find_by_key(source_id, context: context, fields: fields)
|
152
156
|
|
153
|
-
|
157
|
+
resource_set = find_related_resource_set(source_resource,
|
158
|
+
relationship_type,
|
159
|
+
include_directives,
|
160
|
+
options)
|
161
|
+
|
162
|
+
resource_set.populate!(serializer, context, options)
|
154
163
|
|
155
|
-
|
164
|
+
JSONAPI::ResourceSetOperationResult.new(:ok, resource_set, result_options)
|
156
165
|
end
|
157
166
|
|
158
167
|
def show_related_resources
|
@@ -164,11 +173,12 @@ module JSONAPI
|
|
164
173
|
paginator = params[:paginator]
|
165
174
|
fields = params[:fields]
|
166
175
|
include_directives = params[:include_directives]
|
176
|
+
serializer = params[:serializer]
|
167
177
|
|
168
|
-
|
178
|
+
verified_filters = resource_klass.verify_filters(filters, context)
|
169
179
|
|
170
|
-
|
171
|
-
filters:
|
180
|
+
options = {
|
181
|
+
filters: verified_filters,
|
172
182
|
sort_criteria: sort_criteria,
|
173
183
|
paginator: paginator,
|
174
184
|
fields: fields,
|
@@ -176,62 +186,66 @@ module JSONAPI
|
|
176
186
|
include_directives: include_directives
|
177
187
|
}
|
178
188
|
|
179
|
-
|
180
|
-
if params[:cache_serializer]
|
181
|
-
# TODO Could also avoid instantiating source_resource as actual Resource by
|
182
|
-
# allowing LinkBuilder to accept CachedResourceFragment as source in
|
183
|
-
# relationships_related_link
|
184
|
-
scope = source_resource.public_send(:"records_for_#{relationship_type}", rel_opts)
|
185
|
-
relationship = source_klass._relationship(relationship_type)
|
186
|
-
related_resources = relationship.resource_klass.find_serialized_with_caching(
|
187
|
-
scope,
|
188
|
-
params[:cache_serializer],
|
189
|
-
rel_opts
|
190
|
-
)
|
191
|
-
else
|
192
|
-
related_resources = source_resource.public_send(relationship_type, rel_opts)
|
193
|
-
end
|
189
|
+
source_resource = source_klass.find_by_key(source_id, context: context, fields: fields)
|
194
190
|
|
191
|
+
resource_set = find_related_resource_set(source_resource,
|
192
|
+
relationship_type,
|
193
|
+
include_directives,
|
194
|
+
options)
|
195
|
+
|
196
|
+
resource_set.populate!(serializer, context, options)
|
197
|
+
|
198
|
+
opts = result_options
|
195
199
|
if ((JSONAPI.configuration.top_level_meta_include_record_count) ||
|
196
|
-
|
197
|
-
|
198
|
-
related_resource_records = source_resource.public_send("records_for_" + relationship_type)
|
199
|
-
records = resource_klass.filter_records(filters, {},
|
200
|
-
related_resource_records)
|
200
|
+
(paginator && paginator.class.requires_record_count) ||
|
201
|
+
(JSONAPI.configuration.top_level_meta_include_page_count))
|
201
202
|
|
202
|
-
record_count =
|
203
|
+
opts[:record_count] = source_resource.class.count_related(
|
204
|
+
source_resource,
|
205
|
+
relationship_type,
|
206
|
+
options)
|
203
207
|
end
|
204
208
|
|
205
|
-
if (JSONAPI.configuration.top_level_meta_include_page_count && record_count)
|
206
|
-
page_count = paginator.calculate_page_count(record_count)
|
209
|
+
if (JSONAPI.configuration.top_level_meta_include_page_count && opts[:record_count])
|
210
|
+
opts[:page_count] = paginator.calculate_page_count(opts[:record_count])
|
207
211
|
end
|
208
212
|
|
209
|
-
pagination_params = if paginator && JSONAPI.configuration.top_level_links_include_pagination
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
return JSONAPI::RelatedResourcesOperationResult.new(:ok,
|
223
|
-
source_resource,
|
224
|
-
relationship_type,
|
225
|
-
related_resources,
|
226
|
-
opts)
|
213
|
+
opts[:pagination_params] = if paginator && JSONAPI.configuration.top_level_links_include_pagination
|
214
|
+
page_options = {}
|
215
|
+
page_options[:record_count] = opts[:record_count] if paginator.class.requires_record_count
|
216
|
+
paginator.links_page_params(page_options.merge(fetched_resources: resource_set))
|
217
|
+
else
|
218
|
+
{}
|
219
|
+
end
|
220
|
+
|
221
|
+
JSONAPI::RelatedResourcesSetOperationResult.new(:ok,
|
222
|
+
source_resource,
|
223
|
+
relationship_type,
|
224
|
+
resource_set,
|
225
|
+
opts)
|
227
226
|
end
|
228
227
|
|
229
228
|
def create_resource
|
229
|
+
include_directives = params[:include_directives]
|
230
|
+
fields = params[:fields]
|
231
|
+
serializer = params[:serializer]
|
232
|
+
|
230
233
|
data = params[:data]
|
231
234
|
resource = resource_klass.create(context)
|
232
235
|
result = resource.replace_fields(data)
|
233
236
|
|
234
|
-
|
237
|
+
options = {
|
238
|
+
context: context,
|
239
|
+
fields: fields,
|
240
|
+
filters: { resource_klass._primary_key => resource.id },
|
241
|
+
include_directives: include_directives
|
242
|
+
}
|
243
|
+
|
244
|
+
resource_set = find_resource_set(include_directives, options)
|
245
|
+
|
246
|
+
resource_set.populate!(serializer, context, options)
|
247
|
+
|
248
|
+
JSONAPI::ResourceSetOperationResult.new((result == :completed ? :created : :accepted), resource_set, result_options)
|
235
249
|
end
|
236
250
|
|
237
251
|
def remove_resource
|
@@ -240,17 +254,33 @@ module JSONAPI
|
|
240
254
|
resource = resource_klass.find_by_key(resource_id, context: context)
|
241
255
|
result = resource.remove
|
242
256
|
|
243
|
-
|
257
|
+
JSONAPI::OperationResult.new(result == :completed ? :no_content : :accepted, result_options)
|
244
258
|
end
|
245
259
|
|
246
260
|
def replace_fields
|
247
261
|
resource_id = params[:resource_id]
|
262
|
+
include_directives = params[:include_directives]
|
263
|
+
fields = params[:fields]
|
264
|
+
serializer = params[:serializer]
|
265
|
+
|
248
266
|
data = params[:data]
|
249
267
|
|
250
268
|
resource = resource_klass.find_by_key(resource_id, context: context)
|
269
|
+
|
251
270
|
result = resource.replace_fields(data)
|
252
271
|
|
253
|
-
|
272
|
+
options = {
|
273
|
+
context: context,
|
274
|
+
fields: fields,
|
275
|
+
filters: { resource_klass._primary_key => resource.id },
|
276
|
+
include_directives: include_directives
|
277
|
+
}
|
278
|
+
|
279
|
+
resource_set = find_resource_set(include_directives, options)
|
280
|
+
|
281
|
+
resource_set.populate!(serializer, context, options)
|
282
|
+
|
283
|
+
JSONAPI::ResourceSetOperationResult.new((result == :completed ? :ok : :accepted), resource_set, result_options)
|
254
284
|
end
|
255
285
|
|
256
286
|
def replace_to_one_relationship
|
@@ -261,7 +291,7 @@ module JSONAPI
|
|
261
291
|
resource = resource_klass.find_by_key(resource_id, context: context)
|
262
292
|
result = resource.replace_to_one_link(relationship_type, key_value)
|
263
293
|
|
264
|
-
|
294
|
+
JSONAPI::OperationResult.new(result == :completed ? :no_content : :accepted, result_options)
|
265
295
|
end
|
266
296
|
|
267
297
|
def replace_polymorphic_to_one_relationship
|
@@ -273,7 +303,7 @@ module JSONAPI
|
|
273
303
|
resource = resource_klass.find_by_key(resource_id, context: context)
|
274
304
|
result = resource.replace_polymorphic_to_one_link(relationship_type, key_value, key_type)
|
275
305
|
|
276
|
-
|
306
|
+
JSONAPI::OperationResult.new(result == :completed ? :no_content : :accepted, result_options)
|
277
307
|
end
|
278
308
|
|
279
309
|
def create_to_many_relationships
|
@@ -284,7 +314,7 @@ module JSONAPI
|
|
284
314
|
resource = resource_klass.find_by_key(resource_id, context: context)
|
285
315
|
result = resource.create_to_many_links(relationship_type, data)
|
286
316
|
|
287
|
-
|
317
|
+
JSONAPI::OperationResult.new(result == :completed ? :no_content : :accepted, result_options)
|
288
318
|
end
|
289
319
|
|
290
320
|
def replace_to_many_relationships
|
@@ -295,7 +325,7 @@ module JSONAPI
|
|
295
325
|
resource = resource_klass.find_by_key(resource_id, context: context)
|
296
326
|
result = resource.replace_to_many_links(relationship_type, data)
|
297
327
|
|
298
|
-
|
328
|
+
JSONAPI::OperationResult.new(result == :completed ? :no_content : :accepted, result_options)
|
299
329
|
end
|
300
330
|
|
301
331
|
def remove_to_many_relationships
|
@@ -312,7 +342,7 @@ module JSONAPI
|
|
312
342
|
complete = false
|
313
343
|
end
|
314
344
|
end
|
315
|
-
|
345
|
+
JSONAPI::OperationResult.new(complete ? :no_content : :accepted, result_options)
|
316
346
|
end
|
317
347
|
|
318
348
|
def remove_to_one_relationship
|
@@ -322,7 +352,55 @@ module JSONAPI
|
|
322
352
|
resource = resource_klass.find_by_key(resource_id, context: context)
|
323
353
|
result = resource.remove_to_one_link(relationship_type)
|
324
354
|
|
325
|
-
|
355
|
+
JSONAPI::OperationResult.new(result == :completed ? :no_content : :accepted, result_options)
|
356
|
+
end
|
357
|
+
|
358
|
+
def result_options
|
359
|
+
options = {}
|
360
|
+
options[:warnings] = params[:warnings] if params[:warnings]
|
361
|
+
options
|
362
|
+
end
|
363
|
+
|
364
|
+
def find_resource_set(include_directives, options)
|
365
|
+
include_related = include_directives[:include_related] if include_directives
|
366
|
+
|
367
|
+
resource_tree = find_resource_tree(options, include_related)
|
368
|
+
|
369
|
+
JSONAPI::ResourceSet.new(resource_tree)
|
370
|
+
end
|
371
|
+
|
372
|
+
def find_related_resource_set(resource, relationship_name, include_directives, options)
|
373
|
+
include_related = include_directives[:include_related] if include_directives
|
374
|
+
|
375
|
+
resource_tree = find_resource_tree_from_relationship(resource, relationship_name, options, include_related)
|
376
|
+
|
377
|
+
JSONAPI::ResourceSet.new(resource_tree)
|
378
|
+
end
|
379
|
+
|
380
|
+
def find_resource_tree(options, include_related)
|
381
|
+
options[:cache] = resource_klass.caching?
|
382
|
+
|
383
|
+
fragments = resource_klass.find_fragments(options[:filters], options)
|
384
|
+
PrimaryResourceTree.new(fragments: fragments, include_related: include_related, options: options)
|
385
|
+
end
|
386
|
+
|
387
|
+
def find_related_resource_tree(parent_resource, relationship_name, options, include_related)
|
388
|
+
options = options.except(:include_directives)
|
389
|
+
options[:cache] = resource_klass.caching?
|
390
|
+
|
391
|
+
fragments = resource_klass.find_included_fragments([parent_resource], relationship_name, options)
|
392
|
+
PrimaryResourceTree.new(fragments: fragments, include_related: include_related, options: options)
|
393
|
+
end
|
394
|
+
|
395
|
+
def find_resource_tree_from_relationship(resource, relationship_name, options, include_related)
|
396
|
+
relationship = resource.class._relationship(relationship_name)
|
397
|
+
|
398
|
+
options = options.except(:include_directives)
|
399
|
+
options[:cache] = relationship.resource_klass.caching?
|
400
|
+
|
401
|
+
fragments = resource.class.find_related_fragments([resource], relationship_name, options)
|
402
|
+
|
403
|
+
PrimaryResourceTree.new(fragments: fragments, include_related: include_related, options: options)
|
326
404
|
end
|
327
405
|
end
|
328
406
|
end
|
data/lib/jsonapi/relationship.rb
CHANGED
@@ -1,8 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative 'compatibility_helper'
|
1
3
|
module JSONAPI
|
2
4
|
class Relationship
|
3
5
|
attr_reader :acts_as_set, :foreign_key, :options, :name,
|
4
|
-
:class_name, :polymorphic, :
|
5
|
-
:parent_resource, :eager_load_on_include
|
6
|
+
:class_name, :polymorphic, :always_include_optional_linkage_data,
|
7
|
+
:parent_resource, :eager_load_on_include, :custom_methods,
|
8
|
+
:inverse_relationship, :allow_include
|
9
|
+
|
10
|
+
attr_writer :allow_include
|
11
|
+
|
12
|
+
attr_accessor :_routed, :_warned_missing_route
|
6
13
|
|
7
14
|
def initialize(name, options = {})
|
8
15
|
@name = name.to_s
|
@@ -12,22 +19,66 @@ module JSONAPI
|
|
12
19
|
@parent_resource = options[:parent_resource]
|
13
20
|
@relation_name = options.fetch(:relation_name, @name)
|
14
21
|
@polymorphic = options.fetch(:polymorphic, false) == true
|
15
|
-
@
|
22
|
+
@polymorphic_types = options[:polymorphic_types]
|
23
|
+
if options[:polymorphic_relations]
|
24
|
+
JSONAPI::CompatibilityHelper.deprecation_warn('Use polymorphic_types instead of polymorphic_relations')
|
25
|
+
@polymorphic_types ||= options[:polymorphic_relations]
|
26
|
+
end
|
27
|
+
|
28
|
+
@always_include_optional_linkage_data = options.fetch(:always_include_optional_linkage_data, false) == true
|
16
29
|
@eager_load_on_include = options.fetch(:eager_load_on_include, true) == true
|
30
|
+
@allow_include = options[:allow_include]
|
31
|
+
@class_name = nil
|
32
|
+
@inverse_relationship = nil
|
33
|
+
|
34
|
+
@_routed = false
|
35
|
+
@_warned_missing_route = false
|
36
|
+
|
37
|
+
exclude_links(options.fetch(:exclude_links, JSONAPI.configuration.default_exclude_links))
|
38
|
+
|
39
|
+
# Custom methods are reserved for future use
|
40
|
+
@custom_methods = options.fetch(:custom_methods, {})
|
17
41
|
end
|
18
42
|
|
19
43
|
alias_method :polymorphic?, :polymorphic
|
44
|
+
alias_method :parent_resource_klass, :parent_resource
|
20
45
|
|
21
46
|
def primary_key
|
47
|
+
# :nocov:
|
22
48
|
@primary_key ||= resource_klass._primary_key
|
49
|
+
# :nocov:
|
23
50
|
end
|
24
51
|
|
25
52
|
def resource_klass
|
26
|
-
@resource_klass ||= @parent_resource.
|
53
|
+
@resource_klass ||= @parent_resource.resource_klass_for(@class_name)
|
27
54
|
end
|
28
55
|
|
29
56
|
def table_name
|
57
|
+
# :nocov:
|
30
58
|
@table_name ||= resource_klass._table_name
|
59
|
+
# :nocov:
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.polymorphic_types(name)
|
63
|
+
@poly_hash ||= {}.tap do |hash|
|
64
|
+
ObjectSpace.each_object do |klass|
|
65
|
+
next unless Module === klass
|
66
|
+
if ActiveRecord::Base > klass
|
67
|
+
klass.reflect_on_all_associations(:has_many).select{|r| r.options[:as] }.each do |reflection|
|
68
|
+
(hash[reflection.options[:as]] ||= []) << klass.name.downcase
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
@poly_hash[name.to_sym]
|
74
|
+
end
|
75
|
+
|
76
|
+
def resource_types
|
77
|
+
if polymorphic? && belongs_to?
|
78
|
+
@polymorphic_types ||= self.class.polymorphic_types(@relation_name).collect {|t| t.pluralize}
|
79
|
+
else
|
80
|
+
[resource_klass._type.to_s.pluralize]
|
81
|
+
end
|
31
82
|
end
|
32
83
|
|
33
84
|
def type
|
@@ -47,17 +98,35 @@ module JSONAPI
|
|
47
98
|
end
|
48
99
|
end
|
49
100
|
|
50
|
-
def
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
101
|
+
def belongs_to?
|
102
|
+
# :nocov:
|
103
|
+
false
|
104
|
+
# :nocov:
|
105
|
+
end
|
106
|
+
|
107
|
+
def readonly?
|
108
|
+
@options[:readonly]
|
109
|
+
end
|
110
|
+
|
111
|
+
def exclude_links(exclude)
|
112
|
+
case exclude
|
113
|
+
when :default, "default"
|
114
|
+
@_exclude_links = [:self, :related]
|
115
|
+
when :none, "none"
|
116
|
+
@_exclude_links = []
|
117
|
+
when Array
|
118
|
+
@_exclude_links = exclude.collect {|link| link.to_sym}
|
119
|
+
else
|
120
|
+
fail "Invalid exclude_links"
|
56
121
|
end
|
57
122
|
end
|
58
123
|
|
59
|
-
def
|
60
|
-
|
124
|
+
def _exclude_links
|
125
|
+
@_exclude_links ||= []
|
126
|
+
end
|
127
|
+
|
128
|
+
def exclude_link?(link)
|
129
|
+
_exclude_links.include?(link.to_sym)
|
61
130
|
end
|
62
131
|
|
63
132
|
class ToOne < Relationship
|
@@ -68,27 +137,89 @@ module JSONAPI
|
|
68
137
|
@class_name = options.fetch(:class_name, name.to_s.camelize)
|
69
138
|
@foreign_key ||= "#{name}_id".to_sym
|
70
139
|
@foreign_key_on = options.fetch(:foreign_key_on, :self)
|
140
|
+
if parent_resource
|
141
|
+
@inverse_relationship = options.fetch(:inverse_relationship, parent_resource._type)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def to_s
|
146
|
+
# :nocov: useful for debugging
|
147
|
+
"#{parent_resource}.#{name}(#{belongs_to? ? 'BelongsToOne' : 'ToOne'})"
|
148
|
+
# :nocov:
|
71
149
|
end
|
72
150
|
|
73
151
|
def belongs_to?
|
152
|
+
# :nocov:
|
74
153
|
foreign_key_on == :self
|
154
|
+
# :nocov:
|
75
155
|
end
|
76
156
|
|
77
157
|
def polymorphic_type
|
78
158
|
"#{name}_type" if polymorphic?
|
79
159
|
end
|
160
|
+
|
161
|
+
def include_optional_linkage_data?
|
162
|
+
@always_include_optional_linkage_data || JSONAPI::configuration.always_include_to_one_linkage_data
|
163
|
+
end
|
164
|
+
|
165
|
+
def allow_include?(context = nil)
|
166
|
+
strategy = if @allow_include.nil?
|
167
|
+
JSONAPI.configuration.default_allow_include_to_one
|
168
|
+
else
|
169
|
+
@allow_include
|
170
|
+
end
|
171
|
+
|
172
|
+
if !!strategy == strategy #check for boolean
|
173
|
+
return strategy
|
174
|
+
elsif strategy.is_a?(Symbol) || strategy.is_a?(String)
|
175
|
+
parent_resource.send(strategy, context)
|
176
|
+
else
|
177
|
+
strategy.call(context)
|
178
|
+
end
|
179
|
+
end
|
80
180
|
end
|
81
181
|
|
82
182
|
class ToMany < Relationship
|
83
|
-
attr_reader :reflect
|
183
|
+
attr_reader :reflect
|
84
184
|
|
85
185
|
def initialize(name, options = {})
|
86
186
|
super
|
87
187
|
@class_name = options.fetch(:class_name, name.to_s.camelize.singularize)
|
88
188
|
@foreign_key ||= "#{name.to_s.singularize}_ids".to_sym
|
89
189
|
@reflect = options.fetch(:reflect, true) == true
|
90
|
-
|
190
|
+
if parent_resource
|
191
|
+
@inverse_relationship = options.fetch(:inverse_relationship, parent_resource._type.to_s.singularize.to_sym)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def to_s
|
196
|
+
# :nocov: useful for debugging
|
197
|
+
"#{parent_resource}.#{name}(ToMany)"
|
198
|
+
# :nocov:
|
199
|
+
end
|
200
|
+
|
201
|
+
def include_optional_linkage_data?
|
202
|
+
# :nocov:
|
203
|
+
@always_include_optional_linkage_data || JSONAPI::configuration.always_include_to_many_linkage_data
|
204
|
+
# :nocov:
|
91
205
|
end
|
206
|
+
|
207
|
+
def allow_include?(context = nil)
|
208
|
+
strategy = if @allow_include.nil?
|
209
|
+
JSONAPI.configuration.default_allow_include_to_many
|
210
|
+
else
|
211
|
+
@allow_include
|
212
|
+
end
|
213
|
+
|
214
|
+
if !!strategy == strategy #check for boolean
|
215
|
+
return strategy
|
216
|
+
elsif strategy.is_a?(Symbol) || strategy.is_a?(String)
|
217
|
+
parent_resource.send(strategy, context)
|
218
|
+
else
|
219
|
+
strategy.call(context)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
92
223
|
end
|
93
224
|
end
|
94
225
|
end
|