jsonapi-resources 0.10.0.beta2 → 0.10.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.
@@ -1,13 +1,8 @@
1
- require 'jsonapi/active_relation_resource_finder/adapters/join_left_active_record_adapter'
2
-
3
1
  module JSONAPI
4
- module ActiveRelationResourceFinder
5
- def self.included(base)
6
- base.extend ClassMethods
7
- end
8
-
9
- module ClassMethods
2
+ class ActiveRelationResource < BasicResource
3
+ root_resource
10
4
 
5
+ class << self
11
6
  # Finds Resources using the `filters`. Pagination and sort options are used when provided
12
7
  #
13
8
  # @param filters [Hash] the filters hash
@@ -19,16 +14,15 @@ module JSONAPI
19
14
  def find(filters, options = {})
20
15
  sort_criteria = options.fetch(:sort_criteria) { [] }
21
16
 
22
- join_tree = JoinTree.new(resource_klass: self,
23
- options: options,
24
- filters: filters,
25
- sort_criteria: sort_criteria)
17
+ join_manager = ActiveRelation::JoinManager.new(resource_klass: self,
18
+ filters: filters,
19
+ sort_criteria: sort_criteria)
26
20
 
27
21
  paginator = options[:paginator]
28
22
 
29
- records = find_records(records: records(options),
30
- filters: filters,
31
- join_tree: join_tree,
23
+ records = apply_request_settings_to_records(records: records(options),
24
+ sort_criteria: sort_criteria,filters: filters,
25
+ join_manager: join_manager,
32
26
  paginator: paginator,
33
27
  options: options)
34
28
 
@@ -42,13 +36,12 @@ module JSONAPI
42
36
  #
43
37
  # @return [Integer] the count
44
38
  def count(filters, options = {})
45
- join_tree = JoinTree.new(resource_klass: self,
46
- options: options,
47
- filters: filters)
39
+ join_manager = ActiveRelation::JoinManager.new(resource_klass: self,
40
+ filters: filters)
48
41
 
49
- records = find_records(records: records(options),
42
+ records = apply_request_settings_to_records(records: records(options),
50
43
  filters: filters,
51
- join_tree: join_tree,
44
+ join_manager: join_manager,
52
45
  options: options)
53
46
 
54
47
  count_records(records)
@@ -72,6 +65,16 @@ module JSONAPI
72
65
  resources_for(records, options[:context])
73
66
  end
74
67
 
68
+ # Returns an array of Resources identified by the `keys` array. The resources are not filtered as this
69
+ # will have been done in a prior step
70
+ #
71
+ # @param keys [Array<key>] Array of primary keys to find resources for
72
+ # @option options [Hash] :context The context of the request, set in the controller
73
+ def find_to_populate_by_keys(keys, options = {})
74
+ records = records_for_populate(options).where(_primary_key => keys)
75
+ resources_for(records, options[:context])
76
+ end
77
+
75
78
  # Finds Resource fragments using the `filters`. Pagination and sort options are used when provided.
76
79
  # Retrieving the ResourceIdentities and attributes does not instantiate a model instance.
77
80
  # Note: This is incompatible with Polymorphic resources (which are going to come from two separate tables)
@@ -93,26 +96,23 @@ module JSONAPI
93
96
 
94
97
  sort_criteria = options.fetch(:sort_criteria) { [] }
95
98
 
96
- join_tree = JoinTree.new(resource_klass: resource_klass,
97
- source_relationship: nil,
98
- relationships: linkage_relationships,
99
- sort_criteria: sort_criteria,
100
- filters: filters,
101
- options: options)
99
+ join_manager = ActiveRelation::JoinManager.new(resource_klass: resource_klass,
100
+ source_relationship: nil,
101
+ relationships: linkage_relationships,
102
+ sort_criteria: sort_criteria,
103
+ filters: filters)
102
104
 
103
105
  paginator = options[:paginator]
104
106
 
105
- records = find_records(records: records(options),
107
+ records = apply_request_settings_to_records(records: records(options),
106
108
  filters: filters,
107
109
  sort_criteria: sort_criteria,
108
110
  paginator: paginator,
109
- join_tree: join_tree,
111
+ join_manager: join_manager,
110
112
  options: options)
111
113
 
112
- joins = join_tree.joins
113
-
114
114
  # This alias is going to be resolve down to the model's table name and will not actually be an alias
115
- resource_table_alias = joins[''][:alias]
115
+ resource_table_alias = resource_klass._table_name
116
116
 
117
117
  pluck_fields = [Arel.sql("#{concat_table_field(resource_table_alias, resource_klass._primary_key)} AS #{resource_table_alias}_#{resource_klass._primary_key}")]
118
118
 
@@ -131,7 +131,7 @@ module JSONAPI
131
131
  klass = resource_klass_for(resource_type)
132
132
  linkage_fields << {relationship_name: name, resource_klass: klass}
133
133
 
134
- linkage_table_alias = joins["#{linkage_relationship.name.to_s}##{resource_type}"][:alias]
134
+ linkage_table_alias = join_manager.join_details_by_polymorphic_relationship(linkage_relationship, resource_type)[:alias]
135
135
  primary_key = klass._primary_key
136
136
  pluck_fields << Arel.sql("#{concat_table_field(linkage_table_alias, primary_key)} AS #{linkage_table_alias}_#{primary_key}")
137
137
  end
@@ -139,7 +139,7 @@ module JSONAPI
139
139
  klass = linkage_relationship.resource_klass
140
140
  linkage_fields << {relationship_name: name, resource_klass: klass}
141
141
 
142
- linkage_table_alias = joins[name.to_s][:alias]
142
+ linkage_table_alias = join_manager.join_details_by_relationship(linkage_relationship)[:alias]
143
143
  primary_key = klass._primary_key
144
144
  pluck_fields << Arel.sql("#{concat_table_field(linkage_table_alias, primary_key)} AS #{linkage_table_alias}_#{primary_key}")
145
145
  end
@@ -155,7 +155,7 @@ module JSONAPI
155
155
 
156
156
  fragments = {}
157
157
  rows = records.pluck(*pluck_fields)
158
- rows.collect do |row|
158
+ rows.each do |row|
159
159
  rid = JSONAPI::ResourceIdentity.new(resource_klass, pluck_fields.length == 1 ? row : row[0])
160
160
 
161
161
  fragments[rid] ||= JSONAPI::ResourceFragment.new(rid)
@@ -181,6 +181,10 @@ module JSONAPI
181
181
  end
182
182
  end
183
183
 
184
+ if JSONAPI.configuration.warn_on_performance_issues && (rows.length > fragments.length)
185
+ warn "Performance issue detected: `#{self.name.to_s}.records` returned non-normalized results in `#{self.name.to_s}.find_fragments`."
186
+ end
187
+
184
188
  fragments
185
189
  end
186
190
 
@@ -229,28 +233,118 @@ module JSONAPI
229
233
  filters = options.fetch(:filters, {})
230
234
 
231
235
  # Joins in this case are related to the related_klass
232
- join_tree = JoinTree.new(resource_klass: self,
233
- source_relationship: relationship,
234
- filters: filters,
235
- options: options)
236
+ join_manager = ActiveRelation::JoinManager.new(resource_klass: self,
237
+ source_relationship: relationship,
238
+ filters: filters)
236
239
 
237
- records = find_records(records: records(options),
240
+ records = apply_request_settings_to_records(records: records(options),
238
241
  resource_klass: related_klass,
239
242
  primary_keys: source_rid.id,
240
- join_tree: join_tree,
243
+ join_manager: join_manager,
241
244
  filters: filters,
242
245
  options: options)
243
246
 
244
- joins = join_tree.joins
245
- related_alias = joins[''][:alias]
247
+ related_alias = join_manager.join_details_by_relationship(relationship)[:alias]
246
248
 
247
249
  records = records.select(Arel.sql("#{concat_table_field(related_alias, related_klass._primary_key)}"))
248
250
 
249
251
  count_records(records)
250
252
  end
251
253
 
252
- def records(_options = {})
253
- _model_class.distinct.all
254
+ # This resource class (ActiveRelationResource) uses an `ActiveRecord::Relation` as the starting point for
255
+ # retrieving models. From this relation filters, sorts and joins are applied as needed.
256
+ # Depending on which phase of the request processing different `records` methods will be called, giving the user
257
+ # the opportunity to override them differently for performance and security reasons.
258
+
259
+ # begin `records`methods
260
+
261
+ # Base for the `records` methods that follow and is not directly used for accessing model data by this class.
262
+ # Overriding this method gives a single place to affect the `ActiveRecord::Relation` used for the resource.
263
+ #
264
+ # @option options [Hash] :context The context of the request, set in the controller
265
+ #
266
+ # @return [ActiveRecord::Relation]
267
+ def records_base(_options = {})
268
+ _model_class.all
269
+ end
270
+
271
+ # The `ActiveRecord::Relation` used for finding user requested models. This may be overridden to enforce
272
+ # permissions checks on the request.
273
+ #
274
+ # @option options [Hash] :context The context of the request, set in the controller
275
+ #
276
+ # @return [ActiveRecord::Relation]
277
+ def records(options = {})
278
+ records_base(options)
279
+ end
280
+
281
+ # The `ActiveRecord::Relation` used for populating the ResourceSet. Only resources that have been previously
282
+ # identified through the `records` method will be accessed. Thus it should not be necessary to reapply permissions
283
+ # checks. However if the model needs to include other models adding `includes` is appropriate
284
+ #
285
+ # @option options [Hash] :context The context of the request, set in the controller
286
+ #
287
+ # @return [ActiveRecord::Relation]
288
+ def records_for_populate(options = {})
289
+ records_base(options)
290
+ end
291
+
292
+ # The `ActiveRecord::Relation` used for the finding related resources. Only resources that have been previously
293
+ # identified through the `records` method will be accessed and used as the basis to find related resources. Thus
294
+ # it should not be necessary to reapply permissions checks.
295
+ #
296
+ # @option options [Hash] :context The context of the request, set in the controller
297
+ #
298
+ # @return [ActiveRecord::Relation]
299
+ def records_for_source_to_related(options = {})
300
+ records_base(options)
301
+ end
302
+
303
+ # end `records` methods
304
+
305
+ def apply_join(records:, relationship:, resource_type:, join_type:, options:)
306
+ if relationship.polymorphic? && relationship.belongs_to?
307
+ case join_type
308
+ when :inner
309
+ records = records.joins(resource_type.to_s.singularize.to_sym)
310
+ when :left
311
+ records = records.joins_left(resource_type.to_s.singularize.to_sym)
312
+ end
313
+ else
314
+ relation_name = relationship.relation_name(options)
315
+ case join_type
316
+ when :inner
317
+ records = records.joins(relation_name)
318
+ when :left
319
+ records = records.joins_left(relation_name)
320
+ end
321
+ end
322
+ records
323
+ end
324
+
325
+ def relationship_records(relationship:, join_type: :inner, resource_type: nil, options: {})
326
+ records = relationship.parent_resource.records_for_source_to_related(options)
327
+ strategy = relationship.options[:apply_join]
328
+
329
+ if strategy
330
+ records = call_method_or_proc(strategy, records, relationship, resource_type, join_type, options)
331
+ else
332
+ records = apply_join(records: records,
333
+ relationship: relationship,
334
+ resource_type: resource_type,
335
+ join_type: join_type,
336
+ options: options)
337
+ end
338
+
339
+ records
340
+ end
341
+
342
+ def join_relationship(records:, relationship:, resource_type: nil, join_type: :inner, options: {})
343
+ relationship_records = relationship_records(relationship: relationship,
344
+ join_type: join_type,
345
+ resource_type: resource_type,
346
+ options: options)
347
+ records.merge(relationship_records)
254
348
  end
255
349
 
256
350
  protected
@@ -267,13 +361,13 @@ module JSONAPI
267
361
  end
268
362
 
269
363
  def find_record_by_key(key, options = {})
270
- record = find_records(records: records(options), primary_keys: key, options: options).first
364
+ record = apply_request_settings_to_records(records: records(options), primary_keys: key, options: options).first
271
365
  fail JSONAPI::Exceptions::RecordNotFound.new(key) if record.nil?
272
366
  record
273
367
  end
274
368
 
275
369
  def find_records_by_keys(keys, options = {})
276
- find_records(records: records(options), primary_keys: keys, options: options)
370
+ apply_request_settings_to_records(records: records(options), primary_keys: keys, options: options)
277
371
  end
278
372
 
279
373
  def find_related_monomorphic_fragments(source_rids, relationship, options, connect_source_identity)
@@ -290,26 +384,24 @@ module JSONAPI
290
384
  sort_criteria << { field: field, direction: sort[:direction] }
291
385
  end
292
386
 
293
- join_tree = JoinTree.new(resource_klass: self,
294
- source_relationship: relationship,
295
- relationships: linkage_relationships,
296
- sort_criteria: sort_criteria,
297
- filters: filters,
298
- options: options)
387
+ join_manager = ActiveRelation::JoinManager.new(resource_klass: self,
388
+ source_relationship: relationship,
389
+ relationships: linkage_relationships,
390
+ sort_criteria: sort_criteria,
391
+ filters: filters)
299
392
 
300
393
  paginator = options[:paginator] if source_rids.count == 1
301
394
 
302
- records = find_records(records: records(options),
395
+ records = apply_request_settings_to_records(records: records_for_source_to_related(options),
303
396
  resource_klass: resource_klass,
304
397
  sort_criteria: sort_criteria,
305
398
  primary_keys: source_ids,
306
399
  paginator: paginator,
307
400
  filters: filters,
308
- join_tree: join_tree,
401
+ join_manager: join_manager,
309
402
  options: options)
310
403
 
311
- joins = join_tree.joins
312
- resource_table_alias = joins[''][:alias]
404
+ resource_table_alias = join_manager.join_details_by_relationship(relationship)[:alias]
313
405
 
314
406
  pluck_fields = [
315
407
  Arel.sql("#{_table_name}.#{_primary_key} AS source_id"),
@@ -331,7 +423,7 @@ module JSONAPI
331
423
  klass = resource_klass_for(resource_type)
332
424
  linkage_fields << {relationship_name: name, resource_klass: klass}
333
425
 
334
- linkage_table_alias = joins["#{linkage_relationship.name.to_s}##{resource_type}"][:alias]
426
+ linkage_table_alias = join_manager.join_details_by_polymorphic_relationship(linkage_relationship, resource_type)[:alias]
335
427
  primary_key = klass._primary_key
336
428
  pluck_fields << Arel.sql("#{concat_table_field(linkage_table_alias, primary_key)} AS #{linkage_table_alias}_#{primary_key}")
337
429
  end
@@ -339,7 +431,7 @@ module JSONAPI
339
431
  klass = linkage_relationship.resource_klass
340
432
  linkage_fields << {relationship_name: name, resource_klass: klass}
341
433
 
342
- linkage_table_alias = joins[name.to_s][:alias]
434
+ linkage_table_alias = join_manager.join_details_by_relationship(linkage_relationship)[:alias]
343
435
  primary_key = klass._primary_key
344
436
  pluck_fields << Arel.sql("#{concat_table_field(linkage_table_alias, primary_key)} AS #{linkage_table_alias}_#{primary_key}")
345
437
  end
@@ -354,7 +446,7 @@ module JSONAPI
354
446
  end
355
447
 
356
448
  fragments = {}
357
- rows = records.pluck(*pluck_fields)
449
+ rows = records.distinct.pluck(*pluck_fields)
358
450
  rows.each do |row|
359
451
  rid = JSONAPI::ResourceIdentity.new(resource_klass, row[1])
360
452
 
@@ -418,27 +510,24 @@ module JSONAPI
418
510
  end
419
511
  end
420
512
 
421
- join_tree = JoinTree.new(resource_klass: self,
422
- source_relationship: relationship,
423
- relationships: linkage_relationships,
424
- filters: filters,
425
- options: options)
513
+ join_manager = ActiveRelation::JoinManager.new(resource_klass: self,
514
+ source_relationship: relationship,
515
+ relationships: linkage_relationships,
516
+ filters: filters)
426
517
 
427
518
  paginator = options[:paginator] if source_rids.count == 1
428
519
 
429
520
  # Note: We will sort by the source table. Without using unions we can't sort on a polymorphic relationship
430
521
  # in any manner that makes sense
431
- records = find_records(records: records(options),
522
+ records = apply_request_settings_to_records(records: records_for_source_to_related(options),
432
523
  resource_klass: resource_klass,
433
524
  sort_primary: true,
434
525
  primary_keys: source_ids,
435
526
  paginator: paginator,
436
527
  filters: filters,
437
- join_tree: join_tree,
528
+ join_manager: join_manager,
438
529
  options: options)
439
530
 
440
- joins = join_tree.joins
441
-
442
531
  primary_key = concat_table_field(_table_name, _primary_key)
443
532
  related_key = concat_table_field(_table_name, relationship.foreign_key)
444
533
  related_type = concat_table_field(_table_name, relationship.polymorphic_type)
@@ -467,7 +556,7 @@ module JSONAPI
467
556
 
468
557
  cache_field = related_klass.attribute_to_model_field(:_cache_field) if options[:cache]
469
558
 
470
- table_alias = joins["##{type}"][:alias]
559
+ table_alias = join_manager.source_join_details(type)[:alias]
471
560
 
472
561
  cache_offset = relation_index
473
562
  if cache_field
@@ -515,7 +604,7 @@ module JSONAPI
515
604
  klass = resource_klass_for(resource_type)
516
605
  linkage_fields << {relationship: linkage_relationship, resource_klass: klass}
517
606
 
518
- linkage_table_alias = joins[linkage_relationship_path][:alias]
607
+ linkage_table_alias = join_manager.join_details_by_polymorphic_relationship(linkage_relationship, resource_type)[:alias]
519
608
  primary_key = klass._primary_key
520
609
  pluck_fields << Arel.sql("#{concat_table_field(linkage_table_alias, primary_key)} AS #{linkage_table_alias}_#{primary_key}")
521
610
  end
@@ -523,13 +612,13 @@ module JSONAPI
523
612
  klass = linkage_relationship.resource_klass
524
613
  linkage_fields << {relationship: linkage_relationship, resource_klass: klass}
525
614
 
526
- linkage_table_alias = joins[linkage_relationship_path.to_s][:alias]
615
+ linkage_table_alias = join_manager.join_details_by_relationship(linkage_relationship)[:alias]
527
616
  primary_key = klass._primary_key
528
617
  pluck_fields << Arel.sql("#{concat_table_field(linkage_table_alias, primary_key)} AS #{linkage_table_alias}_#{primary_key}")
529
618
  end
530
619
  end
531
620
 
532
- rows = records.pluck(*pluck_fields)
621
+ rows = records.distinct.pluck(*pluck_fields)
533
622
 
534
623
  related_fragments = {}
535
624
 
@@ -581,26 +670,26 @@ module JSONAPI
581
670
  related_fragments
582
671
  end
583
672
 
584
- def find_records(records:,
585
- join_tree: JoinTree.new(resource_klass: self),
586
- resource_klass: self,
587
- filters: nil,
588
- primary_keys: nil,
589
- sort_criteria: nil,
590
- sort_primary: nil,
591
- paginator: nil,
592
- options: {})
673
+ def apply_request_settings_to_records(records:,
674
+ join_manager: ActiveRelation::JoinManager.new(resource_klass: self),
675
+ resource_klass: self,
676
+ filters: {},
677
+ primary_keys: nil,
678
+ sort_criteria: nil,
679
+ sort_primary: nil,
680
+ paginator: nil,
681
+ options: {})
593
682
 
594
683
  opts = options.dup
595
- records = resource_klass.apply_joins(records, join_tree, opts)
684
+ records = resource_klass.apply_joins(records, join_manager, opts)
596
685
 
597
686
  if primary_keys
598
687
  records = records.where(_primary_key => primary_keys)
599
688
  end
600
689
 
601
- opts[:joins] = join_tree.joins
690
+ opts[:join_manager] = join_manager
602
691
 
603
- if filters
692
+ unless filters.empty?
604
693
  records = resource_klass.filter_records(records, filters, opts)
605
694
  end
606
695
 
@@ -618,52 +707,8 @@ module JSONAPI
618
707
  records
619
708
  end
620
709
 
621
- def get_join_alias(records, &block)
622
- init_join_sources = records.arel.join_sources
623
- init_join_sources_length = init_join_sources.length
624
-
625
- records = yield(records)
626
-
627
- join_sources = records.arel.join_sources
628
- if join_sources.length > init_join_sources_length
629
- last_join = (join_sources - init_join_sources).last
630
- join_alias =
631
- case last_join.left
632
- when Arel::Table
633
- last_join.left.name
634
- when Arel::Nodes::TableAlias
635
- last_join.left.right
636
- when Arel::Nodes::StringJoin
637
- # :nocov:
638
- warn "get_join_alias: Unsupported join type - use custom filtering and sorting"
639
- nil
640
- # :nocov:
641
- end
642
- else
643
- # :nocov:
644
- warn "get_join_alias: No join added"
645
- join_alias = nil
646
- # :nocov:
647
- end
648
-
649
- return records, join_alias
650
- end
651
-
652
- def apply_joins(records, join_tree, _options)
653
- joins = join_tree.joins
654
-
655
- joins.each_value do |join|
656
- case join[:join_type]
657
- when :inner
658
- records, join_alias = get_join_alias(records) { |records| records.joins(join[:relation_join_hash]) }
659
- join[:alias] = join_alias
660
- when :left
661
- records, join_alias = get_join_alias(records) { |records| records.joins_left(join[:relation_join_hash]) }
662
- join[:alias] = join_alias
663
- end
664
- end
665
-
666
- return records
710
+ def apply_joins(records, join_manager, options)
711
+ join_manager.join(records, options)
667
712
  end
668
713
 
669
714
  def apply_pagination(records, paginator, order_options)
@@ -689,9 +734,9 @@ module JSONAPI
689
734
  if strategy
690
735
  records = call_method_or_proc(strategy, records, direction, context)
691
736
  else
692
- joins = options[:joins] || {}
737
+ join_manager = options[:join_manager]
693
738
 
694
- records = records.order("#{get_aliased_field(field, joins)} #{direction}")
739
+ records = records.order(Arel.sql("#{get_aliased_field(field, join_manager)} #{direction}"))
695
740
  end
696
741
  records
697
742
  end
@@ -758,22 +803,20 @@ module JSONAPI
758
803
  records
759
804
  end
760
805
 
761
- def get_aliased_field(path_with_field, joins)
806
+ def get_aliased_field(path_with_field, join_manager)
762
807
  path = JSONAPI::Path.new(resource_klass: self, path_string: path_with_field)
763
808
 
764
- relationship = path.segments[-2]
765
- field = path.segments[-1]
766
- relationship_path = path.relationship_path_string
809
+ relationship_segment = path.segments[-2]
810
+ field_segment = path.segments[-1]
767
811
 
768
- if relationship
769
- join_name = relationship_path
770
- join = joins.try(:[], join_name)
771
- table_alias = join.try(:[], :alias)
812
+ if relationship_segment
813
+ join_details = join_manager.join_details[path.last_relationship]
814
+ table_alias = join_details[:alias]
815
+ else
816
+ table_alias = self._table_name
772
817
  end
773
818
 
774
- table_alias ||= joins[''][:alias]
775
-
776
- concat_table_field(table_alias, field.delegated_field_name)
819
+ concat_table_field(table_alias, field_segment.delegated_field_name)
777
820
  end
778
821
 
779
822
  def apply_filter(records, filter, value, options = {})
@@ -782,12 +825,12 @@ module JSONAPI
782
825
  if strategy
783
826
  records = call_method_or_proc(strategy, records, value, options)
784
827
  else
785
- joins = options[:joins] || {}
786
- records = records.where(get_aliased_field(filter, joins) => value)
828
+ join_manager = options[:join_manager]
829
+ records = records.where(Arel.sql(get_aliased_field(filter, join_manager)) => value)
787
830
  end
788
831
 
789
832
  records
790
833
  end
791
834
  end
792
835
  end
793
- end
836
+ end
@@ -70,7 +70,7 @@ module JSONAPI
70
70
  def get_related_resources
71
71
  # :nocov:
72
72
  ActiveSupport::Deprecation.warn "In #{self.class.name} you exposed a `get_related_resources`"\
73
- " action. Please use `index_related_resource` instead."
73
+ " action. Please use `index_related_resources` instead."
74
74
  index_related_resources
75
75
  # :nocov:
76
76
  end
@@ -102,7 +102,8 @@ module JSONAPI
102
102
  base_url: base_url,
103
103
  key_formatter: key_formatter,
104
104
  route_formatter: route_formatter,
105
- serialization_options: serialization_options
105
+ serialization_options: serialization_options,
106
+ controller: self
106
107
  )
107
108
  op.options[:cache_serializer_output] = !JSONAPI.configuration.resource_cache.nil?
108
109
 
@@ -151,7 +152,7 @@ module JSONAPI
151
152
  end
152
153
 
153
154
  def base_url
154
- @base_url ||= request.protocol + request.host_with_port
155
+ @base_url ||= "#{request.protocol}#{request.host_with_port}#{Rails.application.config.relative_url_root}"
155
156
  end
156
157
 
157
158
  def resource_klass_name