jsonapi_compliable 0.11.22 → 0.11.23
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 +4 -4
- data/lib/jsonapi_compliable.rb +1 -0
- data/lib/jsonapi_compliable/adapters/active_record_sideloading.rb +1 -2
- data/lib/jsonapi_compliable/base.rb +25 -13
- data/lib/jsonapi_compliable/errors.rb +42 -1
- data/lib/jsonapi_compliable/resource.rb +26 -1
- data/lib/jsonapi_compliable/util/hooks.rb +33 -0
- data/lib/jsonapi_compliable/util/persistence.rb +11 -2
- data/lib/jsonapi_compliable/util/render_options.rb +15 -6
- data/lib/jsonapi_compliable/util/validation_response.rb +7 -0
- data/lib/jsonapi_compliable/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 48d259ac76e47a2e25542f5131358558e150d4a9
|
4
|
+
data.tar.gz: a23b4c69617f2751d8a6715cd94864564683ea27
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5a6776c91e370adabf118580498abeaaae607c0dd7ad07c4205fa895da978d2ffac0b85a154cb3e58f0f5d72316c3d27ef1042e8c96754494f67d229e527f3eb
|
7
|
+
data.tar.gz: 373820d5666c3afec279519198b3e58fdffdfe3d87363e6172339cdf57d1cb2041c29acb7389ab1b148f3785f3b381e9f7975f18bc21658b1381d80a62e3de1a
|
data/lib/jsonapi_compliable.rb
CHANGED
@@ -24,6 +24,7 @@ require "jsonapi_compliable/util/relationship_payload"
|
|
24
24
|
require "jsonapi_compliable/util/persistence"
|
25
25
|
require "jsonapi_compliable/util/validation_response"
|
26
26
|
require "jsonapi_compliable/util/sideload"
|
27
|
+
require "jsonapi_compliable/util/hooks"
|
27
28
|
|
28
29
|
# require correct jsonapi-rb before extensions
|
29
30
|
if defined?(Rails)
|
@@ -99,8 +99,7 @@ module JsonapiCompliable
|
|
99
99
|
.class.reflections[through.to_s].klass.table_name
|
100
100
|
|
101
101
|
_scope.call
|
102
|
-
.
|
103
|
-
.preload(through) # otherwise n+1 as we reference in #assign
|
102
|
+
.includes(through)
|
104
103
|
.where(table_name => { fk => parent_ids })
|
105
104
|
.distinct
|
106
105
|
end
|
@@ -253,9 +253,20 @@ module JsonapiCompliable
|
|
253
253
|
end
|
254
254
|
end
|
255
255
|
|
256
|
+
# Delete the model
|
257
|
+
# Any error, including validation errors, will roll back the transaction.
|
258
|
+
#
|
259
|
+
# Note: +before_commit+ hooks still run unless excluded
|
260
|
+
#
|
261
|
+
# @return [Util::ValidationResponse]
|
256
262
|
def jsonapi_destroy
|
257
|
-
|
258
|
-
jsonapi_resource.destroy(params[:id])
|
263
|
+
jsonapi_resource.transaction do
|
264
|
+
model = jsonapi_resource.destroy(params[:id])
|
265
|
+
validator = ::JsonapiCompliable::Util::ValidationResponse.new \
|
266
|
+
model, deserialized_params
|
267
|
+
validator.validate!
|
268
|
+
jsonapi_resource.before_commit(model, :destroy)
|
269
|
+
validator
|
259
270
|
end
|
260
271
|
end
|
261
272
|
|
@@ -321,6 +332,18 @@ module JsonapiCompliable
|
|
321
332
|
|
322
333
|
private
|
323
334
|
|
335
|
+
def _persist
|
336
|
+
jsonapi_resource.transaction do
|
337
|
+
::JsonapiCompliable::Util::Hooks.record do
|
338
|
+
model = yield
|
339
|
+
validator = ::JsonapiCompliable::Util::ValidationResponse.new \
|
340
|
+
model, deserialized_params
|
341
|
+
validator.validate!
|
342
|
+
validator
|
343
|
+
end
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
324
347
|
def force_includes?
|
325
348
|
not deserialized_params.data.nil?
|
326
349
|
end
|
@@ -330,16 +353,5 @@ module JsonapiCompliable
|
|
330
353
|
JSONAPI::Serializable::Renderer.new
|
331
354
|
.render(opts.delete(:jsonapi), opts).to_json
|
332
355
|
end
|
333
|
-
|
334
|
-
def _persist
|
335
|
-
validation_response = nil
|
336
|
-
jsonapi_resource.transaction do
|
337
|
-
object = yield
|
338
|
-
validation_response = Util::ValidationResponse.new \
|
339
|
-
object, deserialized_params
|
340
|
-
raise Errors::ValidationError unless validation_response.to_a[1]
|
341
|
-
end
|
342
|
-
validation_response
|
343
|
-
end
|
344
356
|
end
|
345
357
|
end
|
@@ -1,7 +1,48 @@
|
|
1
1
|
module JsonapiCompliable
|
2
2
|
module Errors
|
3
3
|
class BadFilter < StandardError; end
|
4
|
-
|
4
|
+
|
5
|
+
class ValidationError < StandardError
|
6
|
+
attr_reader :validation_response
|
7
|
+
|
8
|
+
def initialize(validation_response)
|
9
|
+
@validation_response = validation_response
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class MissingSerializer < StandardError
|
14
|
+
def initialize(class_name, serializer_name)
|
15
|
+
@class_name = class_name
|
16
|
+
@serializer_name = serializer_name
|
17
|
+
end
|
18
|
+
|
19
|
+
def message
|
20
|
+
<<-MSG
|
21
|
+
Could not find serializer for class '#{@class_name}'!
|
22
|
+
|
23
|
+
Looked for '#{@serializer_name}' but doesn't appear to exist.
|
24
|
+
|
25
|
+
Use a custom Inferrer if you'd like different lookup logic.
|
26
|
+
MSG
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class MissingSerializer < StandardError
|
31
|
+
def initialize(class_name, serializer_name)
|
32
|
+
@class_name = class_name
|
33
|
+
@serializer_name = serializer_name
|
34
|
+
end
|
35
|
+
|
36
|
+
def message
|
37
|
+
<<-MSG
|
38
|
+
Could not find serializer for class '#{@class_name}'!
|
39
|
+
|
40
|
+
Looked for '#{@serializer_name}' but doesn't appear to exist.
|
41
|
+
|
42
|
+
Use a custom Inferrer if you'd like different lookup logic.
|
43
|
+
MSG
|
44
|
+
end
|
45
|
+
end
|
5
46
|
|
6
47
|
class UnsupportedPageSize < StandardError
|
7
48
|
def initialize(size, max)
|
@@ -257,6 +257,29 @@ module JsonapiCompliable
|
|
257
257
|
config[:model] = klass
|
258
258
|
end
|
259
259
|
|
260
|
+
# Register a hook that fires AFTER all validation logic has run -
|
261
|
+
# including validation of nested objects - but BEFORE the transaction
|
262
|
+
# has closed.
|
263
|
+
#
|
264
|
+
# Helpful for things like "contact this external service after persisting
|
265
|
+
# data, but roll everything back if there's an error making the service call"
|
266
|
+
#
|
267
|
+
# @param [Hash] +only: [:create, :update, :destroy]+
|
268
|
+
def self.before_commit(only: [:create, :update, :destroy], &blk)
|
269
|
+
Array(only).each do |verb|
|
270
|
+
config[:before_commit][verb] = blk
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
# Actually fire the before commit hooks
|
275
|
+
#
|
276
|
+
# @see .before_commit
|
277
|
+
# @api private
|
278
|
+
def before_commit(model, method)
|
279
|
+
hook = self.class.config[:before_commit][method]
|
280
|
+
hook.call(model) if hook
|
281
|
+
end
|
282
|
+
|
260
283
|
# Define custom sorting logic
|
261
284
|
#
|
262
285
|
# @example Sort on alternate table
|
@@ -391,6 +414,7 @@ module JsonapiCompliable
|
|
391
414
|
sorting: nil,
|
392
415
|
pagination: nil,
|
393
416
|
model: nil,
|
417
|
+
before_commit: {},
|
394
418
|
adapter: Adapters::Abstract.new
|
395
419
|
}
|
396
420
|
end
|
@@ -705,7 +729,8 @@ module JsonapiCompliable
|
|
705
729
|
adapter.transaction(model) do
|
706
730
|
response = yield
|
707
731
|
end
|
708
|
-
rescue Errors::ValidationError
|
732
|
+
rescue Errors::ValidationError => e
|
733
|
+
response = e.validation_response
|
709
734
|
end
|
710
735
|
response
|
711
736
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module JsonapiCompliable
|
2
|
+
module Util
|
3
|
+
class Hooks
|
4
|
+
def self.record
|
5
|
+
self.hooks = []
|
6
|
+
begin
|
7
|
+
yield.tap { run }
|
8
|
+
ensure
|
9
|
+
self.hooks = []
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self._hooks
|
14
|
+
Thread.current[:_compliable_hooks] ||= []
|
15
|
+
end
|
16
|
+
private_class_method :_hooks
|
17
|
+
|
18
|
+
def self.hooks=(val)
|
19
|
+
Thread.current[:_compliable_hooks] = val
|
20
|
+
end
|
21
|
+
|
22
|
+
# Because hooks will be added from the outer edges of
|
23
|
+
# the graph, working inwards
|
24
|
+
def self.add(prc)
|
25
|
+
_hooks.unshift(prc)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.run
|
29
|
+
_hooks.each { |h| h.call }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -27,10 +27,11 @@ class JsonapiCompliable::Util::Persistence
|
|
27
27
|
# * associate parent objects with current object
|
28
28
|
# * process children
|
29
29
|
# * associate children
|
30
|
+
# * record hooks for later playback
|
30
31
|
# * run post-process sideload hooks
|
31
32
|
# * return current object
|
32
33
|
#
|
33
|
-
# @return
|
34
|
+
# @return a model instance
|
34
35
|
def run
|
35
36
|
parents = process_belongs_to(@relationships)
|
36
37
|
update_foreign_key_for_parents(parents)
|
@@ -45,13 +46,20 @@ class JsonapiCompliable::Util::Persistence
|
|
45
46
|
end
|
46
47
|
|
47
48
|
associate_children(persisted, children) unless @meta[:method] == :destroy
|
49
|
+
|
48
50
|
post_process(persisted, parents)
|
49
51
|
post_process(persisted, children)
|
52
|
+
before_commit = -> { @resource.before_commit(persisted, @meta[:method]) }
|
53
|
+
add_hook(before_commit)
|
50
54
|
persisted
|
51
55
|
end
|
52
56
|
|
53
57
|
private
|
54
58
|
|
59
|
+
def add_hook(prc)
|
60
|
+
::JsonapiCompliable::Util::Hooks.add(prc)
|
61
|
+
end
|
62
|
+
|
55
63
|
# The child's attributes should be modified to nil-out the
|
56
64
|
# foreign_key when the parent is being destroyed or disassociated
|
57
65
|
#
|
@@ -147,7 +155,8 @@ class JsonapiCompliable::Util::Persistence
|
|
147
155
|
groups.each_pair do |method, group|
|
148
156
|
group.group_by { |g| g[:sideload] }.each_pair do |sideload, members|
|
149
157
|
objects = members.map { |x| x[:object] }
|
150
|
-
sideload.fire_hooks!(caller_model, objects, method)
|
158
|
+
hook = -> { sideload.fire_hooks!(caller_model, objects, method) }
|
159
|
+
add_hook(hook)
|
151
160
|
end
|
152
161
|
end
|
153
162
|
end
|
@@ -6,12 +6,6 @@ module JsonapiCompliable
|
|
6
6
|
def self.generate(object, query_hash, overrides = {})
|
7
7
|
resolved = object.respond_to?(:resolve) ? object.resolve : object
|
8
8
|
|
9
|
-
inferrer = ::Hash.new do |h, k|
|
10
|
-
names = k.to_s.split('::')
|
11
|
-
klass = names.pop
|
12
|
-
h[k] = [*names, "Serializable#{klass}"].join('::').safe_constantize
|
13
|
-
end
|
14
|
-
|
15
9
|
fields = query_hash[:fields].dup
|
16
10
|
extra_fields = query_hash[:extra_fields]
|
17
11
|
|
@@ -37,6 +31,21 @@ module JsonapiCompliable
|
|
37
31
|
|
38
32
|
options
|
39
33
|
end
|
34
|
+
|
35
|
+
def self.inferrer
|
36
|
+
::Hash.new do |h, k|
|
37
|
+
names = k.to_s.split('::')
|
38
|
+
klass = names.pop
|
39
|
+
serializer_name = [*names, "Serializable#{klass}"].join('::')
|
40
|
+
serializer = serializer_name.safe_constantize
|
41
|
+
if serializer
|
42
|
+
h[k] = serializer
|
43
|
+
else
|
44
|
+
raise Errors::MissingSerializer.new(k, serializer_name)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
private_class_method :inferrer
|
40
49
|
end
|
41
50
|
end
|
42
51
|
end
|
@@ -30,6 +30,13 @@ class JsonapiCompliable::Util::ValidationResponse
|
|
30
30
|
[object, success?]
|
31
31
|
end
|
32
32
|
|
33
|
+
def validate!
|
34
|
+
unless success?
|
35
|
+
raise ::JsonapiCompliable::Errors::ValidationError.new(self)
|
36
|
+
end
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
33
40
|
private
|
34
41
|
|
35
42
|
def valid_object?(object)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jsonapi_compliable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.11.
|
4
|
+
version: 0.11.23
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lee Richmond
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2018-06-
|
12
|
+
date: 2018-06-14 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: jsonapi-serializable
|
@@ -245,6 +245,7 @@ files:
|
|
245
245
|
- lib/jsonapi_compliable/stats/payload.rb
|
246
246
|
- lib/jsonapi_compliable/util/field_params.rb
|
247
247
|
- lib/jsonapi_compliable/util/hash.rb
|
248
|
+
- lib/jsonapi_compliable/util/hooks.rb
|
248
249
|
- lib/jsonapi_compliable/util/include_params.rb
|
249
250
|
- lib/jsonapi_compliable/util/persistence.rb
|
250
251
|
- lib/jsonapi_compliable/util/relationship_payload.rb
|