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.
Files changed (44) hide show
  1. checksums.yaml +5 -5
  2. data/LICENSE.txt +1 -1
  3. data/README.md +34 -11
  4. data/lib/bug_report_templates/rails_5_latest.rb +125 -0
  5. data/lib/bug_report_templates/rails_5_master.rb +140 -0
  6. data/lib/jsonapi/active_relation/adapters/join_left_active_record_adapter.rb +27 -0
  7. data/lib/jsonapi/active_relation/join_manager.rb +303 -0
  8. data/lib/jsonapi/active_relation_resource.rb +884 -0
  9. data/lib/jsonapi/acts_as_resource_controller.rb +122 -105
  10. data/lib/jsonapi/basic_resource.rb +1162 -0
  11. data/lib/jsonapi/cached_response_fragment.rb +127 -0
  12. data/lib/jsonapi/compiled_json.rb +11 -1
  13. data/lib/jsonapi/configuration.rb +71 -8
  14. data/lib/jsonapi/error.rb +27 -0
  15. data/lib/jsonapi/error_codes.rb +2 -0
  16. data/lib/jsonapi/exceptions.rb +80 -50
  17. data/lib/jsonapi/formatter.rb +3 -3
  18. data/lib/jsonapi/include_directives.rb +18 -65
  19. data/lib/jsonapi/link_builder.rb +74 -80
  20. data/lib/jsonapi/operation.rb +16 -5
  21. data/lib/jsonapi/operation_result.rb +74 -16
  22. data/lib/jsonapi/path.rb +43 -0
  23. data/lib/jsonapi/path_segment.rb +76 -0
  24. data/lib/jsonapi/processor.rb +239 -111
  25. data/lib/jsonapi/relationship.rb +153 -15
  26. data/lib/jsonapi/request_parser.rb +430 -367
  27. data/lib/jsonapi/resource.rb +3 -1253
  28. data/lib/jsonapi/resource_controller_metal.rb +5 -2
  29. data/lib/jsonapi/resource_fragment.rb +47 -0
  30. data/lib/jsonapi/resource_id_tree.rb +112 -0
  31. data/lib/jsonapi/resource_identity.rb +42 -0
  32. data/lib/jsonapi/resource_serializer.rb +143 -285
  33. data/lib/jsonapi/resource_set.rb +176 -0
  34. data/lib/jsonapi/resources/railtie.rb +9 -0
  35. data/lib/jsonapi/resources/version.rb +1 -1
  36. data/lib/jsonapi/response_document.rb +105 -83
  37. data/lib/jsonapi/routing_ext.rb +48 -26
  38. data/lib/jsonapi-resources.rb +20 -4
  39. data/lib/tasks/check_upgrade.rake +52 -0
  40. metadata +50 -20
  41. data/lib/jsonapi/cached_resource_fragment.rb +0 -127
  42. data/lib/jsonapi/operation_dispatcher.rb +0 -88
  43. data/lib/jsonapi/operation_results.rb +0 -35
  44. data/lib/jsonapi/relationship_builder.rb +0 -167
@@ -0,0 +1,176 @@
1
+ module JSONAPI
2
+ # Contains a hash of resource types which contain a hash of resources, relationships and primary status keyed by
3
+ # resource id.
4
+ class ResourceSet
5
+
6
+ attr_reader :resource_klasses, :populated
7
+
8
+ def initialize(resource_id_tree = nil)
9
+ @populated = false
10
+ @resource_klasses = resource_id_tree.nil? ? {} : flatten_resource_id_tree(resource_id_tree)
11
+ end
12
+
13
+ def populate!(serializer, context, find_options)
14
+ # For each resource klass we want to generate the caching key
15
+
16
+ # Hash for collecting types and ids
17
+ # @type [Hash<Class<Resource>, Id[]]]
18
+ missed_resource_ids = {}
19
+
20
+ # Array for collecting CachedResponseFragment::Lookups
21
+ # @type [Lookup[]]
22
+ lookups = []
23
+
24
+
25
+ # Step One collect all of the lookups for the cache, or keys that don't require cache access
26
+ @resource_klasses.each_key do |resource_klass|
27
+
28
+ serializer_config_key = serializer.config_key(resource_klass).gsub("/", "_")
29
+ context_json = resource_klass.attribute_caching_context(context).to_json
30
+ context_b64 = JSONAPI.configuration.resource_cache_digest_function.call(context_json)
31
+ context_key = "ATTR-CTX-#{context_b64.gsub("/", "_")}"
32
+
33
+ if resource_klass.caching?
34
+ cache_ids = @resource_klasses[resource_klass].map do |(k, v)|
35
+ # Store the hashcode of the cache_field to avoid storing objects and to ensure precision isn't lost
36
+ # on timestamp types (i.e. string conversions dropping milliseconds)
37
+ [k, resource_klass.hash_cache_field(v[:cache_id])]
38
+ end
39
+
40
+ lookups.push(
41
+ CachedResponseFragment::Lookup.new(
42
+ resource_klass,
43
+ serializer_config_key,
44
+ context,
45
+ context_key,
46
+ cache_ids
47
+ )
48
+ )
49
+ else
50
+ missed_resource_ids[resource_klass] = @resource_klasses[resource_klass].keys
51
+ end
52
+ end
53
+
54
+ if lookups.any?
55
+ raise "You've declared some Resources as caching without providing a caching store" if JSONAPI.configuration.resource_cache.nil?
56
+
57
+ # Step Two execute the cache lookup
58
+ found_resources = CachedResponseFragment.lookup(lookups, context)
59
+ else
60
+ found_resources = {}
61
+ end
62
+
63
+
64
+ # Step Three collect the results and collect hit/miss stats
65
+ stats = {}
66
+ found_resources.each do |resource_klass, resources|
67
+ resources.each do |id, cached_resource|
68
+ stats[resource_klass] ||= {}
69
+
70
+ if cached_resource.nil?
71
+ stats[resource_klass][:misses] ||= 0
72
+ stats[resource_klass][:misses] += 1
73
+
74
+ # Collect misses
75
+ missed_resource_ids[resource_klass] ||= []
76
+ missed_resource_ids[resource_klass].push(id)
77
+ else
78
+ stats[resource_klass][:hits] ||= 0
79
+ stats[resource_klass][:hits] += 1
80
+
81
+ register_resource(resource_klass, cached_resource)
82
+ end
83
+ end
84
+ end
85
+
86
+ report_stats(stats)
87
+
88
+ writes = []
89
+
90
+ # Step Four find any of the missing resources and join them into the result
91
+ missed_resource_ids.each_pair do |resource_klass, ids|
92
+ find_opts = {context: context, fields: find_options[:fields]}
93
+ found_resources = resource_klass.find_to_populate_by_keys(ids, find_opts)
94
+
95
+ found_resources.each do |resource|
96
+ relationship_data = @resource_klasses[resource_klass][resource.id][:relationships]
97
+
98
+ if resource_klass.caching?
99
+
100
+ serializer_config_key = serializer.config_key(resource_klass).gsub("/", "_")
101
+ context_json = resource_klass.attribute_caching_context(context).to_json
102
+ context_b64 = JSONAPI.configuration.resource_cache_digest_function.call(context_json)
103
+ context_key = "ATTR-CTX-#{context_b64.gsub("/", "_")}"
104
+
105
+ writes.push(CachedResponseFragment::Write.new(
106
+ resource_klass,
107
+ resource,
108
+ serializer,
109
+ serializer_config_key,
110
+ context,
111
+ context_key,
112
+ relationship_data
113
+ ))
114
+ end
115
+
116
+ register_resource(resource_klass, resource)
117
+ end
118
+ end
119
+
120
+ # Step Five conditionally write to the cache
121
+ CachedResponseFragment.write(writes) unless JSONAPI.configuration.resource_cache.nil?
122
+
123
+ mark_populated!
124
+ self
125
+ end
126
+
127
+ def mark_populated!
128
+ @populated = true
129
+ end
130
+
131
+ def register_resource(resource_klass, resource, primary = false)
132
+ @resource_klasses[resource_klass] ||= {}
133
+ @resource_klasses[resource_klass][resource.id] ||= {primary: resource.try(:primary) || primary, relationships: {}}
134
+ @resource_klasses[resource_klass][resource.id][:resource] = resource
135
+ end
136
+
137
+ private
138
+
139
+ def report_stats(stats)
140
+ return unless JSONAPI.configuration.resource_cache_usage_report_function || JSONAPI.configuration.resource_cache.nil?
141
+
142
+ stats.each_pair do |resource_klass, stat|
143
+ JSONAPI.configuration.resource_cache_usage_report_function.call(
144
+ resource_klass.name,
145
+ stat[:hits] || 0,
146
+ stat[:misses] || 0
147
+ )
148
+ end
149
+ end
150
+
151
+ def flatten_resource_id_tree(resource_id_tree, flattened_tree = {})
152
+ resource_id_tree.fragments.each_pair do |resource_rid, fragment|
153
+
154
+ resource_klass = resource_rid.resource_klass
155
+ id = resource_rid.id
156
+
157
+ flattened_tree[resource_klass] ||= {}
158
+
159
+ flattened_tree[resource_klass][id] ||= {primary: fragment.primary, relationships: {}}
160
+ flattened_tree[resource_klass][id][:cache_id] ||= fragment.cache
161
+
162
+ fragment.related.try(:each_pair) do |relationship_name, related_rids|
163
+ flattened_tree[resource_klass][id][:relationships][relationship_name] ||= Set.new
164
+ flattened_tree[resource_klass][id][:relationships][relationship_name].merge(related_rids)
165
+ end
166
+ end
167
+
168
+ related_resource_id_trees = resource_id_tree.related_resource_id_trees
169
+ related_resource_id_trees.try(:each_value) do |related_resource_id_tree|
170
+ flatten_resource_id_tree(related_resource_id_tree, flattened_tree)
171
+ end
172
+
173
+ flattened_tree
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,9 @@
1
+ module JSONAPI
2
+ module Resources
3
+ class Railtie < Rails::Railtie
4
+ rake_tasks do
5
+ load 'tasks/check_upgrade.rake'
6
+ end
7
+ end
8
+ end
9
+ end
@@ -1,5 +1,5 @@
1
1
  module JSONAPI
2
2
  module Resources
3
- VERSION = '0.9.0'
3
+ VERSION = '0.10.6'
4
4
  end
5
5
  end
@@ -1,81 +1,138 @@
1
1
  module JSONAPI
2
2
  class ResponseDocument
3
- def initialize(operation_results, serializer, options = {})
4
- @operation_results = operation_results
5
- @serializer = serializer
3
+ attr_reader :serialized_results
4
+
5
+ def initialize(options = {})
6
+ @serialized_results = []
7
+ @result_codes = []
8
+ @error_results = []
9
+ @global_errors = []
10
+
6
11
  @options = options
7
12
 
13
+ @top_level_meta = @options.fetch(:base_meta, {})
14
+ @top_level_links = @options.fetch(:base_links, {})
15
+
8
16
  @key_formatter = @options.fetch(:key_formatter, JSONAPI.configuration.key_formatter)
9
17
  end
10
18
 
11
- def contents
12
- hash = results_to_hash
19
+ def has_errors?
20
+ @error_results.length > 0 || @global_errors.length > 0
21
+ end
13
22
 
14
- meta = top_level_meta
15
- hash.merge!(meta: meta) unless meta.empty?
23
+ def add_result(result, operation)
24
+ if result.is_a?(JSONAPI::ErrorsOperationResult)
25
+ # Clear any serialized results
26
+ @serialized_results = []
16
27
 
17
- links = top_level_links
18
- hash.merge!(links: links) unless links.empty?
28
+ # In JSONAPI v1 we only have one operation so all errors can be kept together
29
+ result.errors.each do |error|
30
+ add_global_error(error)
31
+ end
32
+ else
33
+ @serialized_results.push result.to_hash(operation.options[:serializer])
34
+ @result_codes.push result.code.to_i
35
+ update_links(operation.options[:serializer], result)
36
+ update_meta(result)
37
+ end
38
+ end
19
39
 
20
- hash
40
+ def add_global_error(error)
41
+ @global_errors.push error
21
42
  end
22
43
 
23
- def status
24
- if @operation_results.has_errors?
25
- @operation_results.all_errors[0].status
44
+ def contents
45
+ if has_errors?
46
+ return { 'errors' => @global_errors }
26
47
  else
27
- @operation_results.results[0].code
48
+ hash = @serialized_results[0]
49
+ meta = top_level_meta
50
+ hash.merge!('meta' => meta) unless meta.empty?
51
+
52
+ links = top_level_links
53
+ hash.merge!('links' => links) unless links.empty?
54
+
55
+ return hash
28
56
  end
29
57
  end
30
58
 
31
- private
59
+ def status
60
+ status_codes = if has_errors?
61
+ @global_errors.collect do |error|
62
+ error.status.to_i
63
+ end
64
+ else
65
+ @result_codes
66
+ end
67
+
68
+ # Count the unique status codes
69
+ counts = status_codes.each_with_object(Hash.new(0)) { |code, counts| counts[code] += 1 }
70
+
71
+ # if there is only one status code we can return that
72
+ return counts.keys[0].to_i if counts.length == 1
73
+
74
+ # :nocov: not currently used
75
+
76
+ # if there are many we should return the highest general code, 200, 400, 500 etc.
77
+ max_status = 0
78
+ status_codes.each do |status|
79
+ code = status.to_i
80
+ max_status = code if max_status < code
81
+ end
82
+ return (max_status / 100).floor * 100
83
+ # :nocov:
84
+ end
32
85
 
33
- # Rolls up the top level meta data from the base_meta, the set of operations,
34
- # and the result of each operation. The keys are then formatted.
35
- def top_level_meta
36
- meta = @options.fetch(:base_meta, {})
86
+ private
37
87
 
38
- meta.merge!(@operation_results.meta)
88
+ def update_meta(result)
89
+ @top_level_meta.merge!(result.meta)
39
90
 
40
- @operation_results.results.each do |result|
41
- meta.merge!(result.meta)
91
+ if JSONAPI.configuration.top_level_meta_include_record_count && result.respond_to?(:record_count)
92
+ @top_level_meta[JSONAPI.configuration.top_level_meta_record_count_key] = result.record_count
93
+ end
42
94
 
43
- if JSONAPI.configuration.top_level_meta_include_record_count && result.respond_to?(:record_count)
44
- meta[JSONAPI.configuration.top_level_meta_record_count_key] = result.record_count
45
- end
95
+ if JSONAPI.configuration.top_level_meta_include_page_count && result.respond_to?(:page_count)
96
+ @top_level_meta[JSONAPI.configuration.top_level_meta_page_count_key] = result.page_count
97
+ end
46
98
 
47
- if JSONAPI.configuration.top_level_meta_include_page_count && result.respond_to?(:page_count)
48
- meta[JSONAPI.configuration.top_level_meta_page_count_key] = result.page_count
99
+ if result.warnings.any?
100
+ @top_level_meta[:warnings] = result.warnings.collect do |warning|
101
+ warning.to_hash
49
102
  end
50
103
  end
104
+ end
51
105
 
52
- meta.as_json.deep_transform_keys { |key| @key_formatter.format(key) }
106
+ def top_level_meta
107
+ @top_level_meta.as_json.deep_transform_keys { |key| @key_formatter.format(key) }
53
108
  end
54
109
 
55
- # Rolls up the top level links from the base_links, the set of operations,
56
- # and the result of each operation. The keys are then formatted.
57
- def top_level_links
58
- links = @options.fetch(:base_links, {})
59
-
60
- links.merge!(@operation_results.links)
61
-
62
- @operation_results.results.each do |result|
63
- links.merge!(result.links)
64
-
65
- # Build pagination links
66
- if result.is_a?(JSONAPI::ResourcesOperationResult) || result.is_a?(JSONAPI::RelatedResourcesOperationResult)
67
- result.pagination_params.each_pair do |link_name, params|
68
- if result.is_a?(JSONAPI::RelatedResourcesOperationResult)
69
- relationship = result.source_resource.class._relationships[result._type.to_sym]
70
- links[link_name] = @serializer.link_builder.relationships_related_link(result.source_resource, relationship, query_params(params))
71
- else
72
- links[link_name] = @serializer.query_link(query_params(params))
73
- end
110
+ def update_links(serializer, result)
111
+ @top_level_links.merge!(result.links)
112
+
113
+ # Build pagination links
114
+ if result.is_a?(JSONAPI::ResourceSetOperationResult) ||
115
+ result.is_a?(JSONAPI::ResourcesSetOperationResult) ||
116
+ result.is_a?(JSONAPI::RelatedResourcesSetOperationResult)
117
+
118
+ result.pagination_params.each_pair do |link_name, params|
119
+ if result.is_a?(JSONAPI::RelatedResourcesSetOperationResult)
120
+ relationship = result.source_resource.class._relationships[result._type.to_sym]
121
+ unless relationship.exclude_link?(link_name)
122
+ link = serializer.link_builder.relationships_related_link(result.source_resource, relationship, query_params(params))
123
+ end
124
+ else
125
+ unless serializer.link_builder.primary_resource_klass.exclude_link?(link_name)
126
+ link = serializer.link_builder.query_link(query_params(params))
74
127
  end
128
+ end
129
+ @top_level_links[link_name] = link unless link.blank?
75
130
  end
76
131
  end
132
+ end
77
133
 
78
- links.deep_transform_keys { |key| @key_formatter.format(key) }
134
+ def top_level_links
135
+ @top_level_links.deep_transform_keys { |key| @key_formatter.format(key) }
79
136
  end
80
137
 
81
138
  def query_params(params)
@@ -96,40 +153,5 @@ module JSONAPI
96
153
 
97
154
  query_params
98
155
  end
99
-
100
- def results_to_hash
101
- if @operation_results.has_errors?
102
- { errors: @operation_results.all_errors }
103
- else
104
- if @operation_results.results.length == 1
105
- result = @operation_results.results[0]
106
-
107
- case result
108
- when JSONAPI::ResourceOperationResult
109
- @serializer.serialize_to_hash(result.resource)
110
- when JSONAPI::ResourcesOperationResult
111
- @serializer.serialize_to_hash(result.resources)
112
- when JSONAPI::LinksObjectOperationResult
113
- @serializer.serialize_to_links_hash(result.parent_resource,
114
- result.relationship)
115
- when JSONAPI::OperationResult
116
- {}
117
- end
118
-
119
- elsif @operation_results.results.length > 1
120
- resources = []
121
- @operation_results.results.each do |result|
122
- case result
123
- when JSONAPI::ResourceOperationResult
124
- resources.push(result.resource)
125
- when JSONAPI::ResourcesOperationResult
126
- resources.concat(result.resources)
127
- end
128
- end
129
-
130
- @serializer.serialize_to_hash(resources)
131
- end
132
- end
133
- end
134
156
  end
135
157
  end
@@ -18,7 +18,13 @@ module ActionDispatch
18
18
 
19
19
  def jsonapi_resource(*resources, &_block)
20
20
  @resource_type = resources.first
21
- res = JSONAPI::Resource.resource_for(resource_type_with_module_prefix(@resource_type))
21
+ res = JSONAPI::Resource.resource_klass_for(resource_type_with_module_prefix(@resource_type))
22
+
23
+ res._routed = true
24
+
25
+ unless res.singleton?
26
+ warn "Singleton routes created for non singleton resource #{res}. Links may not be generated correctly."
27
+ end
22
28
 
23
29
  options = resources.extract_options!.dup
24
30
  options[:controller] ||= @resource_type
@@ -33,8 +39,8 @@ module ActionDispatch
33
39
  end
34
40
 
35
41
  if res._immutable
36
- options[:except] << :create unless options[:except].include?(:create) || options[:except].include?('create')
37
- options[:except] << :update unless options[:except].include?(:update) || options[:except].include?('update')
42
+ options[:except] << :create unless options[:except].include?(:create) || options[:except].include?('create')
43
+ options[:except] << :update unless options[:except].include?(:update) || options[:except].include?('update')
38
44
  options[:except] << :destroy unless options[:except].include?(:destroy) || options[:except].include?('destroy')
39
45
  end
40
46
 
@@ -64,7 +70,7 @@ module ActionDispatch
64
70
  end
65
71
 
66
72
  def jsonapi_relationships(options = {})
67
- res = JSONAPI::Resource.resource_for(resource_type_with_module_prefix(@resource_type))
73
+ res = JSONAPI::Resource.resource_klass_for(resource_type_with_module_prefix(@resource_type))
68
74
  res._relationships.each do |relationship_name, relationship|
69
75
  if relationship.is_a?(JSONAPI::Relationship::ToMany)
70
76
  jsonapi_links(relationship_name, options)
@@ -78,7 +84,13 @@ module ActionDispatch
78
84
 
79
85
  def jsonapi_resources(*resources, &_block)
80
86
  @resource_type = resources.first
81
- res = JSONAPI::Resource.resource_for(resource_type_with_module_prefix(@resource_type))
87
+ res = JSONAPI::Resource.resource_klass_for(resource_type_with_module_prefix(@resource_type))
88
+
89
+ res._routed = true
90
+
91
+ if res.singleton?
92
+ warn "Singleton resource #{res} should use `jsonapi_resource` instead."
93
+ end
82
94
 
83
95
  options = resources.extract_options!.dup
84
96
  options[:controller] ||= @resource_type
@@ -102,8 +114,8 @@ module ActionDispatch
102
114
  end
103
115
 
104
116
  if res._immutable
105
- options[:except] << :create unless options[:except].include?(:create) || options[:except].include?('create')
106
- options[:except] << :update unless options[:except].include?(:update) || options[:except].include?('update')
117
+ options[:except] << :create unless options[:except].include?(:create) || options[:except].include?('create')
118
+ options[:except] << :update unless options[:except].include?(:update) || options[:except].include?('update')
107
119
  options[:except] << :destroy unless options[:except].include?(:destroy) || options[:except].include?('destroy')
108
120
  end
109
121
 
@@ -147,25 +159,26 @@ module ActionDispatch
147
159
  formatted_relationship_name = format_route(link_type)
148
160
  options = links.extract_options!.dup
149
161
 
150
- res = JSONAPI::Resource.resource_for(resource_type_with_module_prefix)
162
+ res = JSONAPI::Resource.resource_klass_for(resource_type_with_module_prefix)
151
163
  options[:controller] ||= res._type.to_s
152
164
 
153
165
  methods = links_methods(options)
154
166
 
155
167
  if methods.include?(:show)
156
168
  match "relationships/#{formatted_relationship_name}", controller: options[:controller],
157
- action: 'show_relationship', relationship: link_type.to_s, via: [:get]
169
+ action: 'show_relationship', relationship: link_type.to_s, via: [:get],
170
+ as: "relationships/#{link_type}"
158
171
  end
159
172
 
160
173
  if res.mutable?
161
174
  if methods.include?(:update)
162
175
  match "relationships/#{formatted_relationship_name}", controller: options[:controller],
163
- action: 'update_relationship', relationship: link_type.to_s, via: [:put, :patch]
176
+ action: 'update_relationship', relationship: link_type.to_s, via: [:put, :patch]
164
177
  end
165
178
 
166
179
  if methods.include?(:destroy)
167
180
  match "relationships/#{formatted_relationship_name}", controller: options[:controller],
168
- action: 'destroy_relationship', relationship: link_type.to_s, via: [:delete]
181
+ action: 'destroy_relationship', relationship: link_type.to_s, via: [:delete]
169
182
  end
170
183
  end
171
184
  end
@@ -175,69 +188,77 @@ module ActionDispatch
175
188
  formatted_relationship_name = format_route(link_type)
176
189
  options = links.extract_options!.dup
177
190
 
178
- res = JSONAPI::Resource.resource_for(resource_type_with_module_prefix)
191
+ res = JSONAPI::Resource.resource_klass_for(resource_type_with_module_prefix)
179
192
  options[:controller] ||= res._type.to_s
180
193
 
181
194
  methods = links_methods(options)
182
195
 
183
196
  if methods.include?(:show)
184
197
  match "relationships/#{formatted_relationship_name}", controller: options[:controller],
185
- action: 'show_relationship', relationship: link_type.to_s, via: [:get]
198
+ action: 'show_relationship', relationship: link_type.to_s, via: [:get],
199
+ as: "relationships/#{link_type}"
186
200
  end
187
201
 
188
202
  if res.mutable?
189
203
  if methods.include?(:create)
190
204
  match "relationships/#{formatted_relationship_name}", controller: options[:controller],
191
- action: 'create_relationship', relationship: link_type.to_s, via: [:post]
205
+ action: 'create_relationship', relationship: link_type.to_s, via: [:post]
192
206
  end
193
207
 
194
208
  if methods.include?(:update)
195
209
  match "relationships/#{formatted_relationship_name}", controller: options[:controller],
196
- action: 'update_relationship', relationship: link_type.to_s, via: [:put, :patch]
210
+ action: 'update_relationship', relationship: link_type.to_s, via: [:put, :patch]
197
211
  end
198
212
 
199
213
  if methods.include?(:destroy)
200
214
  match "relationships/#{formatted_relationship_name}", controller: options[:controller],
201
- action: 'destroy_relationship', relationship: link_type.to_s, via: [:delete]
215
+ action: 'destroy_relationship', relationship: link_type.to_s, via: [:delete]
202
216
  end
203
217
  end
204
218
  end
205
219
 
206
220
  def jsonapi_related_resource(*relationship)
207
- source = JSONAPI::Resource.resource_for(resource_type_with_module_prefix)
221
+ source = JSONAPI::Resource.resource_klass_for(resource_type_with_module_prefix)
208
222
  options = relationship.extract_options!.dup
209
223
 
210
224
  relationship_name = relationship.first
211
225
  relationship = source._relationships[relationship_name]
212
226
 
227
+ relationship._routed = true
228
+
213
229
  formatted_relationship_name = format_route(relationship.name)
214
230
 
215
231
  if relationship.polymorphic?
216
232
  options[:controller] ||= relationship.class_name.underscore.pluralize
217
233
  else
218
- related_resource = JSONAPI::Resource.resource_for(resource_type_with_module_prefix(relationship.class_name.underscore.pluralize))
234
+ related_resource = JSONAPI::Resource.resource_klass_for(resource_type_with_module_prefix(relationship.class_name.underscore.pluralize))
219
235
  options[:controller] ||= related_resource._type.to_s
220
236
  end
221
237
 
222
- match "#{formatted_relationship_name}", controller: options[:controller],
223
- relationship: relationship.name, source: resource_type_with_module_prefix(source._type),
224
- action: 'get_related_resource', via: [:get]
238
+ match formatted_relationship_name, controller: options[:controller],
239
+ relationship: relationship.name, source: resource_type_with_module_prefix(source._type),
240
+ action: 'show_related_resource', via: [:get],
241
+ as: "related/#{relationship_name}"
225
242
  end
226
243
 
227
244
  def jsonapi_related_resources(*relationship)
228
- source = JSONAPI::Resource.resource_for(resource_type_with_module_prefix)
245
+ source = JSONAPI::Resource.resource_klass_for(resource_type_with_module_prefix)
229
246
  options = relationship.extract_options!.dup
230
247
 
231
248
  relationship_name = relationship.first
232
249
  relationship = source._relationships[relationship_name]
233
250
 
251
+ relationship._routed = true
252
+
234
253
  formatted_relationship_name = format_route(relationship.name)
235
- related_resource = JSONAPI::Resource.resource_for(resource_type_with_module_prefix(relationship.class_name.underscore))
254
+ related_resource = JSONAPI::Resource.resource_klass_for(resource_type_with_module_prefix(relationship.class_name.underscore))
236
255
  options[:controller] ||= related_resource._type.to_s
237
256
 
238
- match "#{formatted_relationship_name}", controller: options[:controller],
239
- relationship: relationship.name, source: resource_type_with_module_prefix(source._type),
240
- action: 'get_related_resources', via: [:get]
257
+ match formatted_relationship_name,
258
+ controller: options[:controller],
259
+ relationship: relationship.name, source: resource_type_with_module_prefix(source._type),
260
+ action: 'index_related_resources', via: [:get],
261
+ as: "related/#{relationship_name}"
241
262
  end
242
263
 
243
264
  protected
@@ -250,6 +271,7 @@ module ActionDispatch
250
271
  @scope = @scope.parent
251
272
  end
252
273
  # :nocov:
274
+
253
275
  private
254
276
 
255
277
  def resource_type_with_module_prefix(resource = nil)
@@ -1,10 +1,19 @@
1
+ require 'jsonapi/resources/railtie'
1
2
  require 'jsonapi/naive_cache'
2
3
  require 'jsonapi/compiled_json'
4
+ require 'jsonapi/basic_resource'
5
+ require 'jsonapi/active_relation_resource'
3
6
  require 'jsonapi/resource'
4
- require 'jsonapi/cached_resource_fragment'
7
+ require 'jsonapi/cached_response_fragment'
5
8
  require 'jsonapi/response_document'
6
9
  require 'jsonapi/acts_as_resource_controller'
7
- require 'jsonapi/resource_controller'
10
+ if Rails::VERSION::MAJOR >= 6
11
+ ActiveSupport.on_load(:action_controller_base) do
12
+ require 'jsonapi/resource_controller'
13
+ end
14
+ else
15
+ require 'jsonapi/resource_controller'
16
+ end
8
17
  require 'jsonapi/resource_controller_metal'
9
18
  require 'jsonapi/resources/version'
10
19
  require 'jsonapi/configuration'
@@ -17,11 +26,18 @@ require 'jsonapi/exceptions'
17
26
  require 'jsonapi/error'
18
27
  require 'jsonapi/error_codes'
19
28
  require 'jsonapi/request_parser'
20
- require 'jsonapi/operation_dispatcher'
21
29
  require 'jsonapi/processor'
22
30
  require 'jsonapi/relationship'
23
31
  require 'jsonapi/include_directives'
32
+ require 'jsonapi/operation'
24
33
  require 'jsonapi/operation_result'
25
- require 'jsonapi/operation_results'
26
34
  require 'jsonapi/callbacks'
27
35
  require 'jsonapi/link_builder'
36
+ require 'jsonapi/active_relation/adapters/join_left_active_record_adapter'
37
+ require 'jsonapi/active_relation/join_manager'
38
+ require 'jsonapi/resource_identity'
39
+ require 'jsonapi/resource_fragment'
40
+ require 'jsonapi/resource_id_tree'
41
+ require 'jsonapi/resource_set'
42
+ require 'jsonapi/path'
43
+ require 'jsonapi/path_segment'