jsonapi-resources 0.4.2 → 0.4.3
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/Gemfile +2 -2
- data/README.md +103 -71
- data/Rakefile +2 -2
- data/jsonapi-resources.gemspec +2 -2
- data/lib/jsonapi-resources.rb +0 -1
- data/lib/jsonapi/active_record_operations_processor.rb +10 -2
- data/lib/jsonapi/acts_as_resource_controller.rb +26 -24
- data/lib/jsonapi/association.rb +50 -15
- data/lib/jsonapi/callbacks.rb +1 -2
- data/lib/jsonapi/configuration.rb +8 -24
- data/lib/jsonapi/error.rb +1 -2
- data/lib/jsonapi/error_codes.rb +3 -1
- data/lib/jsonapi/exceptions.rb +59 -47
- data/lib/jsonapi/include_directives.rb +11 -11
- data/lib/jsonapi/mime_types.rb +2 -2
- data/lib/jsonapi/operation.rb +28 -11
- data/lib/jsonapi/operations_processor.rb +16 -5
- data/lib/jsonapi/paginator.rb +19 -19
- data/lib/jsonapi/request.rb +175 -196
- data/lib/jsonapi/resource.rb +158 -105
- data/lib/jsonapi/resource_serializer.rb +37 -26
- data/lib/jsonapi/resources/version.rb +2 -2
- data/lib/jsonapi/response_document.rb +5 -4
- data/lib/jsonapi/routing_ext.rb +24 -19
- data/test/controllers/controller_test.rb +261 -31
- data/test/fixtures/active_record.rb +206 -8
- data/test/fixtures/book_comments.yml +2 -1
- data/test/fixtures/books.yml +1 -0
- data/test/fixtures/documents.yml +3 -0
- data/test/fixtures/people.yml +8 -1
- data/test/fixtures/pictures.yml +15 -0
- data/test/fixtures/products.yml +3 -0
- data/test/fixtures/vehicles.yml +8 -0
- data/test/helpers/{hash_helpers.rb → assertions.rb} +6 -1
- data/test/integration/requests/request_test.rb +14 -3
- data/test/integration/routes/routes_test.rb +47 -0
- data/test/test_helper.rb +27 -4
- data/test/unit/serializer/include_directives_test.rb +5 -0
- data/test/unit/serializer/polymorphic_serializer_test.rb +384 -0
- data/test/unit/serializer/serializer_test.rb +19 -1
- metadata +14 -4
data/lib/jsonapi/resource.rb
CHANGED
@@ -6,7 +6,7 @@ module JSONAPI
|
|
6
6
|
|
7
7
|
@@resource_types = {}
|
8
8
|
|
9
|
-
|
9
|
+
attr_reader :context
|
10
10
|
attr_reader :model
|
11
11
|
|
12
12
|
define_jsonapi_resources_callbacks :create,
|
@@ -17,6 +17,7 @@ module JSONAPI
|
|
17
17
|
:replace_has_many_links,
|
18
18
|
:create_has_one_link,
|
19
19
|
:replace_has_one_link,
|
20
|
+
:replace_polymorphic_has_one_link,
|
20
21
|
:remove_has_many_link,
|
21
22
|
:remove_has_one_link,
|
22
23
|
:replace_fields
|
@@ -48,9 +49,7 @@ module JSONAPI
|
|
48
49
|
completed = (yield == :completed)
|
49
50
|
end
|
50
51
|
|
51
|
-
if @save_needed || is_new?
|
52
|
-
completed = (save == :completed)
|
53
|
-
end
|
52
|
+
completed = (save == :completed) if @save_needed || is_new?
|
54
53
|
end
|
55
54
|
end
|
56
55
|
|
@@ -81,6 +80,12 @@ module JSONAPI
|
|
81
80
|
end
|
82
81
|
end
|
83
82
|
|
83
|
+
def replace_polymorphic_has_one_link(association_type, association_key_value, association_key_type)
|
84
|
+
change :replace_polymorphic_has_one_link do
|
85
|
+
_replace_polymorphic_has_one_link(association_type, association_key_value, association_key_type)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
84
89
|
def remove_has_many_link(association_type, key)
|
85
90
|
change :remove_has_many_link do
|
86
91
|
_remove_has_many_link(association_type, key)
|
@@ -106,11 +111,12 @@ module JSONAPI
|
|
106
111
|
|
107
112
|
# Override this on a resource to customize how the associated records
|
108
113
|
# are fetched for a model. Particularly helpful for authorization.
|
109
|
-
def records_for(association_name,
|
114
|
+
def records_for(association_name, _options = {})
|
110
115
|
model.send association_name
|
111
116
|
end
|
112
117
|
|
113
118
|
private
|
119
|
+
|
114
120
|
def save
|
115
121
|
run_callbacks :save do
|
116
122
|
_save
|
@@ -132,45 +138,43 @@ module JSONAPI
|
|
132
138
|
# ```
|
133
139
|
def _save
|
134
140
|
unless @model.valid?
|
135
|
-
|
141
|
+
fail JSONAPI::Exceptions::ValidationErrors.new(@model.errors.messages)
|
136
142
|
end
|
137
143
|
|
138
144
|
if defined? @model.save
|
139
145
|
saved = @model.save
|
140
|
-
unless saved
|
141
|
-
raise JSONAPI::Exceptions::SaveFailed.new
|
142
|
-
end
|
146
|
+
fail JSONAPI::Exceptions::SaveFailed.new unless saved
|
143
147
|
else
|
144
148
|
saved = true
|
145
149
|
end
|
146
150
|
|
147
151
|
@save_needed = !saved
|
148
152
|
|
149
|
-
|
153
|
+
:completed
|
150
154
|
end
|
151
155
|
|
152
156
|
def _remove
|
153
157
|
@model.destroy
|
154
158
|
|
155
|
-
|
159
|
+
:completed
|
156
160
|
end
|
157
161
|
|
158
162
|
def _create_has_many_links(association_type, association_key_values)
|
159
163
|
association = self.class._associations[association_type]
|
160
164
|
|
161
165
|
association_key_values.each do |association_key_value|
|
162
|
-
related_resource =
|
166
|
+
related_resource = association.resource_klass.find_by_key(association_key_value, context: @context)
|
163
167
|
|
164
|
-
#
|
168
|
+
# TODO: Add option to skip relations that already exist instead of returning an error?
|
165
169
|
relation = @model.send(association.type).where(association.primary_key => association_key_value).first
|
166
170
|
if relation.nil?
|
167
171
|
@model.send(association.type) << related_resource.model
|
168
172
|
else
|
169
|
-
|
173
|
+
fail JSONAPI::Exceptions::HasManyRelationExists.new(association_key_value)
|
170
174
|
end
|
171
175
|
end
|
172
176
|
|
173
|
-
|
177
|
+
:completed
|
174
178
|
end
|
175
179
|
|
176
180
|
def _replace_has_many_links(association_type, association_key_values)
|
@@ -179,7 +183,7 @@ module JSONAPI
|
|
179
183
|
send("#{association.foreign_key}=", association_key_values)
|
180
184
|
@save_needed = true
|
181
185
|
|
182
|
-
|
186
|
+
:completed
|
183
187
|
end
|
184
188
|
|
185
189
|
def _replace_has_one_link(association_type, association_key_value)
|
@@ -188,7 +192,18 @@ module JSONAPI
|
|
188
192
|
send("#{association.foreign_key}=", association_key_value)
|
189
193
|
@save_needed = true
|
190
194
|
|
191
|
-
|
195
|
+
:completed
|
196
|
+
end
|
197
|
+
|
198
|
+
def _replace_polymorphic_has_one_link(association_type, key_value, key_type)
|
199
|
+
association = self.class._associations[association_type.to_sym]
|
200
|
+
|
201
|
+
model.send("#{association.foreign_key}=", key_value)
|
202
|
+
model.send("#{association.polymorphic_type}=", key_type.to_s.classify)
|
203
|
+
|
204
|
+
@save_needed = true
|
205
|
+
|
206
|
+
:completed
|
192
207
|
end
|
193
208
|
|
194
209
|
def _remove_has_many_link(association_type, key)
|
@@ -196,7 +211,7 @@ module JSONAPI
|
|
196
211
|
|
197
212
|
@model.send(association.type).delete(key)
|
198
213
|
|
199
|
-
|
214
|
+
:completed
|
200
215
|
end
|
201
216
|
|
202
217
|
def _remove_has_one_link(association_type)
|
@@ -205,7 +220,7 @@ module JSONAPI
|
|
205
220
|
send("#{association.foreign_key}=", nil)
|
206
221
|
@save_needed = true
|
207
222
|
|
208
|
-
|
223
|
+
:completed
|
209
224
|
end
|
210
225
|
|
211
226
|
def _replace_fields(field_data)
|
@@ -224,7 +239,12 @@ module JSONAPI
|
|
224
239
|
if value.nil?
|
225
240
|
remove_has_one_link(association_type)
|
226
241
|
else
|
227
|
-
|
242
|
+
case value
|
243
|
+
when Hash
|
244
|
+
replace_polymorphic_has_one_link(association_type.to_s, value.fetch(:id), value.fetch(:type))
|
245
|
+
else
|
246
|
+
replace_has_one_link(association_type, value)
|
247
|
+
end
|
228
248
|
end
|
229
249
|
end if field_data[:has_one]
|
230
250
|
|
@@ -232,7 +252,7 @@ module JSONAPI
|
|
232
252
|
replace_has_many_links(association_type, values)
|
233
253
|
end if field_data[:has_many]
|
234
254
|
|
235
|
-
|
255
|
+
:completed
|
236
256
|
end
|
237
257
|
|
238
258
|
class << self
|
@@ -253,15 +273,15 @@ module JSONAPI
|
|
253
273
|
resource_name = JSONAPI::Resource._resource_name_from_type(type)
|
254
274
|
resource = resource_name.safe_constantize if resource_name
|
255
275
|
if resource.nil?
|
256
|
-
|
276
|
+
fail NameError, "JSONAPI: Could not find resource '#{type}'. (Class #{resource_name} not found)"
|
257
277
|
end
|
258
278
|
resource
|
259
279
|
end
|
260
280
|
|
261
|
-
attr_accessor :_attributes, :_associations, :_allowed_filters
|
281
|
+
attr_accessor :_attributes, :_associations, :_allowed_filters, :_type, :_paginator
|
262
282
|
|
263
283
|
def create(context)
|
264
|
-
|
284
|
+
new(create_model, context)
|
265
285
|
end
|
266
286
|
|
267
287
|
def create_model
|
@@ -303,7 +323,7 @@ module JSONAPI
|
|
303
323
|
end
|
304
324
|
|
305
325
|
def default_attribute_options
|
306
|
-
{format: :default}
|
326
|
+
{ format: :default }
|
307
327
|
end
|
308
328
|
|
309
329
|
def has_one(*attrs)
|
@@ -319,7 +339,7 @@ module JSONAPI
|
|
319
339
|
end
|
320
340
|
|
321
341
|
def filters(*attrs)
|
322
|
-
@_allowed_filters.merge!(attrs.inject(
|
342
|
+
@_allowed_filters.merge!(attrs.inject({}) { |h, attr| h[attr] = {}; h })
|
323
343
|
end
|
324
344
|
|
325
345
|
def filter(attr, *args)
|
@@ -334,11 +354,11 @@ module JSONAPI
|
|
334
354
|
# :nocov:
|
335
355
|
def method_missing(method, *args)
|
336
356
|
if method.to_s.match /createable_fields/
|
337
|
-
ActiveSupport::Deprecation.warn(
|
338
|
-
|
357
|
+
ActiveSupport::Deprecation.warn('`createable_fields` is deprecated, please use `creatable_fields` instead')
|
358
|
+
creatable_fields(*args)
|
339
359
|
elsif method.to_s.match /updateable_fields/
|
340
|
-
ActiveSupport::Deprecation.warn(
|
341
|
-
|
360
|
+
ActiveSupport::Deprecation.warn('`updateable_fields` is deprecated, please use `updatable_fields` instead')
|
361
|
+
updatable_fields(*args)
|
342
362
|
else
|
343
363
|
super
|
344
364
|
end
|
@@ -346,17 +366,17 @@ module JSONAPI
|
|
346
366
|
# :nocov:
|
347
367
|
|
348
368
|
# Override in your resource to filter the updatable keys
|
349
|
-
def updatable_fields(
|
369
|
+
def updatable_fields(_context = nil)
|
350
370
|
_updatable_associations | _attributes.keys - [:id]
|
351
371
|
end
|
352
372
|
|
353
373
|
# Override in your resource to filter the creatable keys
|
354
|
-
def creatable_fields(
|
374
|
+
def creatable_fields(_context = nil)
|
355
375
|
_updatable_associations | _attributes.keys
|
356
376
|
end
|
357
377
|
|
358
378
|
# Override in your resource to filter the sortable keys
|
359
|
-
def sortable_fields(
|
379
|
+
def sortable_fields(_context = nil)
|
360
380
|
_attributes.keys
|
361
381
|
end
|
362
382
|
|
@@ -364,15 +384,38 @@ module JSONAPI
|
|
364
384
|
_associations.keys | _attributes.keys
|
365
385
|
end
|
366
386
|
|
367
|
-
def
|
368
|
-
|
387
|
+
def resolve_association_names_to_relations(resource_klass, model_includes, options = {})
|
388
|
+
case model_includes
|
389
|
+
when Array
|
390
|
+
return model_includes.map do |value|
|
391
|
+
resolve_association_names_to_relations(resource_klass, value, options)
|
392
|
+
end
|
393
|
+
when Hash
|
394
|
+
model_includes.keys.each do |key|
|
395
|
+
association = resource_klass._associations[key]
|
396
|
+
value = model_includes[key]
|
397
|
+
model_includes.delete(key)
|
398
|
+
model_includes[association.relation_name(options)] = resolve_association_names_to_relations(association.resource_klass, value, options)
|
399
|
+
end
|
400
|
+
return model_includes
|
401
|
+
when Symbol
|
402
|
+
association = resource_klass._associations[model_includes]
|
403
|
+
return association.relation_name(options)
|
404
|
+
end
|
405
|
+
end
|
406
|
+
|
407
|
+
def apply_includes(records, options = {})
|
408
|
+
include_directives = options[:include_directives]
|
409
|
+
if include_directives
|
410
|
+
model_includes = resolve_association_names_to_relations(self, include_directives.model_includes, options)
|
411
|
+
records = records.includes(model_includes)
|
412
|
+
end
|
413
|
+
|
369
414
|
records
|
370
415
|
end
|
371
416
|
|
372
417
|
def apply_pagination(records, paginator, order_options)
|
373
|
-
if paginator
|
374
|
-
records = paginator.apply(records, order_options)
|
375
|
-
end
|
418
|
+
records = paginator.apply(records, order_options) if paginator
|
376
419
|
records
|
377
420
|
end
|
378
421
|
|
@@ -384,7 +427,7 @@ module JSONAPI
|
|
384
427
|
end
|
385
428
|
end
|
386
429
|
|
387
|
-
def apply_filter(records, filter, value,
|
430
|
+
def apply_filter(records, filter, value, _options = {})
|
388
431
|
records.where(filter => value)
|
389
432
|
end
|
390
433
|
|
@@ -395,7 +438,7 @@ module JSONAPI
|
|
395
438
|
filters.each do |filter, value|
|
396
439
|
if _associations.include?(filter)
|
397
440
|
if _associations[filter].is_a?(JSONAPI::Association::HasMany)
|
398
|
-
required_includes.push(filter)
|
441
|
+
required_includes.push(filter.to_s)
|
399
442
|
records = apply_filter(records, "#{filter}.#{_associations[filter].primary_key}", value, options)
|
400
443
|
else
|
401
444
|
records = apply_filter(records, "#{_associations[filter].foreign_key}", value, options)
|
@@ -407,20 +450,16 @@ module JSONAPI
|
|
407
450
|
end
|
408
451
|
|
409
452
|
if required_includes.any?
|
410
|
-
records.
|
411
|
-
elsif records.respond_to? :to_ary
|
412
|
-
records
|
413
|
-
else
|
414
|
-
records.all
|
453
|
+
records = apply_includes(records, options.merge(include_directives: IncludeDirectives.new(required_includes)))
|
415
454
|
end
|
455
|
+
|
456
|
+
records
|
416
457
|
end
|
417
458
|
|
418
459
|
def filter_records(filters, options)
|
419
|
-
include_directives = options[:include_directives]
|
420
|
-
|
421
460
|
records = records(options)
|
422
|
-
records =
|
423
|
-
|
461
|
+
records = apply_filters(records, filters, options)
|
462
|
+
apply_includes(records, options)
|
424
463
|
end
|
425
464
|
|
426
465
|
def sort_records(records, order_options)
|
@@ -445,27 +484,24 @@ module JSONAPI
|
|
445
484
|
|
446
485
|
resources = []
|
447
486
|
records.each do |model|
|
448
|
-
resources.push
|
487
|
+
resources.push new(model, context)
|
449
488
|
end
|
450
489
|
|
451
|
-
|
490
|
+
resources
|
452
491
|
end
|
453
492
|
|
454
493
|
def find_by_key(key, options = {})
|
455
494
|
context = options[:context]
|
456
|
-
include_directives = options[:include_directives]
|
457
495
|
records = records(options)
|
458
|
-
records = apply_includes(records,
|
496
|
+
records = apply_includes(records, options)
|
459
497
|
model = records.where({_primary_key => key}).first
|
460
|
-
if model.nil?
|
461
|
-
|
462
|
-
end
|
463
|
-
self.new(model, context)
|
498
|
+
fail JSONAPI::Exceptions::RecordNotFound.new(key) if model.nil?
|
499
|
+
new(model, context)
|
464
500
|
end
|
465
501
|
|
466
502
|
# Override this method if you want to customize the relation for
|
467
503
|
# finder methods (find, find_by_key)
|
468
|
-
def records(
|
504
|
+
def records(_options = {})
|
469
505
|
_model_class
|
470
506
|
end
|
471
507
|
|
@@ -494,7 +530,7 @@ module JSONAPI
|
|
494
530
|
end
|
495
531
|
|
496
532
|
# override to allow for key processing and checking
|
497
|
-
def verify_key(key,
|
533
|
+
def verify_key(key, _context = nil)
|
498
534
|
key && Integer(key)
|
499
535
|
rescue
|
500
536
|
raise JSONAPI::Exceptions::InvalidFieldValue.new(:id, key)
|
@@ -508,13 +544,13 @@ module JSONAPI
|
|
508
544
|
end
|
509
545
|
|
510
546
|
# override to allow for custom filters
|
511
|
-
def verify_custom_filter(filter, value,
|
512
|
-
|
547
|
+
def verify_custom_filter(filter, value, _context = nil)
|
548
|
+
[filter, value]
|
513
549
|
end
|
514
550
|
|
515
551
|
# override to allow for custom association logic, such as uuids, multiple keys or permission checks on keys
|
516
|
-
def verify_association_filter(filter, raw,
|
517
|
-
|
552
|
+
def verify_association_filter(filter, raw, _context = nil)
|
553
|
+
[filter, raw]
|
518
554
|
end
|
519
555
|
|
520
556
|
# quasi private class methods
|
@@ -523,12 +559,12 @@ module JSONAPI
|
|
523
559
|
end
|
524
560
|
|
525
561
|
def _updatable_associations
|
526
|
-
@_associations.map { |key,
|
562
|
+
@_associations.map { |key, _association| key }
|
527
563
|
end
|
528
564
|
|
529
565
|
def _has_association?(type)
|
530
566
|
type = type.to_s
|
531
|
-
@_associations.
|
567
|
+
@_associations.key?(type.singularize.to_sym) || @_associations.key?(type.pluralize.to_sym)
|
532
568
|
end
|
533
569
|
|
534
570
|
def _association(type)
|
@@ -537,7 +573,7 @@ module JSONAPI
|
|
537
573
|
end
|
538
574
|
|
539
575
|
def _model_name
|
540
|
-
@_model_name ||=
|
576
|
+
@_model_name ||= name.demodulize.sub(/Resource$/, '')
|
541
577
|
end
|
542
578
|
|
543
579
|
def _primary_key
|
@@ -549,13 +585,13 @@ module JSONAPI
|
|
549
585
|
end
|
550
586
|
|
551
587
|
def _allowed_filters
|
552
|
-
!@_allowed_filters.nil? ? @_allowed_filters : { :
|
588
|
+
!@_allowed_filters.nil? ? @_allowed_filters : { id: {} }
|
553
589
|
end
|
554
590
|
|
555
591
|
def _resource_name_from_type(type)
|
556
592
|
class_name = @@resource_types[type]
|
557
593
|
if class_name.nil?
|
558
|
-
class_name = "#{type.to_s.singularize}_resource".camelize
|
594
|
+
class_name = "#{type.to_s.underscore.singularize}_resource".camelize
|
559
595
|
@@resource_types[type] = class_name
|
560
596
|
end
|
561
597
|
return class_name
|
@@ -578,19 +614,20 @@ module JSONAPI
|
|
578
614
|
end
|
579
615
|
|
580
616
|
def module_path
|
581
|
-
@module_path ||=
|
617
|
+
@module_path ||= name =~ /::[^:]+\Z/ ? ($`.freeze.gsub('::', '/') + '/').downcase : ''
|
582
618
|
end
|
583
619
|
|
584
620
|
def construct_order_options(sort_params)
|
585
621
|
return {} unless sort_params
|
586
622
|
|
587
|
-
sort_params.each_with_object({})
|
623
|
+
sort_params.each_with_object({}) do |sort, order_hash|
|
588
624
|
field = sort[:field] == 'id' ? _primary_key : sort[:field]
|
589
625
|
order_hash[field] = sort[:direction]
|
590
|
-
|
626
|
+
end
|
591
627
|
end
|
592
628
|
|
593
629
|
private
|
630
|
+
|
594
631
|
def check_reserved_resource_name(type, name)
|
595
632
|
if [:ids, :types, :hrefs, :links].include?(type)
|
596
633
|
warn "[NAME COLLISION] `#{name}` is a reserved resource name."
|
@@ -618,63 +655,79 @@ module JSONAPI
|
|
618
655
|
|
619
656
|
attrs.each do |attr|
|
620
657
|
check_reserved_association_name(attr)
|
658
|
+
@_associations[attr] = association = klass.new(attr, options)
|
621
659
|
|
622
|
-
|
660
|
+
associated_records_method_name = case association
|
661
|
+
when JSONAPI::Association::HasOne then "record_for_#{attr}"
|
662
|
+
when JSONAPI::Association::HasMany then "records_for_#{attr}"
|
663
|
+
end
|
623
664
|
|
624
|
-
foreign_key =
|
625
|
-
|
626
|
-
define_method foreign_key do
|
627
|
-
@model.method(foreign_key).call
|
628
|
-
end unless method_defined?(foreign_key)
|
665
|
+
foreign_key = association.foreign_key
|
629
666
|
|
630
667
|
define_method "#{foreign_key}=" do |value|
|
631
668
|
@model.method("#{foreign_key}=").call(value)
|
632
669
|
end unless method_defined?("#{foreign_key}=")
|
633
670
|
|
634
|
-
associated_records_method_name =
|
635
|
-
|
636
|
-
|
637
|
-
end
|
638
|
-
|
639
|
-
define_method associated_records_method_name do |options={}|
|
640
|
-
records_for(attr, options)
|
671
|
+
define_method associated_records_method_name do |options = {}|
|
672
|
+
relation_name = association.relation_name(options.merge({context: @context}))
|
673
|
+
records_for(relation_name, options)
|
641
674
|
end unless method_defined?(associated_records_method_name)
|
642
675
|
|
643
|
-
if
|
644
|
-
define_method
|
645
|
-
|
646
|
-
|
647
|
-
|
676
|
+
if association.is_a?(JSONAPI::Association::HasOne)
|
677
|
+
define_method foreign_key do
|
678
|
+
@model.method(foreign_key).call
|
679
|
+
end unless method_defined?(foreign_key)
|
680
|
+
|
681
|
+
define_method attr do |options = {}|
|
682
|
+
if association.polymorphic?
|
648
683
|
associated_model = public_send(associated_records_method_name)
|
649
|
-
|
684
|
+
resource_klass = Resource.resource_for(self.class.module_path + associated_model.class.to_s.underscore) if associated_model
|
685
|
+
return resource_klass.new(associated_model, @context) if resource_klass
|
686
|
+
else
|
687
|
+
resource_klass = association.resource_klass
|
688
|
+
if resource_klass
|
689
|
+
associated_model = public_send(associated_records_method_name)
|
690
|
+
return associated_model ? resource_klass.new(associated_model, @context) : nil
|
691
|
+
end
|
650
692
|
end
|
651
693
|
end unless method_defined?(attr)
|
652
|
-
elsif
|
694
|
+
elsif association.is_a?(JSONAPI::Association::HasMany)
|
695
|
+
define_method foreign_key do
|
696
|
+
records = public_send(associated_records_method_name)
|
697
|
+
return records.collect do |record|
|
698
|
+
record.send(association.resource_klass._primary_key)
|
699
|
+
end
|
700
|
+
end unless method_defined?(foreign_key)
|
653
701
|
define_method attr do |options = {}|
|
654
|
-
|
655
|
-
|
702
|
+
resource_klass = association.resource_klass
|
703
|
+
records = public_send(associated_records_method_name)
|
704
|
+
|
656
705
|
filters = options.fetch(:filters, {})
|
706
|
+
unless filters.nil? || filters.empty?
|
707
|
+
records = resource_klass.apply_filters(records, filters, options)
|
708
|
+
end
|
709
|
+
|
657
710
|
sort_criteria = options.fetch(:sort_criteria, {})
|
658
|
-
|
711
|
+
unless sort_criteria.nil? || sort_criteria.empty?
|
712
|
+
order_options = self.class.construct_order_options(sort_criteria)
|
713
|
+
records = resource_klass.apply_sort(records, order_options)
|
714
|
+
end
|
659
715
|
|
660
|
-
|
716
|
+
paginator = options[:paginator]
|
717
|
+
if paginator
|
718
|
+
records = resource_klass.apply_pagination(records, paginator, order_options)
|
719
|
+
end
|
661
720
|
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
order_options = self.class.construct_order_options(sort_criteria)
|
666
|
-
records = resource_class.apply_sort(records, order_options)
|
667
|
-
records = resource_class.apply_pagination(records, paginator, order_options)
|
668
|
-
records.each do |record|
|
669
|
-
resources.push resource_class.new(record, @context)
|
721
|
+
return records.collect do |record|
|
722
|
+
if association.polymorphic?
|
723
|
+
resource_klass = Resource.resource_for(self.class.module_path + record.class.to_s.underscore)
|
670
724
|
end
|
725
|
+
resource_klass.new(record, @context)
|
671
726
|
end
|
672
|
-
return resources
|
673
727
|
end unless method_defined?(attr)
|
674
728
|
end
|
675
729
|
end
|
676
730
|
end
|
677
731
|
end
|
678
|
-
|
679
732
|
end
|
680
733
|
end
|