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.
- checksums.yaml +5 -5
- data/lib/jsonapi/{active_relation_resource_finder → active_relation}/adapters/join_left_active_record_adapter.rb +2 -2
- data/lib/jsonapi/active_relation/join_manager.rb +297 -0
- data/lib/jsonapi/{active_relation_resource_finder.rb → active_relation_resource.rb} +189 -146
- data/lib/jsonapi/acts_as_resource_controller.rb +4 -3
- data/lib/jsonapi/basic_resource.rb +1162 -0
- data/lib/jsonapi/cached_response_fragment.rb +66 -49
- data/lib/jsonapi/configuration.rb +18 -13
- data/lib/jsonapi/include_directives.rb +6 -8
- data/lib/jsonapi/link_builder.rb +71 -85
- data/lib/jsonapi/operation_result.rb +2 -2
- data/lib/jsonapi/path.rb +8 -0
- data/lib/jsonapi/path_segment.rb +17 -5
- data/lib/jsonapi/processor.rb +6 -7
- data/lib/jsonapi/relationship.rb +32 -3
- data/lib/jsonapi/request_parser.rb +44 -10
- data/lib/jsonapi/resource.rb +3 -1106
- data/lib/jsonapi/resource_controller_metal.rb +3 -0
- data/lib/jsonapi/resource_serializer.rb +39 -33
- data/lib/jsonapi/resource_set.rb +118 -48
- data/lib/jsonapi/resources/railtie.rb +9 -0
- data/lib/jsonapi/resources/version.rb +1 -1
- data/lib/jsonapi/response_document.rb +7 -2
- data/lib/jsonapi/routing_ext.rb +24 -4
- data/lib/jsonapi-resources.rb +12 -3
- data/lib/tasks/check_upgrade.rake +52 -0
- metadata +28 -12
- data/lib/jsonapi/active_relation_resource_finder/join_tree.rb +0 -227
|
@@ -10,6 +10,9 @@ module JSONAPI
|
|
|
10
10
|
JSONAPI::ActsAsResourceController
|
|
11
11
|
].freeze
|
|
12
12
|
|
|
13
|
+
# Note, the url_helpers are not loaded. This will prevent links from being generated for resources, and warnings
|
|
14
|
+
# will be emitted. Link support can be added by including `Rails.application.routes.url_helpers`, and links
|
|
15
|
+
# can be disabled, and warning suppressed, for a resource with `exclude_links :default`
|
|
13
16
|
MODULES.each do |mod|
|
|
14
17
|
include mod
|
|
15
18
|
end
|
|
@@ -59,7 +59,7 @@ module JSONAPI
|
|
|
59
59
|
end
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
-
fail "
|
|
62
|
+
fail "Too many primary objects for show" if (primary_objects.count > 1)
|
|
63
63
|
primary_hash = { 'data' => primary_objects[0] }
|
|
64
64
|
|
|
65
65
|
primary_hash['included'] = included_objects if included_objects.size > 0
|
|
@@ -93,24 +93,19 @@ module JSONAPI
|
|
|
93
93
|
return serialize_resource_set_to_hash_plural(resource_set)
|
|
94
94
|
end
|
|
95
95
|
|
|
96
|
-
def
|
|
96
|
+
def serialize_to_relationship_hash(source, requested_relationship, resource_ids)
|
|
97
97
|
if requested_relationship.is_a?(JSONAPI::Relationship::ToOne)
|
|
98
98
|
data = to_one_linkage(resource_ids[0])
|
|
99
99
|
else
|
|
100
100
|
data = to_many_linkage(resource_ids)
|
|
101
101
|
end
|
|
102
102
|
|
|
103
|
-
{
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
},
|
|
108
|
-
'data' => data
|
|
109
|
-
}
|
|
110
|
-
end
|
|
103
|
+
rel_hash = { 'data': data }
|
|
104
|
+
|
|
105
|
+
links = default_relationship_links(source, requested_relationship)
|
|
106
|
+
rel_hash['links'] = links unless links.blank?
|
|
111
107
|
|
|
112
|
-
|
|
113
|
-
link_builder.query_link(query_params)
|
|
108
|
+
rel_hash
|
|
114
109
|
end
|
|
115
110
|
|
|
116
111
|
def format_key(key)
|
|
@@ -136,11 +131,10 @@ module JSONAPI
|
|
|
136
131
|
def config_description(resource_klass)
|
|
137
132
|
{
|
|
138
133
|
class_name: self.class.name,
|
|
139
|
-
|
|
134
|
+
serialization_options: serialization_options.sort.map(&:as_json),
|
|
140
135
|
supplying_attribute_fields: supplying_attribute_fields(resource_klass).sort,
|
|
141
136
|
supplying_relationship_fields: supplying_relationship_fields(resource_klass).sort,
|
|
142
137
|
link_builder_base_url: link_builder.base_url,
|
|
143
|
-
route_formatter_class: link_builder.route_formatter.uncached.class.name,
|
|
144
138
|
key_formatter_class: key_formatter.uncached.class.name,
|
|
145
139
|
always_include_to_one_linkage_data: always_include_to_one_linkage_data,
|
|
146
140
|
always_include_to_many_linkage_data: always_include_to_many_linkage_data
|
|
@@ -165,7 +159,7 @@ module JSONAPI
|
|
|
165
159
|
obj_hash['attributes'] = source.attributes_json if source.attributes_json
|
|
166
160
|
|
|
167
161
|
relationships = cached_relationships_hash(source, fetchable_fields, relationship_data)
|
|
168
|
-
obj_hash['relationships'] = relationships unless relationships.
|
|
162
|
+
obj_hash['relationships'] = relationships unless relationships.blank?
|
|
169
163
|
|
|
170
164
|
obj_hash['meta'] = source.meta_json if source.meta_json
|
|
171
165
|
else
|
|
@@ -184,7 +178,7 @@ module JSONAPI
|
|
|
184
178
|
obj_hash['attributes'] = attributes unless attributes.empty?
|
|
185
179
|
|
|
186
180
|
relationships = relationships_hash(source, fetchable_fields, relationship_data)
|
|
187
|
-
obj_hash['relationships'] = relationships unless relationships.
|
|
181
|
+
obj_hash['relationships'] = relationships unless relationships.blank?
|
|
188
182
|
|
|
189
183
|
meta = meta_hash(source)
|
|
190
184
|
obj_hash['meta'] = meta unless meta.empty?
|
|
@@ -199,7 +193,7 @@ module JSONAPI
|
|
|
199
193
|
@_supplying_attribute_fields.fetch resource_klass do
|
|
200
194
|
attrs = Set.new(resource_klass._attributes.keys.map(&:to_sym))
|
|
201
195
|
cur = resource_klass
|
|
202
|
-
while cur
|
|
196
|
+
while !cur.root? # do not traverse beyond the first root resource
|
|
203
197
|
if @fields.has_key?(cur._type)
|
|
204
198
|
attrs &= @fields[cur._type]
|
|
205
199
|
break
|
|
@@ -214,7 +208,7 @@ module JSONAPI
|
|
|
214
208
|
@_supplying_relationship_fields.fetch resource_klass do
|
|
215
209
|
relationships = Set.new(resource_klass._relationships.keys.map(&:to_sym))
|
|
216
210
|
cur = resource_klass
|
|
217
|
-
while cur
|
|
211
|
+
while !cur.root? # do not traverse beyond the first root resource
|
|
218
212
|
if @fields.has_key?(cur._type)
|
|
219
213
|
relationships &= @fields[cur._type]
|
|
220
214
|
break
|
|
@@ -236,7 +230,7 @@ module JSONAPI
|
|
|
236
230
|
end
|
|
237
231
|
|
|
238
232
|
def custom_generation_options
|
|
239
|
-
{
|
|
233
|
+
@_custom_generation_options ||= {
|
|
240
234
|
serializer: self,
|
|
241
235
|
serialization_options: @serialization_options
|
|
242
236
|
}
|
|
@@ -249,7 +243,9 @@ module JSONAPI
|
|
|
249
243
|
|
|
250
244
|
def links_hash(source)
|
|
251
245
|
links = custom_links_hash(source)
|
|
252
|
-
links
|
|
246
|
+
if !links.key?('self') && !source.class.exclude_link?(:self)
|
|
247
|
+
links['self'] = link_builder.self_link(source)
|
|
248
|
+
end
|
|
253
249
|
links.compact
|
|
254
250
|
end
|
|
255
251
|
|
|
@@ -274,7 +270,8 @@ module JSONAPI
|
|
|
274
270
|
end
|
|
275
271
|
end
|
|
276
272
|
|
|
277
|
-
|
|
273
|
+
ro = relationship_object(source, relationship, rids, include_data)
|
|
274
|
+
hash[format_key(name)] = ro unless ro.blank?
|
|
278
275
|
end
|
|
279
276
|
end
|
|
280
277
|
end
|
|
@@ -323,6 +320,13 @@ module JSONAPI
|
|
|
323
320
|
link_builder.relationships_related_link(source, relationship)
|
|
324
321
|
end
|
|
325
322
|
|
|
323
|
+
def default_relationship_links(source, relationship)
|
|
324
|
+
links = {}
|
|
325
|
+
links['self'] = self_link(source, relationship) unless relationship.exclude_link?(:self)
|
|
326
|
+
links['related'] = related_link(source, relationship) unless relationship.exclude_link?(:related)
|
|
327
|
+
links.compact
|
|
328
|
+
end
|
|
329
|
+
|
|
326
330
|
def to_many_linkage(rids)
|
|
327
331
|
linkage = []
|
|
328
332
|
|
|
@@ -346,37 +350,39 @@ module JSONAPI
|
|
|
346
350
|
}
|
|
347
351
|
end
|
|
348
352
|
|
|
349
|
-
def
|
|
353
|
+
def relationship_object_to_one(source, relationship, rid, include_data)
|
|
350
354
|
link_object_hash = {}
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
355
|
+
|
|
356
|
+
links = default_relationship_links(source, relationship)
|
|
357
|
+
|
|
358
|
+
link_object_hash['links'] = links unless links.blank?
|
|
354
359
|
link_object_hash['data'] = to_one_linkage(rid) if include_data
|
|
355
360
|
link_object_hash
|
|
356
361
|
end
|
|
357
362
|
|
|
358
|
-
def
|
|
363
|
+
def relationship_object_to_many(source, relationship, rids, include_data)
|
|
359
364
|
link_object_hash = {}
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
link_object_hash['links']
|
|
365
|
+
|
|
366
|
+
links = default_relationship_links(source, relationship)
|
|
367
|
+
link_object_hash['links'] = links unless links.blank?
|
|
363
368
|
link_object_hash['data'] = to_many_linkage(rids) if include_data
|
|
364
369
|
link_object_hash
|
|
365
370
|
end
|
|
366
371
|
|
|
367
|
-
def
|
|
372
|
+
def relationship_object(source, relationship, rid, include_data)
|
|
368
373
|
if relationship.is_a?(JSONAPI::Relationship::ToOne)
|
|
369
|
-
|
|
374
|
+
relationship_object_to_one(source, relationship, rid, include_data)
|
|
370
375
|
elsif relationship.is_a?(JSONAPI::Relationship::ToMany)
|
|
371
|
-
|
|
376
|
+
relationship_object_to_many(source, relationship, rid, include_data)
|
|
372
377
|
end
|
|
373
378
|
end
|
|
374
379
|
|
|
375
380
|
def generate_link_builder(primary_resource_klass, options)
|
|
376
381
|
LinkBuilder.new(
|
|
377
382
|
base_url: options.fetch(:base_url, ''),
|
|
378
|
-
route_formatter: options.fetch(:route_formatter, JSONAPI.configuration.route_formatter),
|
|
379
383
|
primary_resource_klass: primary_resource_klass,
|
|
384
|
+
route_formatter: options.fetch(:route_formatter, JSONAPI.configuration.route_formatter),
|
|
385
|
+
url_helpers: options.fetch(:url_helpers, options[:controller]),
|
|
380
386
|
)
|
|
381
387
|
end
|
|
382
388
|
end
|
data/lib/jsonapi/resource_set.rb
CHANGED
|
@@ -5,14 +5,25 @@ module JSONAPI
|
|
|
5
5
|
|
|
6
6
|
attr_reader :resource_klasses, :populated
|
|
7
7
|
|
|
8
|
-
def initialize(resource_id_tree)
|
|
8
|
+
def initialize(resource_id_tree = nil)
|
|
9
9
|
@populated = false
|
|
10
|
-
@resource_klasses = flatten_resource_id_tree(resource_id_tree)
|
|
10
|
+
@resource_klasses = resource_id_tree.nil? ? {} : flatten_resource_id_tree(resource_id_tree)
|
|
11
11
|
end
|
|
12
12
|
|
|
13
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
|
|
14
26
|
@resource_klasses.each_key do |resource_klass|
|
|
15
|
-
missed_ids = []
|
|
16
27
|
|
|
17
28
|
serializer_config_key = serializer.config_key(resource_klass).gsub("/", "_")
|
|
18
29
|
context_json = resource_klass.attribute_caching_context(context).to_json
|
|
@@ -20,65 +31,124 @@ module JSONAPI
|
|
|
20
31
|
context_key = "ATTR-CTX-#{context_b64.gsub("/", "_")}"
|
|
21
32
|
|
|
22
33
|
if resource_klass.caching?
|
|
23
|
-
cache_ids = []
|
|
24
|
-
|
|
25
|
-
@resource_klasses[resource_klass].each_pair do |k, v|
|
|
34
|
+
cache_ids = @resource_klasses[resource_klass].map do |(k, v)|
|
|
26
35
|
# Store the hashcode of the cache_field to avoid storing objects and to ensure precision isn't lost
|
|
27
36
|
# on timestamp types (i.e. string conversions dropping milliseconds)
|
|
28
|
-
|
|
37
|
+
[k, resource_klass.hash_cache_field(v[:cache_id])]
|
|
29
38
|
end
|
|
30
39
|
|
|
31
|
-
|
|
40
|
+
lookups.push(
|
|
41
|
+
CachedResponseFragment::Lookup.new(
|
|
32
42
|
resource_klass,
|
|
33
43
|
serializer_config_key,
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if resource.nil?
|
|
40
|
-
missed_ids.push(found_result[0])
|
|
41
|
-
else
|
|
42
|
-
@resource_klasses[resource_klass][resource.id][:resource] = resource
|
|
43
|
-
end
|
|
44
|
-
end
|
|
44
|
+
context,
|
|
45
|
+
context_key,
|
|
46
|
+
cache_ids
|
|
47
|
+
)
|
|
48
|
+
)
|
|
45
49
|
else
|
|
46
|
-
|
|
50
|
+
missed_resource_ids[resource_klass] ||= {}
|
|
51
|
+
missed_resource_ids[resource_klass] = @resource_klasses[resource_klass].keys
|
|
47
52
|
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
if lookups.any?
|
|
56
|
+
raise "You've declared some Resources as caching without providing a caching store" if JSONAPI.configuration.resource_cache.nil?
|
|
57
|
+
|
|
58
|
+
# Step Two execute the cache lookup
|
|
59
|
+
found_resources = CachedResponseFragment.lookup(lookups, context)
|
|
60
|
+
else
|
|
61
|
+
found_resources = {}
|
|
62
|
+
end
|
|
48
63
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
relationship_data)
|
|
69
|
-
|
|
70
|
-
@resource_klasses[resource_klass][id][:resource] = cr
|
|
71
|
-
else
|
|
72
|
-
@resource_klasses[resource_klass][resource.id][:resource] = resource
|
|
73
|
-
end
|
|
64
|
+
|
|
65
|
+
# Step Three collect the results and collect hit/miss stats
|
|
66
|
+
stats = {}
|
|
67
|
+
found_resources.each do |resource_klass, resources|
|
|
68
|
+
resources.each do |id, cached_resource|
|
|
69
|
+
stats[resource_klass] ||= {}
|
|
70
|
+
|
|
71
|
+
if cached_resource.nil?
|
|
72
|
+
stats[resource_klass][:misses] ||= 0
|
|
73
|
+
stats[resource_klass][:misses] += 1
|
|
74
|
+
|
|
75
|
+
# Collect misses
|
|
76
|
+
missed_resource_ids[resource_klass] ||= []
|
|
77
|
+
missed_resource_ids[resource_klass].push(id)
|
|
78
|
+
else
|
|
79
|
+
stats[resource_klass][:hits] ||= 0
|
|
80
|
+
stats[resource_klass][:hits] += 1
|
|
81
|
+
|
|
82
|
+
register_resource(resource_klass, cached_resource)
|
|
74
83
|
end
|
|
75
84
|
end
|
|
76
85
|
end
|
|
77
|
-
|
|
86
|
+
|
|
87
|
+
report_stats(stats)
|
|
88
|
+
|
|
89
|
+
writes = []
|
|
90
|
+
|
|
91
|
+
# Step Four find any of the missing resources and join them into the result
|
|
92
|
+
missed_resource_ids.each_pair do |resource_klass, ids|
|
|
93
|
+
find_opts = {context: context, fields: find_options[:fields]}
|
|
94
|
+
found_resources = resource_klass.find_to_populate_by_keys(ids, find_opts)
|
|
95
|
+
|
|
96
|
+
found_resources.each do |resource|
|
|
97
|
+
relationship_data = @resource_klasses[resource_klass][resource.id][:relationships]
|
|
98
|
+
|
|
99
|
+
if resource_klass.caching?
|
|
100
|
+
|
|
101
|
+
serializer_config_key = serializer.config_key(resource_klass).gsub("/", "_")
|
|
102
|
+
context_json = resource_klass.attribute_caching_context(context).to_json
|
|
103
|
+
context_b64 = JSONAPI.configuration.resource_cache_digest_function.call(context_json)
|
|
104
|
+
context_key = "ATTR-CTX-#{context_b64.gsub("/", "_")}"
|
|
105
|
+
|
|
106
|
+
writes.push(CachedResponseFragment::Write.new(
|
|
107
|
+
resource_klass,
|
|
108
|
+
resource,
|
|
109
|
+
serializer,
|
|
110
|
+
serializer_config_key,
|
|
111
|
+
context,
|
|
112
|
+
context_key,
|
|
113
|
+
relationship_data
|
|
114
|
+
))
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
register_resource(resource_klass, resource)
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Step Five conditionally write to the cache
|
|
122
|
+
CachedResponseFragment.write(writes) unless JSONAPI.configuration.resource_cache.nil?
|
|
123
|
+
|
|
124
|
+
mark_populated!
|
|
78
125
|
self
|
|
79
126
|
end
|
|
80
127
|
|
|
128
|
+
def mark_populated!
|
|
129
|
+
@populated = true
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def register_resource(resource_klass, resource, primary = false)
|
|
133
|
+
@resource_klasses[resource_klass] ||= {}
|
|
134
|
+
@resource_klasses[resource_klass][resource.id] ||= {primary: resource.try(:primary) || primary, relationships: {}}
|
|
135
|
+
@resource_klasses[resource_klass][resource.id][:resource] = resource
|
|
136
|
+
end
|
|
137
|
+
|
|
81
138
|
private
|
|
139
|
+
|
|
140
|
+
def report_stats(stats)
|
|
141
|
+
return unless JSONAPI.configuration.resource_cache_usage_report_function || JSONAPI.configuration.resource_cache.nil?
|
|
142
|
+
|
|
143
|
+
stats.each_pair do |resource_klass, stat|
|
|
144
|
+
JSONAPI.configuration.resource_cache_usage_report_function.call(
|
|
145
|
+
resource_klass.name,
|
|
146
|
+
stat[:hits] || 0,
|
|
147
|
+
stat[:misses] || 0
|
|
148
|
+
)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
82
152
|
def flatten_resource_id_tree(resource_id_tree, flattened_tree = {})
|
|
83
153
|
resource_id_tree.fragments.each_pair do |resource_rid, fragment|
|
|
84
154
|
|
|
@@ -87,7 +157,7 @@ module JSONAPI
|
|
|
87
157
|
|
|
88
158
|
flattened_tree[resource_klass] ||= {}
|
|
89
159
|
|
|
90
|
-
flattened_tree[resource_klass][id] ||= {
|
|
160
|
+
flattened_tree[resource_klass][id] ||= {primary: fragment.primary, relationships: {}}
|
|
91
161
|
flattened_tree[resource_klass][id][:cache_id] ||= fragment.cache
|
|
92
162
|
|
|
93
163
|
fragment.related.try(:each_pair) do |relationship_name, related_rids|
|
|
@@ -104,4 +174,4 @@ module JSONAPI
|
|
|
104
174
|
flattened_tree
|
|
105
175
|
end
|
|
106
176
|
end
|
|
107
|
-
end
|
|
177
|
+
end
|
|
@@ -118,10 +118,15 @@ module JSONAPI
|
|
|
118
118
|
result.pagination_params.each_pair do |link_name, params|
|
|
119
119
|
if result.is_a?(JSONAPI::RelatedResourcesSetOperationResult)
|
|
120
120
|
relationship = result.source_resource.class._relationships[result._type.to_sym]
|
|
121
|
-
|
|
121
|
+
unless relationship.exclude_link?(link_name)
|
|
122
|
+
link = serializer.link_builder.relationships_related_link(result.source_resource, relationship, query_params(params))
|
|
123
|
+
end
|
|
122
124
|
else
|
|
123
|
-
|
|
125
|
+
unless serializer.link_builder.primary_resource_klass.exclude_link?(link_name)
|
|
126
|
+
link = serializer.link_builder.query_link(query_params(params))
|
|
127
|
+
end
|
|
124
128
|
end
|
|
129
|
+
@top_level_links[link_name] = link unless link.blank?
|
|
125
130
|
end
|
|
126
131
|
end
|
|
127
132
|
end
|
data/lib/jsonapi/routing_ext.rb
CHANGED
|
@@ -20,6 +20,12 @@ module ActionDispatch
|
|
|
20
20
|
@resource_type = resources.first
|
|
21
21
|
res = JSONAPI::Resource.resource_klass_for(resource_type_with_module_prefix(@resource_type))
|
|
22
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
|
|
28
|
+
|
|
23
29
|
options = resources.extract_options!.dup
|
|
24
30
|
options[:controller] ||= @resource_type
|
|
25
31
|
options.merge!(res.routing_resource_options)
|
|
@@ -80,6 +86,12 @@ module ActionDispatch
|
|
|
80
86
|
@resource_type = resources.first
|
|
81
87
|
res = JSONAPI::Resource.resource_klass_for(resource_type_with_module_prefix(@resource_type))
|
|
82
88
|
|
|
89
|
+
res._routed = true
|
|
90
|
+
|
|
91
|
+
if res.singleton?
|
|
92
|
+
warn "Singleton resource #{res} should use `jsonapi_resource` instead."
|
|
93
|
+
end
|
|
94
|
+
|
|
83
95
|
options = resources.extract_options!.dup
|
|
84
96
|
options[:controller] ||= @resource_type
|
|
85
97
|
options.merge!(res.routing_resource_options)
|
|
@@ -154,7 +166,8 @@ module ActionDispatch
|
|
|
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?
|
|
@@ -182,7 +195,8 @@ module ActionDispatch
|
|
|
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?
|
|
@@ -210,6 +224,8 @@ module ActionDispatch
|
|
|
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?
|
|
@@ -221,7 +237,8 @@ module ActionDispatch
|
|
|
221
237
|
|
|
222
238
|
match formatted_relationship_name, controller: options[:controller],
|
|
223
239
|
relationship: relationship.name, source: resource_type_with_module_prefix(source._type),
|
|
224
|
-
action: 'show_related_resource', via: [:get]
|
|
240
|
+
action: 'show_related_resource', via: [:get],
|
|
241
|
+
as: "related/#{relationship_name}"
|
|
225
242
|
end
|
|
226
243
|
|
|
227
244
|
def jsonapi_related_resources(*relationship)
|
|
@@ -231,6 +248,8 @@ module ActionDispatch
|
|
|
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
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
|
|
@@ -238,7 +257,8 @@ module ActionDispatch
|
|
|
238
257
|
match formatted_relationship_name,
|
|
239
258
|
controller: options[:controller],
|
|
240
259
|
relationship: relationship.name, source: resource_type_with_module_prefix(source._type),
|
|
241
|
-
action: 'index_related_resources', via: [:get]
|
|
260
|
+
action: 'index_related_resources', via: [:get],
|
|
261
|
+
as: "related/#{relationship_name}"
|
|
242
262
|
end
|
|
243
263
|
|
|
244
264
|
protected
|
data/lib/jsonapi-resources.rb
CHANGED
|
@@ -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
7
|
require 'jsonapi/cached_response_fragment'
|
|
5
8
|
require 'jsonapi/response_document'
|
|
6
9
|
require 'jsonapi/acts_as_resource_controller'
|
|
7
|
-
|
|
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'
|
|
@@ -24,8 +33,8 @@ require 'jsonapi/operation'
|
|
|
24
33
|
require 'jsonapi/operation_result'
|
|
25
34
|
require 'jsonapi/callbacks'
|
|
26
35
|
require 'jsonapi/link_builder'
|
|
27
|
-
require 'jsonapi/
|
|
28
|
-
require 'jsonapi/
|
|
36
|
+
require 'jsonapi/active_relation/adapters/join_left_active_record_adapter'
|
|
37
|
+
require 'jsonapi/active_relation/join_manager'
|
|
29
38
|
require 'jsonapi/resource_identity'
|
|
30
39
|
require 'jsonapi/resource_fragment'
|
|
31
40
|
require 'jsonapi/resource_id_tree'
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
require 'rake'
|
|
2
|
+
require 'jsonapi-resources'
|
|
3
|
+
|
|
4
|
+
namespace :jsonapi do
|
|
5
|
+
namespace :resources do
|
|
6
|
+
desc 'Checks application for orphaned overrides'
|
|
7
|
+
task :check_upgrade => :environment do
|
|
8
|
+
Rails.application.eager_load!
|
|
9
|
+
|
|
10
|
+
resource_klasses = ObjectSpace.each_object(Class).select { |klass| klass < JSONAPI::Resource}
|
|
11
|
+
|
|
12
|
+
puts "Checking #{resource_klasses.count} resources"
|
|
13
|
+
|
|
14
|
+
issues_found = 0
|
|
15
|
+
|
|
16
|
+
klasses_with_deprecated = resource_klasses.select { |klass| klass.methods.include?(:find_records) }
|
|
17
|
+
unless klasses_with_deprecated.empty?
|
|
18
|
+
puts " Found the following resources the still implement `find_records`:"
|
|
19
|
+
klasses_with_deprecated.each { |klass| puts " #{klass}"}
|
|
20
|
+
puts " The `find_records` method is no longer called by JR. Please review and ensure your functionality is ported over."
|
|
21
|
+
|
|
22
|
+
issues_found = issues_found + klasses_with_deprecated.length
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
klasses_with_deprecated = resource_klasses.select { |klass| klass.methods.include?(:records_for) }
|
|
26
|
+
unless klasses_with_deprecated.empty?
|
|
27
|
+
puts " Found the following resources the still implement `records_for`:"
|
|
28
|
+
klasses_with_deprecated.each { |klass| puts " #{klass}"}
|
|
29
|
+
puts " The `records_for` method is no longer called by JR. Please review and ensure your functionality is ported over."
|
|
30
|
+
|
|
31
|
+
issues_found = issues_found + klasses_with_deprecated.length
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
klasses_with_deprecated = resource_klasses.select { |klass| klass.methods.include?(:apply_includes) }
|
|
35
|
+
unless klasses_with_deprecated.empty?
|
|
36
|
+
puts " Found the following resources the still implement `apply_includes`:"
|
|
37
|
+
klasses_with_deprecated.each { |klass| puts " #{klass}"}
|
|
38
|
+
puts " The `apply_includes` method is no longer called by JR. Please review and ensure your functionality is ported over."
|
|
39
|
+
|
|
40
|
+
issues_found = issues_found + klasses_with_deprecated.length
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
if issues_found > 0
|
|
44
|
+
puts "Finished inspection. #{issues_found} issues found that may impact upgrading. Please address these issues. "
|
|
45
|
+
else
|
|
46
|
+
puts "Finished inspection with no issues found. Note this is only a cursory check for method overrides that will no \n" \
|
|
47
|
+
"longer be called by JSONAPI::Resources. This check in no way assures your code will continue to function as \n" \
|
|
48
|
+
"it did before the upgrade. Please do adequate testing before using in production."
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|