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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8a39b1b919948cf402b0d9d0bbeedd2fac003f78
4
- data.tar.gz: 27f32e6407b6630a9d10d6f7881f6b309f3aa130
3
+ metadata.gz: 48d259ac76e47a2e25542f5131358558e150d4a9
4
+ data.tar.gz: a23b4c69617f2751d8a6715cd94864564683ea27
5
5
  SHA512:
6
- metadata.gz: 79556dc3d297f38e5be9d4f8e462ccf5d3cf1928d64299c2da9eac72b1fb729c52b04a76f7d388917c791c6f2b82f684289f65dc5a40c5d2a402e5d8042722ec
7
- data.tar.gz: 1d999e08ebbf82e359d9481c1d4deb167c589ef9db460bd0d191b29a30ee0a6204fc6f50be8cdfe5be62c5d4359a2c89afa66019989f2e923ac14485c005ff01
6
+ metadata.gz: 5a6776c91e370adabf118580498abeaaae607c0dd7ad07c4205fa895da978d2ffac0b85a154cb3e58f0f5d72316c3d27ef1042e8c96754494f67d229e527f3eb
7
+ data.tar.gz: 373820d5666c3afec279519198b3e58fdffdfe3d87363e6172339cdf57d1cb2041c29acb7389ab1b148f3785f3b381e9f7975f18bc21658b1381d80a62e3de1a
@@ -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
- .joins(through)
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
- _persist do
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
- class ValidationError < StandardError; end
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 the persisted model instance
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)
@@ -1,3 +1,3 @@
1
1
  module JsonapiCompliable
2
- VERSION = "0.11.22"
2
+ VERSION = "0.11.23"
3
3
  end
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.22
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-13 00:00:00.000000000 Z
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