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.
- checksums.yaml +5 -5
- data/LICENSE.txt +1 -1
- data/README.md +34 -11
- data/lib/bug_report_templates/rails_5_latest.rb +125 -0
- data/lib/bug_report_templates/rails_5_master.rb +140 -0
- data/lib/jsonapi/active_relation/adapters/join_left_active_record_adapter.rb +27 -0
- data/lib/jsonapi/active_relation/join_manager.rb +303 -0
- data/lib/jsonapi/active_relation_resource.rb +884 -0
- data/lib/jsonapi/acts_as_resource_controller.rb +122 -105
- data/lib/jsonapi/basic_resource.rb +1162 -0
- data/lib/jsonapi/cached_response_fragment.rb +127 -0
- data/lib/jsonapi/compiled_json.rb +11 -1
- data/lib/jsonapi/configuration.rb +71 -8
- data/lib/jsonapi/error.rb +27 -0
- data/lib/jsonapi/error_codes.rb +2 -0
- data/lib/jsonapi/exceptions.rb +80 -50
- data/lib/jsonapi/formatter.rb +3 -3
- data/lib/jsonapi/include_directives.rb +18 -65
- data/lib/jsonapi/link_builder.rb +74 -80
- data/lib/jsonapi/operation.rb +16 -5
- data/lib/jsonapi/operation_result.rb +74 -16
- data/lib/jsonapi/path.rb +43 -0
- data/lib/jsonapi/path_segment.rb +76 -0
- data/lib/jsonapi/processor.rb +239 -111
- data/lib/jsonapi/relationship.rb +153 -15
- data/lib/jsonapi/request_parser.rb +430 -367
- data/lib/jsonapi/resource.rb +3 -1253
- data/lib/jsonapi/resource_controller_metal.rb +5 -2
- data/lib/jsonapi/resource_fragment.rb +47 -0
- data/lib/jsonapi/resource_id_tree.rb +112 -0
- data/lib/jsonapi/resource_identity.rb +42 -0
- data/lib/jsonapi/resource_serializer.rb +143 -285
- data/lib/jsonapi/resource_set.rb +176 -0
- data/lib/jsonapi/resources/railtie.rb +9 -0
- data/lib/jsonapi/resources/version.rb +1 -1
- data/lib/jsonapi/response_document.rb +105 -83
- data/lib/jsonapi/routing_ext.rb +48 -26
- data/lib/jsonapi-resources.rb +20 -4
- data/lib/tasks/check_upgrade.rake +52 -0
- metadata +50 -20
- data/lib/jsonapi/cached_resource_fragment.rb +0 -127
- data/lib/jsonapi/operation_dispatcher.rb +0 -88
- data/lib/jsonapi/operation_results.rb +0 -35
- data/lib/jsonapi/relationship_builder.rb +0 -167
@@ -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
|
metadata
CHANGED
@@ -1,30 +1,30 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jsonapi-resources
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.10.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dan Gebhardt
|
8
8
|
- Larry Gebhardt
|
9
|
-
autorequire:
|
9
|
+
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2022-02-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
|
-
- - "
|
18
|
+
- - ">="
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version: '1.
|
20
|
+
version: '1.17'
|
21
21
|
type: :development
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
|
-
- - "
|
25
|
+
- - ">="
|
26
26
|
- !ruby/object:Gem::Version
|
27
|
-
version: '1.
|
27
|
+
version: '1.17'
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
29
|
name: rake
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
@@ -43,16 +43,22 @@ dependencies:
|
|
43
43
|
name: minitest
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
45
45
|
requirements:
|
46
|
-
- - "
|
46
|
+
- - "~>"
|
47
47
|
- !ruby/object:Gem::Version
|
48
|
-
version: '
|
48
|
+
version: '5.10'
|
49
|
+
- - "!="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: 5.10.2
|
49
52
|
type: :development
|
50
53
|
prerelease: false
|
51
54
|
version_requirements: !ruby/object:Gem::Requirement
|
52
55
|
requirements:
|
53
|
-
- - "
|
56
|
+
- - "~>"
|
54
57
|
- !ruby/object:Gem::Version
|
55
|
-
version: '
|
58
|
+
version: '5.10'
|
59
|
+
- - "!="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 5.10.2
|
56
62
|
- !ruby/object:Gem::Dependency
|
57
63
|
name: minitest-spec-rails
|
58
64
|
requirement: !ruby/object:Gem::Requirement
|
@@ -109,6 +115,20 @@ dependencies:
|
|
109
115
|
- - ">="
|
110
116
|
- !ruby/object:Gem::Version
|
111
117
|
version: '0'
|
118
|
+
- !ruby/object:Gem::Dependency
|
119
|
+
name: database_cleaner
|
120
|
+
requirement: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
type: :development
|
126
|
+
prerelease: false
|
127
|
+
version_requirements: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
112
132
|
- !ruby/object:Gem::Dependency
|
113
133
|
name: activerecord
|
114
134
|
requirement: !ruby/object:Gem::Requirement
|
@@ -162,14 +182,20 @@ extra_rdoc_files: []
|
|
162
182
|
files:
|
163
183
|
- LICENSE.txt
|
164
184
|
- README.md
|
185
|
+
- lib/bug_report_templates/rails_5_latest.rb
|
186
|
+
- lib/bug_report_templates/rails_5_master.rb
|
165
187
|
- lib/generators/jsonapi/USAGE
|
166
188
|
- lib/generators/jsonapi/controller_generator.rb
|
167
189
|
- lib/generators/jsonapi/resource_generator.rb
|
168
190
|
- lib/generators/jsonapi/templates/jsonapi_controller.rb
|
169
191
|
- lib/generators/jsonapi/templates/jsonapi_resource.rb
|
170
192
|
- lib/jsonapi-resources.rb
|
193
|
+
- lib/jsonapi/active_relation/adapters/join_left_active_record_adapter.rb
|
194
|
+
- lib/jsonapi/active_relation/join_manager.rb
|
195
|
+
- lib/jsonapi/active_relation_resource.rb
|
171
196
|
- lib/jsonapi/acts_as_resource_controller.rb
|
172
|
-
- lib/jsonapi/
|
197
|
+
- lib/jsonapi/basic_resource.rb
|
198
|
+
- lib/jsonapi/cached_response_fragment.rb
|
173
199
|
- lib/jsonapi/callbacks.rb
|
174
200
|
- lib/jsonapi/compiled_json.rb
|
175
201
|
- lib/jsonapi/configuration.rb
|
@@ -182,26 +208,31 @@ files:
|
|
182
208
|
- lib/jsonapi/mime_types.rb
|
183
209
|
- lib/jsonapi/naive_cache.rb
|
184
210
|
- lib/jsonapi/operation.rb
|
185
|
-
- lib/jsonapi/operation_dispatcher.rb
|
186
211
|
- lib/jsonapi/operation_result.rb
|
187
|
-
- lib/jsonapi/operation_results.rb
|
188
212
|
- lib/jsonapi/paginator.rb
|
213
|
+
- lib/jsonapi/path.rb
|
214
|
+
- lib/jsonapi/path_segment.rb
|
189
215
|
- lib/jsonapi/processor.rb
|
190
216
|
- lib/jsonapi/relationship.rb
|
191
|
-
- lib/jsonapi/relationship_builder.rb
|
192
217
|
- lib/jsonapi/request_parser.rb
|
193
218
|
- lib/jsonapi/resource.rb
|
194
219
|
- lib/jsonapi/resource_controller.rb
|
195
220
|
- lib/jsonapi/resource_controller_metal.rb
|
221
|
+
- lib/jsonapi/resource_fragment.rb
|
222
|
+
- lib/jsonapi/resource_id_tree.rb
|
223
|
+
- lib/jsonapi/resource_identity.rb
|
196
224
|
- lib/jsonapi/resource_serializer.rb
|
225
|
+
- lib/jsonapi/resource_set.rb
|
226
|
+
- lib/jsonapi/resources/railtie.rb
|
197
227
|
- lib/jsonapi/resources/version.rb
|
198
228
|
- lib/jsonapi/response_document.rb
|
199
229
|
- lib/jsonapi/routing_ext.rb
|
230
|
+
- lib/tasks/check_upgrade.rake
|
200
231
|
homepage: https://github.com/cerebris/jsonapi-resources
|
201
232
|
licenses:
|
202
233
|
- MIT
|
203
234
|
metadata: {}
|
204
|
-
post_install_message:
|
235
|
+
post_install_message:
|
205
236
|
rdoc_options: []
|
206
237
|
require_paths:
|
207
238
|
- lib
|
@@ -209,16 +240,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
209
240
|
requirements:
|
210
241
|
- - ">="
|
211
242
|
- !ruby/object:Gem::Version
|
212
|
-
version: '2.
|
243
|
+
version: '2.3'
|
213
244
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
214
245
|
requirements:
|
215
246
|
- - ">="
|
216
247
|
- !ruby/object:Gem::Version
|
217
248
|
version: '0'
|
218
249
|
requirements: []
|
219
|
-
|
220
|
-
|
221
|
-
signing_key:
|
250
|
+
rubygems_version: 3.1.6
|
251
|
+
signing_key:
|
222
252
|
specification_version: 4
|
223
253
|
summary: Easily support JSON API in Rails.
|
224
254
|
test_files: []
|
@@ -1,127 +0,0 @@
|
|
1
|
-
module JSONAPI
|
2
|
-
class CachedResourceFragment
|
3
|
-
def self.fetch_fragments(resource_klass, serializer, context, cache_ids)
|
4
|
-
serializer_config_key = serializer.config_key(resource_klass).gsub("/", "_")
|
5
|
-
context_json = resource_klass.attribute_caching_context(context).to_json
|
6
|
-
context_b64 = JSONAPI.configuration.resource_cache_digest_function.call(context_json)
|
7
|
-
context_key = "ATTR-CTX-#{context_b64.gsub("/", "_")}"
|
8
|
-
|
9
|
-
results = self.lookup(resource_klass, serializer_config_key, context, context_key, cache_ids)
|
10
|
-
|
11
|
-
miss_ids = results.select{|k,v| v.nil? }.keys
|
12
|
-
unless miss_ids.empty?
|
13
|
-
find_filters = {resource_klass._primary_key => miss_ids.uniq}
|
14
|
-
find_options = {context: context}
|
15
|
-
resource_klass.find(find_filters, find_options).each do |resource|
|
16
|
-
(id, cr) = write(resource_klass, resource, serializer, serializer_config_key, context, context_key)
|
17
|
-
results[id] = cr
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
if JSONAPI.configuration.resource_cache_usage_report_function
|
22
|
-
JSONAPI.configuration.resource_cache_usage_report_function.call(
|
23
|
-
resource_klass.name,
|
24
|
-
cache_ids.size - miss_ids.size,
|
25
|
-
miss_ids.size
|
26
|
-
)
|
27
|
-
end
|
28
|
-
|
29
|
-
return results
|
30
|
-
end
|
31
|
-
|
32
|
-
attr_reader :resource_klass, :id, :type, :context, :fetchable_fields, :relationships,
|
33
|
-
:links_json, :attributes_json, :meta_json,
|
34
|
-
:preloaded_fragments
|
35
|
-
|
36
|
-
def initialize(resource_klass, id, type, context, fetchable_fields, relationships,
|
37
|
-
links_json, attributes_json, meta_json)
|
38
|
-
@resource_klass = resource_klass
|
39
|
-
@id = id
|
40
|
-
@type = type
|
41
|
-
@context = context
|
42
|
-
@fetchable_fields = Set.new(fetchable_fields)
|
43
|
-
|
44
|
-
# Relationships left uncompiled because we'll often want to insert included ids on retrieval
|
45
|
-
@relationships = relationships
|
46
|
-
|
47
|
-
@links_json = CompiledJson.of(links_json)
|
48
|
-
@attributes_json = CompiledJson.of(attributes_json)
|
49
|
-
@meta_json = CompiledJson.of(meta_json)
|
50
|
-
|
51
|
-
# A hash of hashes
|
52
|
-
@preloaded_fragments ||= Hash.new
|
53
|
-
end
|
54
|
-
|
55
|
-
def to_cache_value
|
56
|
-
{
|
57
|
-
id: id,
|
58
|
-
type: type,
|
59
|
-
fetchable: fetchable_fields,
|
60
|
-
rels: relationships,
|
61
|
-
links: links_json.try(:to_s),
|
62
|
-
attrs: attributes_json.try(:to_s),
|
63
|
-
meta: meta_json.try(:to_s)
|
64
|
-
}
|
65
|
-
end
|
66
|
-
|
67
|
-
def to_real_resource
|
68
|
-
rs = Resource.resource_for(self.type).find_by_keys([self.id], {context: self.context})
|
69
|
-
return rs.try(:first)
|
70
|
-
end
|
71
|
-
|
72
|
-
private
|
73
|
-
|
74
|
-
def self.lookup(resource_klass, serializer_config_key, context, context_key, cache_ids)
|
75
|
-
type = resource_klass._type
|
76
|
-
|
77
|
-
keys = cache_ids.map do |(id, cache_key)|
|
78
|
-
[type, id, cache_key, serializer_config_key, context_key]
|
79
|
-
end
|
80
|
-
|
81
|
-
hits = JSONAPI.configuration.resource_cache.read_multi(*keys).reject{|_,v| v.nil? }
|
82
|
-
return keys.each_with_object({}) do |key, hash|
|
83
|
-
(_, id, _, _) = key
|
84
|
-
if hits.has_key?(key)
|
85
|
-
hash[id] = self.from_cache_value(resource_klass, context, hits[key])
|
86
|
-
else
|
87
|
-
hash[id] = nil
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
def self.from_cache_value(resource_klass, context, h)
|
93
|
-
new(
|
94
|
-
resource_klass,
|
95
|
-
h.fetch(:id),
|
96
|
-
h.fetch(:type),
|
97
|
-
context,
|
98
|
-
h.fetch(:fetchable),
|
99
|
-
h.fetch(:rels, nil),
|
100
|
-
h.fetch(:links, nil),
|
101
|
-
h.fetch(:attrs, nil),
|
102
|
-
h.fetch(:meta, nil)
|
103
|
-
)
|
104
|
-
end
|
105
|
-
|
106
|
-
def self.write(resource_klass, resource, serializer, serializer_config_key, context, context_key)
|
107
|
-
(id, cache_key) = resource.cache_id
|
108
|
-
json = serializer.object_hash(resource) # No inclusions passed to object_hash
|
109
|
-
cr = self.new(
|
110
|
-
resource_klass,
|
111
|
-
json['id'],
|
112
|
-
json['type'],
|
113
|
-
context,
|
114
|
-
resource.fetchable_fields,
|
115
|
-
json['relationships'],
|
116
|
-
json['links'],
|
117
|
-
json['attributes'],
|
118
|
-
json['meta']
|
119
|
-
)
|
120
|
-
|
121
|
-
key = [resource_klass._type, id, cache_key, serializer_config_key, context_key]
|
122
|
-
JSONAPI.configuration.resource_cache.write(key, cr.to_cache_value)
|
123
|
-
return [id, cr]
|
124
|
-
end
|
125
|
-
|
126
|
-
end
|
127
|
-
end
|
@@ -1,88 +0,0 @@
|
|
1
|
-
module JSONAPI
|
2
|
-
class OperationDispatcher
|
3
|
-
|
4
|
-
def initialize(transaction: lambda { |&block| block.yield },
|
5
|
-
rollback: lambda { },
|
6
|
-
server_error_callbacks: [])
|
7
|
-
|
8
|
-
@transaction = transaction
|
9
|
-
@rollback = rollback
|
10
|
-
@server_error_callbacks = server_error_callbacks
|
11
|
-
end
|
12
|
-
|
13
|
-
def process(operations)
|
14
|
-
results = JSONAPI::OperationResults.new
|
15
|
-
|
16
|
-
# Use transactions if more than one operation and if one of the operations can be transactional
|
17
|
-
# Even if transactional transactions won't be used unless the derived OperationsProcessor supports them.
|
18
|
-
transactional = false
|
19
|
-
|
20
|
-
operations.each do |operation|
|
21
|
-
transactional |= operation.transactional?
|
22
|
-
end if JSONAPI.configuration.allow_transactions
|
23
|
-
|
24
|
-
transaction(transactional) do
|
25
|
-
# Links and meta data global to the set of operations
|
26
|
-
operations_meta = {}
|
27
|
-
operations_links = {}
|
28
|
-
operations.each do |operation|
|
29
|
-
results.add_result(process_operation(operation))
|
30
|
-
rollback(transactional) if results.has_errors?
|
31
|
-
end
|
32
|
-
results.meta = operations_meta
|
33
|
-
results.links = operations_links
|
34
|
-
end
|
35
|
-
results
|
36
|
-
end
|
37
|
-
|
38
|
-
private
|
39
|
-
|
40
|
-
def transaction(transactional)
|
41
|
-
if transactional
|
42
|
-
@transaction.call do
|
43
|
-
yield
|
44
|
-
end
|
45
|
-
else
|
46
|
-
yield
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def rollback(transactional)
|
51
|
-
if transactional
|
52
|
-
@rollback.call
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def process_operation(operation)
|
57
|
-
with_default_handling do
|
58
|
-
operation.process
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def with_default_handling(&block)
|
63
|
-
block.yield
|
64
|
-
rescue => e
|
65
|
-
if JSONAPI.configuration.exception_class_whitelisted?(e)
|
66
|
-
raise e
|
67
|
-
else
|
68
|
-
@server_error_callbacks.each { |callback|
|
69
|
-
safe_run_callback(callback, e)
|
70
|
-
}
|
71
|
-
|
72
|
-
internal_server_error = JSONAPI::Exceptions::InternalServerError.new(e)
|
73
|
-
Rails.logger.error { "Internal Server Error: #{e.message} #{e.backtrace.join("\n")}" }
|
74
|
-
return JSONAPI::ErrorsOperationResult.new(internal_server_error.errors[0].code, internal_server_error.errors)
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def safe_run_callback(callback, error)
|
79
|
-
begin
|
80
|
-
callback.call(error)
|
81
|
-
rescue => e
|
82
|
-
Rails.logger.error { "Error in error handling callback: #{e.message} #{e.backtrace.join("\n")}" }
|
83
|
-
internal_server_error = JSONAPI::Exceptions::InternalServerError.new(e)
|
84
|
-
return JSONAPI::ErrorsOperationResult.new(internal_server_error.errors[0].code, internal_server_error.errors)
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
@@ -1,35 +0,0 @@
|
|
1
|
-
module JSONAPI
|
2
|
-
class OperationResults
|
3
|
-
attr_accessor :results
|
4
|
-
attr_accessor :meta
|
5
|
-
attr_accessor :links
|
6
|
-
|
7
|
-
def initialize
|
8
|
-
@results = []
|
9
|
-
@has_errors = false
|
10
|
-
@meta = {}
|
11
|
-
@links = {}
|
12
|
-
end
|
13
|
-
|
14
|
-
def add_result(result)
|
15
|
-
@has_errors = true if result.is_a?(JSONAPI::ErrorsOperationResult)
|
16
|
-
@results.push(result)
|
17
|
-
end
|
18
|
-
|
19
|
-
def has_errors?
|
20
|
-
@has_errors
|
21
|
-
end
|
22
|
-
|
23
|
-
def all_errors
|
24
|
-
errors = []
|
25
|
-
if @has_errors
|
26
|
-
@results.each do |result|
|
27
|
-
if result.is_a?(JSONAPI::ErrorsOperationResult)
|
28
|
-
errors.concat(result.errors)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
errors
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
@@ -1,167 +0,0 @@
|
|
1
|
-
module JSONAPI
|
2
|
-
class RelationshipBuilder
|
3
|
-
attr_reader :model_class, :options, :relationship_class
|
4
|
-
delegate :register_relationship, to: :@resource_class
|
5
|
-
|
6
|
-
def initialize(relationship_class, model_class, options)
|
7
|
-
@relationship_class = relationship_class
|
8
|
-
@model_class = model_class
|
9
|
-
@resource_class = options[:parent_resource]
|
10
|
-
@options = options
|
11
|
-
end
|
12
|
-
|
13
|
-
def define_relationship_methods(relationship_name)
|
14
|
-
# Initialize from an ActiveRecord model's properties
|
15
|
-
if model_class && model_class.ancestors.collect{|ancestor| ancestor.name}.include?('ActiveRecord::Base')
|
16
|
-
model_association = model_class.reflect_on_association(relationship_name)
|
17
|
-
if model_association
|
18
|
-
options[:class_name] ||= model_association.class_name
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
relationship = register_relationship(
|
23
|
-
relationship_name,
|
24
|
-
relationship_class.new(relationship_name, options)
|
25
|
-
)
|
26
|
-
|
27
|
-
foreign_key = define_foreign_key_setter(relationship.foreign_key)
|
28
|
-
|
29
|
-
case relationship
|
30
|
-
when JSONAPI::Relationship::ToOne
|
31
|
-
associated = define_resource_relationship_accessor(:one, relationship_name)
|
32
|
-
args = [relationship, foreign_key, associated, relationship_name]
|
33
|
-
|
34
|
-
relationship.belongs_to? ? build_belongs_to(*args) : build_has_one(*args)
|
35
|
-
when JSONAPI::Relationship::ToMany
|
36
|
-
associated = define_resource_relationship_accessor(:many, relationship_name)
|
37
|
-
|
38
|
-
build_to_many(relationship, foreign_key, associated, relationship_name)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
def define_foreign_key_setter(foreign_key)
|
43
|
-
define_on_resource "#{foreign_key}=" do |value|
|
44
|
-
@model.method("#{foreign_key}=").call(value)
|
45
|
-
end
|
46
|
-
foreign_key
|
47
|
-
end
|
48
|
-
|
49
|
-
def define_resource_relationship_accessor(type, relationship_name)
|
50
|
-
associated_records_method_name = {
|
51
|
-
one: "record_for_#{relationship_name}",
|
52
|
-
many: "records_for_#{relationship_name}"
|
53
|
-
}
|
54
|
-
.fetch(type)
|
55
|
-
|
56
|
-
define_on_resource associated_records_method_name do |options = {}|
|
57
|
-
relationship = self.class._relationships[relationship_name]
|
58
|
-
relation_name = relationship.relation_name(context: @context)
|
59
|
-
records = records_for(relation_name)
|
60
|
-
|
61
|
-
resource_klass = relationship.resource_klass
|
62
|
-
|
63
|
-
filters = options.fetch(:filters, {})
|
64
|
-
unless filters.nil? || filters.empty?
|
65
|
-
records = resource_klass.apply_filters(records, filters, options)
|
66
|
-
end
|
67
|
-
|
68
|
-
sort_criteria = options.fetch(:sort_criteria, {})
|
69
|
-
unless sort_criteria.nil? || sort_criteria.empty?
|
70
|
-
order_options = relationship.resource_klass.construct_order_options(sort_criteria)
|
71
|
-
records = resource_klass.apply_sort(records, order_options, @context)
|
72
|
-
end
|
73
|
-
|
74
|
-
paginator = options[:paginator]
|
75
|
-
if paginator
|
76
|
-
records = resource_klass.apply_pagination(records, paginator, order_options)
|
77
|
-
end
|
78
|
-
|
79
|
-
records
|
80
|
-
end
|
81
|
-
|
82
|
-
associated_records_method_name
|
83
|
-
end
|
84
|
-
|
85
|
-
def build_belongs_to(relationship, foreign_key, associated_records_method_name, relationship_name)
|
86
|
-
# Calls method matching foreign key name on model instance
|
87
|
-
define_on_resource foreign_key do
|
88
|
-
@model.method(foreign_key).call
|
89
|
-
end
|
90
|
-
|
91
|
-
# Returns instantiated related resource object or nil
|
92
|
-
define_on_resource relationship_name do |options = {}|
|
93
|
-
relationship = self.class._relationships[relationship_name]
|
94
|
-
|
95
|
-
if relationship.polymorphic?
|
96
|
-
associated_model = public_send(associated_records_method_name)
|
97
|
-
resource_klass = self.class.resource_for_model(associated_model) if associated_model
|
98
|
-
return resource_klass.new(associated_model, @context) if resource_klass
|
99
|
-
else
|
100
|
-
resource_klass = relationship.resource_klass
|
101
|
-
if resource_klass
|
102
|
-
associated_model = public_send(associated_records_method_name)
|
103
|
-
return associated_model ? resource_klass.new(associated_model, @context) : nil
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
def build_has_one(relationship, foreign_key, associated_records_method_name, relationship_name)
|
110
|
-
# Returns primary key name of related resource class
|
111
|
-
define_on_resource foreign_key do
|
112
|
-
relationship = self.class._relationships[relationship_name]
|
113
|
-
|
114
|
-
record = public_send(associated_records_method_name)
|
115
|
-
return nil if record.nil?
|
116
|
-
record.public_send(relationship.resource_klass._primary_key)
|
117
|
-
end
|
118
|
-
|
119
|
-
# Returns instantiated related resource object or nil
|
120
|
-
define_on_resource relationship_name do |options = {}|
|
121
|
-
relationship = self.class._relationships[relationship_name]
|
122
|
-
|
123
|
-
if relationship.polymorphic?
|
124
|
-
associated_model = public_send(associated_records_method_name)
|
125
|
-
resource_klass = self.class.resource_for_model(associated_model) if associated_model
|
126
|
-
return resource_klass.new(associated_model, @context) if resource_klass && associated_model
|
127
|
-
else
|
128
|
-
resource_klass = relationship.resource_klass
|
129
|
-
if resource_klass
|
130
|
-
associated_model = public_send(associated_records_method_name)
|
131
|
-
return associated_model ? resource_klass.new(associated_model, @context) : nil
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
def build_to_many(relationship, foreign_key, associated_records_method_name, relationship_name)
|
138
|
-
# Returns array of primary keys of related resource classes
|
139
|
-
define_on_resource foreign_key do
|
140
|
-
records = public_send(associated_records_method_name)
|
141
|
-
return records.collect do |record|
|
142
|
-
record.public_send(relationship.resource_klass._primary_key)
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
# Returns array of instantiated related resource objects
|
147
|
-
define_on_resource relationship_name do |options = {}|
|
148
|
-
relationship = self.class._relationships[relationship_name]
|
149
|
-
|
150
|
-
resource_klass = relationship.resource_klass
|
151
|
-
records = public_send(associated_records_method_name, options)
|
152
|
-
|
153
|
-
return records.collect do |record|
|
154
|
-
if relationship.polymorphic?
|
155
|
-
resource_klass = self.class.resource_for_model(record)
|
156
|
-
end
|
157
|
-
resource_klass.new(record, @context)
|
158
|
-
end
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
def define_on_resource(method_name, &block)
|
163
|
-
return if @resource_class.method_defined?(method_name)
|
164
|
-
@resource_class.inject_method_definition(method_name, block)
|
165
|
-
end
|
166
|
-
end
|
167
|
-
end
|