jsonapi-resources 0.9.12 → 0.10.7

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 (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