jsonapi_compliable 0.11.22 → 0.11.23
Sign up to get free protection for your applications and to get access to all the features.
- 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
|