jsonapi-resources 0.9.12 → 0.10.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  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 -106
  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 +57 -8
  14. data/lib/jsonapi/error.rb +27 -0
  15. data/lib/jsonapi/error_codes.rb +2 -0
  16. data/lib/jsonapi/exceptions.rb +63 -40
  17. data/lib/jsonapi/formatter.rb +3 -3
  18. data/lib/jsonapi/include_directives.rb +18 -75
  19. data/lib/jsonapi/link_builder.rb +18 -25
  20. data/lib/jsonapi/operation.rb +16 -5
  21. data/lib/jsonapi/operation_result.rb +73 -15
  22. data/lib/jsonapi/paginator.rb +17 -0
  23. data/lib/jsonapi/path.rb +43 -0
  24. data/lib/jsonapi/path_segment.rb +76 -0
  25. data/lib/jsonapi/processor.rb +246 -111
  26. data/lib/jsonapi/relationship.rb +117 -24
  27. data/lib/jsonapi/request_parser.rb +383 -396
  28. data/lib/jsonapi/resource.rb +3 -1376
  29. data/lib/jsonapi/resource_controller_metal.rb +3 -0
  30. data/lib/jsonapi/resource_fragment.rb +47 -0
  31. data/lib/jsonapi/resource_id_tree.rb +112 -0
  32. data/lib/jsonapi/resource_identity.rb +42 -0
  33. data/lib/jsonapi/resource_serializer.rb +124 -286
  34. data/lib/jsonapi/resource_set.rb +176 -0
  35. data/lib/jsonapi/resources/railtie.rb +9 -0
  36. data/lib/jsonapi/resources/version.rb +1 -1
  37. data/lib/jsonapi/response_document.rb +104 -87
  38. data/lib/jsonapi/routing_ext.rb +19 -21
  39. data/lib/jsonapi-resources.rb +20 -4
  40. data/lib/tasks/check_upgrade.rake +52 -0
  41. metadata +34 -31
  42. data/lib/jsonapi/cached_resource_fragment.rb +0 -127
  43. data/lib/jsonapi/operation_dispatcher.rb +0 -88
  44. data/lib/jsonapi/operation_results.rb +0 -35
  45. data/lib/jsonapi/relationship_builder.rb +0 -167
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jsonapi-resources
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.12
4
+ version: 0.10.7
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: 2021-02-04 00:00:00.000000000 Z
12
+ date: 2022-03-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -17,14 +17,14 @@ dependencies:
17
17
  requirements:
18
18
  - - ">="
19
19
  - !ruby/object:Gem::Version
20
- version: '0'
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: '0'
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: '0'
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: '0'
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
@@ -123,20 +129,6 @@ dependencies:
123
129
  - - ">="
124
130
  - !ruby/object:Gem::Version
125
131
  version: '0'
126
- - !ruby/object:Gem::Dependency
127
- name: memory_profiler
128
- requirement: !ruby/object:Gem::Requirement
129
- requirements:
130
- - - ">="
131
- - !ruby/object:Gem::Version
132
- version: '0'
133
- type: :development
134
- prerelease: false
135
- version_requirements: !ruby/object:Gem::Requirement
136
- requirements:
137
- - - ">="
138
- - !ruby/object:Gem::Version
139
- version: '0'
140
132
  - !ruby/object:Gem::Dependency
141
133
  name: activerecord
142
134
  requirement: !ruby/object:Gem::Requirement
@@ -190,14 +182,20 @@ extra_rdoc_files: []
190
182
  files:
191
183
  - LICENSE.txt
192
184
  - README.md
185
+ - lib/bug_report_templates/rails_5_latest.rb
186
+ - lib/bug_report_templates/rails_5_master.rb
193
187
  - lib/generators/jsonapi/USAGE
194
188
  - lib/generators/jsonapi/controller_generator.rb
195
189
  - lib/generators/jsonapi/resource_generator.rb
196
190
  - lib/generators/jsonapi/templates/jsonapi_controller.rb
197
191
  - lib/generators/jsonapi/templates/jsonapi_resource.rb
198
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
199
196
  - lib/jsonapi/acts_as_resource_controller.rb
200
- - lib/jsonapi/cached_resource_fragment.rb
197
+ - lib/jsonapi/basic_resource.rb
198
+ - lib/jsonapi/cached_response_fragment.rb
201
199
  - lib/jsonapi/callbacks.rb
202
200
  - lib/jsonapi/compiled_json.rb
203
201
  - lib/jsonapi/configuration.rb
@@ -210,26 +208,31 @@ files:
210
208
  - lib/jsonapi/mime_types.rb
211
209
  - lib/jsonapi/naive_cache.rb
212
210
  - lib/jsonapi/operation.rb
213
- - lib/jsonapi/operation_dispatcher.rb
214
211
  - lib/jsonapi/operation_result.rb
215
- - lib/jsonapi/operation_results.rb
216
212
  - lib/jsonapi/paginator.rb
213
+ - lib/jsonapi/path.rb
214
+ - lib/jsonapi/path_segment.rb
217
215
  - lib/jsonapi/processor.rb
218
216
  - lib/jsonapi/relationship.rb
219
- - lib/jsonapi/relationship_builder.rb
220
217
  - lib/jsonapi/request_parser.rb
221
218
  - lib/jsonapi/resource.rb
222
219
  - lib/jsonapi/resource_controller.rb
223
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
224
224
  - lib/jsonapi/resource_serializer.rb
225
+ - lib/jsonapi/resource_set.rb
226
+ - lib/jsonapi/resources/railtie.rb
225
227
  - lib/jsonapi/resources/version.rb
226
228
  - lib/jsonapi/response_document.rb
227
229
  - lib/jsonapi/routing_ext.rb
230
+ - lib/tasks/check_upgrade.rake
228
231
  homepage: https://github.com/cerebris/jsonapi-resources
229
232
  licenses:
230
233
  - MIT
231
234
  metadata: {}
232
- post_install_message:
235
+ post_install_message:
233
236
  rdoc_options: []
234
237
  require_paths:
235
238
  - lib
@@ -237,15 +240,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
237
240
  requirements:
238
241
  - - ">="
239
242
  - !ruby/object:Gem::Version
240
- version: '2.1'
243
+ version: '2.3'
241
244
  required_rubygems_version: !ruby/object:Gem::Requirement
242
245
  requirements:
243
246
  - - ">="
244
247
  - !ruby/object:Gem::Version
245
248
  version: '0'
246
249
  requirements: []
247
- rubygems_version: 3.0.3
248
- signing_key:
250
+ rubygems_version: 3.1.6
251
+ signing_key:
249
252
  specification_version: 4
250
253
  summary: Easily support JSON API in Rails.
251
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
- records = resource_klass.apply_includes(records, options)
64
-
65
- filters = options.fetch(:filters, {})
66
- unless filters.nil? || filters.empty?
67
- records = resource_klass.apply_filters(records, filters, options)
68
- end
69
-
70
- sort_criteria = options.fetch(:sort_criteria, {})
71
- order_options = relationship.resource_klass.construct_order_options(sort_criteria)
72
- records = resource_klass.apply_sort(records, order_options, @context)
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