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