iry 0.5.0 → 0.6.0

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
  SHA256:
3
- metadata.gz: 8c37af75076323e55b59756a3929843bbbca04cd0bc43bc49ae89ef68e9d1665
4
- data.tar.gz: 2b4ca6aef3667fc9f6215d1d828d732dac0c8930044e88f8234cf690b5b7a514
3
+ metadata.gz: 592290e30f1db830217241ebde319fbf952ee459ac05c3bc1e87b4b47a73e143
4
+ data.tar.gz: '09cabb7faf8e78a91e7d2443bdfd5466d7e8f63bc90fcfeb2abdb72f93bd79bf'
5
5
  SHA512:
6
- metadata.gz: 8a9d1bb387cfc7d5809235378f80f84d8961a53cba63c9dddc6f8538352412cb02ab59d87e8fea16d5ac6c87ce1a243541ea421f4a1f14ce568331c8d1c77d70
7
- data.tar.gz: 0fe39194ef0b3f17719f9d66a6a7881eafd8cb02e3ef50cd42c154a18875ebc726e31155a570d3fc0687547bf01b158bf7267adb4c19ac3422b9fe5aa86e1394
6
+ metadata.gz: 915d87f39809daa1a0a76ea66c4718e20afe6dbef4ced1853df47e9457927a464a06204cd4c0abd71dad861d8df6f23c974b2a75796ff7136b17f94875fb36ae
7
+ data.tar.gz: f8b10f525eab24f478a3bb6ee2c1039258b65d532eb5475d584f68bec23cf6c2e305e47033b4621dfe49be563da584c488683905d12c9abf529c07ec194d46bc
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.0
1
+ 0.6.0
@@ -30,7 +30,7 @@ module Iry
30
30
  end
31
31
 
32
32
  # @param model [Handlers::Model]
33
- # @return [void]
33
+ # @return [ActiveModel::Error]
34
34
  def apply(model)
35
35
  model.errors.add(key, message)
36
36
  end
@@ -30,7 +30,7 @@ module Iry
30
30
  end
31
31
 
32
32
  # @param model [Handlers::Model]
33
- # @return [void]
33
+ # @return [ActiveModel::Error]
34
34
  def apply(model)
35
35
  model.errors.add(key, message)
36
36
  end
@@ -35,7 +35,7 @@ module Iry
35
35
  end
36
36
 
37
37
  # @param model [Handlers::Model]
38
- # @return [void]
38
+ # @return [ActiveModel::Error]
39
39
  def apply(model)
40
40
  model.errors.add(error_key, message)
41
41
  end
@@ -35,7 +35,7 @@ module Iry
35
35
  end
36
36
 
37
37
  # @param model [Handlers::Model]
38
- # @return [void]
38
+ # @return [ActiveModel::Error]
39
39
  def apply(model)
40
40
  model.errors.add(error_key, message)
41
41
  end
@@ -6,7 +6,7 @@ module Iry
6
6
  # Sets validation errors on the model
7
7
  # @abstract
8
8
  # @param model [Handlers::Model]
9
- # @return [void]
9
+ # @return [ActiveModel::Error]
10
10
  def apply(model)
11
11
  end
12
12
 
@@ -15,9 +15,9 @@ module Iry
15
15
  # Return always false, failing to handle any constraint
16
16
  # @param err [ActiveRecord::StatementInvalid]
17
17
  # @param model [Model] should inherit {ActiveRecord::Base} and`include Iry` to match the interface
18
- # @return [Boolean]
18
+ # @return [nil, ActiveModel::Error]
19
19
  def handle(err, model)
20
- return false
20
+ return nil
21
21
  end
22
22
  end
23
23
  end
@@ -36,7 +36,9 @@ module Iry
36
36
  # Appends constraint errors as model errors
37
37
  # @param err [ActiveRecord::StatementInvalid]
38
38
  # @param model [Model] should inherit {ActiveRecord::Base} and`include Iry` to match the interface
39
- # @return [void]
39
+ # @return [nil, ActiveModel::Error] if handled constraint, returns the
40
+ # error attached to the model. If constraint wasn't handled or handling
41
+ # failed, `nil` is returned
40
42
  def handle(err, model)
41
43
  pgerr = err.cause
42
44
  constraint_name_msg = pgerr.result.error_field(::PG::Constants::PG_DIAG_MESSAGE_PRIMARY)
@@ -44,12 +46,10 @@ module Iry
44
46
  constraint_name = match[1]
45
47
  constraint = model.class.constraints[constraint_name]
46
48
  if constraint.nil?
47
- return false
49
+ return nil
48
50
  end
49
51
 
50
- constraint.apply(model)
51
-
52
- return true
52
+ return constraint.apply(model)
53
53
  end
54
54
  end
55
55
  end
data/lib/iry/handlers.rb CHANGED
@@ -12,7 +12,8 @@ module Iry
12
12
  # @abstract
13
13
  # @param err [ActiveRecord::StatementInvalid] possible constraint error to handle
14
14
  # @param model [Model]
15
- # @return [Boolean] true if this database handler handled the constraint error
15
+ # @return [nil, ActiveModel::Error] `nil` if couldn't handle the error,
16
+ # otherwise the {ActiveModel::Error} added to the model
16
17
  def handle(err, model)
17
18
  end
18
19
  end
data/lib/iry/patch.rb CHANGED
@@ -6,7 +6,7 @@ module Iry
6
6
  # @return [Boolean] true if successful
7
7
  def create_or_update(...)
8
8
  result = false
9
- Iry.handle_constraints!(self) { result = super }
9
+ TransformConstraints.nested_constraints!(self) { result = super }
10
10
  result
11
11
  end
12
12
  end
@@ -0,0 +1,139 @@
1
+ module Iry
2
+ # Implementation of {Iry} methods, helps ensuring the main module focus on
3
+ # documentation
4
+ module TransformConstraints
5
+ extend self
6
+
7
+ # @param model [Handlers::Model]
8
+ # @yield
9
+ # @return [nil, Handlers::Model]
10
+ def handle_constraints(model, &block)
11
+ handle_constraints!(model, &block)
12
+ rescue StatementInvalid
13
+ return nil
14
+ end
15
+
16
+ # @param model [Handlers::Model]
17
+ # @yield
18
+ # @return [Handlers::Model]
19
+ def handle_constraints!(model, &block)
20
+ # Allows checking if Iry has been activated (handling).
21
+ # Number is used to support nested handle_constraints!
22
+ Thread.current[:iry] ||= 0
23
+ Thread.current[:iry] += 1
24
+
25
+ nested_constraints!(model, &block)
26
+ rescue StatementInvalid => err
27
+ # Imports errors from "nested" models back into the parent, to ensure
28
+ # `errors` is populated and the record is considered invalid
29
+
30
+ # Skip if error has been added to the same object being handled
31
+ if err.record.object_id == model.object_id
32
+ raise
33
+ end
34
+
35
+ # Adds the error only if it hasn't been added already
36
+ already_imported = model
37
+ .errors
38
+ .each
39
+ .lazy
40
+ .select { |ae| ae.respond_to?(:inner_error) }
41
+ .map { |ae| ae.inner_error }
42
+ .include?(err.error)
43
+ if !already_imported
44
+ model.errors.import(err.error)
45
+ end
46
+
47
+ raise
48
+ ensure
49
+ # "Pop" handle_constraints! usage, when 0, no constraint handling should
50
+ # happen
51
+ Thread.current[:iry] -= 1
52
+ end
53
+
54
+ # Tracks constraints of models saved as a consequence of saving another
55
+ # model. This usually represents a situation of model using
56
+ # `accepts_nested_attributes_for`
57
+ # @param model [Handlers::Model]
58
+ # @yield
59
+ # @return [Handlers::Model]
60
+ # @private
61
+ def nested_constraints!(model, &block)
62
+ raise ArgumentError, "Block required" if block.nil?
63
+
64
+ block.()
65
+
66
+ return model
67
+ rescue ActiveRecord::StatementInvalid => err
68
+ # Exit immediately if Iry hasn't been activated
69
+ if Thread.current[:iry].nil? || Thread.current[:iry] == 0
70
+ raise
71
+ end
72
+
73
+ # Exception might be an unknown constraint that is not handled by Iry
74
+ # yet. If that's the case, Null handler will ensure that everything
75
+ # proceeds as if Iry wasn't involved
76
+ handler = Handlers::Null
77
+ case
78
+ when Handlers::PG.handle?(err)
79
+ handler = Handlers::PG
80
+ end
81
+
82
+ model_error = handler.handle(err, model)
83
+
84
+ # This constraint is not handled by Iry and should raise normally
85
+ if model_error.nil?
86
+ raise
87
+ end
88
+
89
+ raise(
90
+ StatementInvalid.new(
91
+ err.message,
92
+ sql: err.sql,
93
+ binds: err.binds,
94
+ record: model,
95
+ error: model_error
96
+ )
97
+ )
98
+ end
99
+
100
+ # @param model [Handlers::Model]
101
+ # @return [Boolean]
102
+ def save(model, ...)
103
+ success = nil
104
+ constraint_model = handle_constraints(model) { success = model.save(...) }
105
+
106
+ if constraint_model
107
+ return success
108
+ end
109
+
110
+ return false
111
+ end
112
+
113
+ # @param model [Handlers::Model]
114
+ # @return [true]
115
+ # @raise [ConstraintViolation]
116
+ # @raise [ActiveRecord::RecordInvalid]
117
+ def save!(model, ...)
118
+ constraint_model = handle_constraints(model) { model.save!(...) }
119
+
120
+ if constraint_model
121
+ return true
122
+ end
123
+
124
+ raise ConstraintViolation.new(model)
125
+ end
126
+
127
+ # @param model [Handlers::Model]
128
+ # @return [Handlers::Model]
129
+ def destroy(model)
130
+ constraint_result = handle_constraints(model) { model.destroy }
131
+
132
+ if constraint_result.nil?
133
+ return false
134
+ end
135
+
136
+ return constraint_result
137
+ end
138
+ end
139
+ end
data/lib/iry.rb CHANGED
@@ -12,6 +12,7 @@ require_relative "iry/constraint/exclusion"
12
12
  require_relative "iry/constraint/foreign_key"
13
13
  require_relative "iry/constraint/unique"
14
14
  require_relative "iry/patch"
15
+ require_relative "iry/transform_constraints"
15
16
 
16
17
  # Entrypoint of constraint validation, include in a class inheriting {ActiveRecord::Base} and the following class-level
17
18
  # methods will be available:
@@ -40,7 +41,8 @@ module Iry
40
41
  end
41
42
 
42
43
  # Raised when constraints have been violated and have been converted to
43
- # model errors
44
+ # model errors, on {ActiveRecord::Base#save!} calls, to simulate a behavior
45
+ # similar to {ActiveRecord::RecordInvalid} when it's raised
44
46
  class ConstraintViolation < ActiveRecord::RecordInvalid
45
47
  include Error
46
48
 
@@ -50,8 +52,26 @@ module Iry
50
52
  # @return [Handlers::Model]
51
53
  end
52
54
 
55
+ # Raised when constraints errors happen and go through Iry, even if these
56
+ # are not handled. This class inherits from {ActiveRecord::StatementInvalid}
57
+ # to maximize compatibility with existing code
53
58
  class StatementInvalid < ActiveRecord::StatementInvalid
54
59
  include Error
60
+
61
+ # @return [Handlers::Model] model affected by the constraint violation
62
+ attr_reader :record
63
+ # @return [ActiveModel::Error] error attached to the `record` for the
64
+ # constraint violation
65
+ attr_reader :error
66
+
67
+ # @param message [nil, String]
68
+ # @param record [Handlers::Model]
69
+ # @param error [ActiveModel::Error]
70
+ def initialize(message = nil, record:, error:, **kwargs)
71
+ @record = record
72
+ @error = error
73
+ super(message, **kwargs)
74
+ end
55
75
  end
56
76
 
57
77
  # @param klass [Module]
@@ -88,9 +108,7 @@ module Iry
88
108
  # result #=> nil
89
109
  # fail_user.errors.details.fetch(:email) #=> [{error: :taken}]
90
110
  def self.handle_constraints(model, &block)
91
- handle_constraints!(model, &block)
92
- rescue StatementInvalid
93
- return nil
111
+ TransformConstraints.handle_constraints(model, &block)
94
112
  end
95
113
 
96
114
  # Executes block and in case of constraints violations on `model`, block is
@@ -100,26 +118,7 @@ module Iry
100
118
  # @yield block must perform the save operation, usually with `save`
101
119
  # @return [Handlers::Model] returns `model` parameter
102
120
  def self.handle_constraints!(model, &block)
103
- raise ArgumentError, "Block required" if block.nil?
104
-
105
- block.()
106
-
107
- return model
108
- rescue ActiveRecord::StatementInvalid => err
109
- handler = Handlers::Null
110
- case
111
- when Handlers::PG.handle?(err)
112
- handler = Handlers::PG
113
- end
114
-
115
- is_handled = handler.handle(err, model)
116
-
117
- # This constraint is not handled by Iry and should raise normally
118
- if !is_handled
119
- raise
120
- end
121
-
122
- raise StatementInvalid.new(err.message, sql: err.sql, binds: err.binds)
121
+ TransformConstraints.handle_constraints!(model, &block)
123
122
  end
124
123
 
125
124
  # Similar to {ActiveRecord::Base#save} but in case of constraint violations,
@@ -129,14 +128,7 @@ module Iry
129
128
  # @param model [Handlers::Model] model to save
130
129
  # @return [Boolean] `true` if successful
131
130
  def self.save(model, ...)
132
- success = nil
133
- constraint_model = handle_constraints(model) { success = model.save(...) }
134
-
135
- if constraint_model
136
- return success
137
- end
138
-
139
- return false
131
+ TransformConstraints.save(model, ...)
140
132
  end
141
133
 
142
134
  # Similar to {ActiveRecord::Base#save!} but in case of constraint violations,
@@ -151,13 +143,7 @@ module Iry
151
143
  # @raise [ActiveRecord::RecordInvalid] triggered when a validation error is
152
144
  # raised, but not a constraint violation
153
145
  def self.save!(model, ...)
154
- constraint_model = handle_constraints(model) { model.save!(...) }
155
-
156
- if constraint_model
157
- return true
158
- end
159
-
160
- raise ConstraintViolation.new(model)
146
+ TransformConstraints.save!(model, ...)
161
147
  end
162
148
 
163
149
  # Similar to {ActiveRecord::Base#destroy} but in case of constraint
@@ -165,12 +151,6 @@ module Iry
165
151
  # @param model [Handlers::Model] model to destroy
166
152
  # @return [Handlers::Model] the destroyed model
167
153
  def self.destroy(model)
168
- constraint_result = handle_constraints(model) { model.destroy }
169
-
170
- if constraint_result.nil?
171
- return false
172
- end
173
-
174
- return constraint_result
154
+ TransformConstraints.destroy(model)
175
155
  end
176
156
  end
data/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "iry",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "iry",
9
- "version": "0.5.0",
9
+ "version": "0.6.0",
10
10
  "license": "MIT",
11
11
  "devDependencies": {
12
12
  "conventional-changelog-cli": ">= 3.0.0",
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iry",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Transform database constraint errors into activerecord validation errors",
5
5
  "private": true,
6
6
  "main": "index.js",
data/rbi/iry.rbi CHANGED
@@ -57,6 +57,15 @@ module Iry
57
57
  sig { params(model: Handlers::Model, block: T.untyped).void }
58
58
  def self.handle_constraints(model, &block); end
59
59
 
60
+ # Executes block and in case of constraints violations on `model`, block is
61
+ # halted, errors are appended to `model` and {StatementInvalid} is raised
62
+ #
63
+ # _@param_ `model` — model object for which constraints should be monitored and for which errors should be added to
64
+ #
65
+ # _@return_ — returns `model` parameter
66
+ sig { params(model: Handlers::Model, block: T.untyped).returns(Handlers::Model) }
67
+ def self.handle_constraints!(model, &block); end
68
+
60
69
  # Similar to {ActiveRecord::Base#save} but in case of constraint violations,
61
70
  # `false` is returned and `errors` are populated.
62
71
  # Aside from `model`, it takes the same arguments as
@@ -77,17 +86,71 @@ module Iry
77
86
  sig { params(model: Handlers::Model).returns(T::Boolean) }
78
87
  def self.save!(model); end
79
88
 
89
+ # Similar to {ActiveRecord::Base#destroy} but in case of constraint
90
+ # violations, `false` is returned and `errors` are populated.
91
+ #
92
+ # _@param_ `model` — model to destroy
93
+ #
94
+ # _@return_ — the destroyed model
95
+ sig { params(model: Handlers::Model).returns(Handlers::Model) }
96
+ def self.destroy(model); end
97
+
80
98
  # Included in all exceptions triggered by Iry, this allows to rescue any
81
99
  # gem-related exception by rescuing {Iry::Error}
82
100
  module Error
83
101
  end
84
102
 
85
103
  # Raised when constraints have been violated and have been converted to
86
- # model errors
104
+ # model errors, on {ActiveRecord::Base#save!} calls, to simulate a behavior
105
+ # similar to {ActiveRecord::RecordInvalid} when it's raised
87
106
  class ConstraintViolation < ActiveRecord::RecordInvalid
88
107
  include Iry::Error
89
108
  end
90
109
 
110
+ # Raised when constraints errors happen and go through Iry, even if these
111
+ # are not handled. This class inherits from {ActiveRecord::StatementInvalid}
112
+ # to maximize compatibility with existing code
113
+ class StatementInvalid < ActiveRecord::StatementInvalid
114
+ include Iry::Error
115
+
116
+ # sord warn - ActiveModel::Error wasn't able to be resolved to a constant in this project
117
+ # sord omit - no YARD type given for "**kwargs", using untyped
118
+ # _@param_ `message`
119
+ #
120
+ # _@param_ `record`
121
+ #
122
+ # _@param_ `error`
123
+ sig do
124
+ params(
125
+ message: T.nilable(String),
126
+ record: Handlers::Model,
127
+ error: ActiveModel::Error,
128
+ kwargs: T.untyped
129
+ ).void
130
+ end
131
+ def initialize(message = nil, record:, error:, **kwargs); end
132
+
133
+ # _@return_ — model affected by the constraint violation
134
+ sig { returns(Handlers::Model) }
135
+ attr_reader :record
136
+
137
+ # sord warn - ActiveModel::Error wasn't able to be resolved to a constant in this project
138
+ # _@return_ — error attached to the `record` for the
139
+ # constraint violation
140
+ sig { returns(ActiveModel::Error) }
141
+ attr_reader :error
142
+ end
143
+
144
+ # Overrides private API method {ActiveRecord#create_or_update} to handle
145
+ # constraints and attach errors to the including model
146
+ module Patch
147
+ # Takes attributes as named arguments
148
+ #
149
+ # _@return_ — true if successful
150
+ sig { returns(T::Boolean) }
151
+ def create_or_update; end
152
+ end
153
+
91
154
  # Class-level methods available to classes executing `include Iry`
92
155
  module Macros
93
156
  # Constraints by name
@@ -169,8 +232,9 @@ module Iry
169
232
  #
170
233
  # _@param_ `model`
171
234
  #
172
- # _@return_ — true if this database handler handled the constraint error
173
- sig { params(err: ActiveRecord::StatementInvalid, model: Model).returns(T::Boolean) }
235
+ # _@return_ — `nil` if couldn't handle the error,
236
+ # otherwise the {ActiveModel::Error} added to the model
237
+ sig { params(err: ActiveRecord::StatementInvalid, model: Model).void }
174
238
  def handle(err, model); end
175
239
  end
176
240
 
@@ -207,7 +271,7 @@ module Iry
207
271
  exclusion\sconstraint|
208
272
  foreign\skey\sconstraint
209
273
  )
210
- \s"(.+)"
274
+ \s"([^"]+)"
211
275
  }x, T.untyped)
212
276
 
213
277
  # sord warn - ActiveRecord::StatementInvalid wasn't able to be resolved to a constant in this project
@@ -225,6 +289,10 @@ module Iry
225
289
  # _@param_ `err`
226
290
  #
227
291
  # _@param_ `model` — should inherit {ActiveRecord::Base} and`include Iry` to match the interface
292
+ #
293
+ # _@return_ — if handled constraint, returns the
294
+ # error attached to the model. If constraint wasn't handled or handling
295
+ # failed, `nil` is returned
228
296
  sig { params(err: ActiveRecord::StatementInvalid, model: Model).void }
229
297
  def handle(err, model); end
230
298
 
@@ -243,6 +311,10 @@ module Iry
243
311
  # _@param_ `err`
244
312
  #
245
313
  # _@param_ `model` — should inherit {ActiveRecord::Base} and`include Iry` to match the interface
314
+ #
315
+ # _@return_ — if handled constraint, returns the
316
+ # error attached to the model. If constraint wasn't handled or handling
317
+ # failed, `nil` is returned
246
318
  sig { params(err: ActiveRecord::StatementInvalid, model: Model).void }
247
319
  def self.handle(err, model); end
248
320
  end
@@ -265,7 +337,7 @@ module Iry
265
337
  # _@param_ `err`
266
338
  #
267
339
  # _@param_ `model` — should inherit {ActiveRecord::Base} and`include Iry` to match the interface
268
- sig { params(err: ActiveRecord::StatementInvalid, model: Model).returns(T::Boolean) }
340
+ sig { params(err: ActiveRecord::StatementInvalid, model: Model).void }
269
341
  def handle(err, model); end
270
342
 
271
343
  # sord warn - ActiveRecord::StatementInvalid wasn't able to be resolved to a constant in this project
@@ -281,7 +353,7 @@ module Iry
281
353
  # _@param_ `err`
282
354
  #
283
355
  # _@param_ `model` — should inherit {ActiveRecord::Base} and`include Iry` to match the interface
284
- sig { params(err: ActiveRecord::StatementInvalid, model: Model).returns(T::Boolean) }
356
+ sig { params(err: ActiveRecord::StatementInvalid, model: Model).void }
285
357
  def self.handle(err, model); end
286
358
  end
287
359
  end
@@ -305,10 +377,11 @@ module Iry
305
377
  # A constraint has a name and can apply errors to an object inheriting from {ActiveRecord::Base}
306
378
  # @abstract
307
379
  module Constraint
380
+ # sord warn - ActiveModel::Error wasn't able to be resolved to a constant in this project
308
381
  # Sets validation errors on the model
309
382
  #
310
383
  # _@param_ `model`
311
- sig { params(model: Handlers::Model).void }
384
+ sig { params(model: Handlers::Model).returns(ActiveModel::Error) }
312
385
  def apply(model); end
313
386
 
314
387
  # Name of the constraint to be caught from the database
@@ -337,8 +410,9 @@ module Iry
337
410
  sig { params(key: Symbol, name: String, message: T.any(Symbol, String)).void }
338
411
  def initialize(key, name:, message: :invalid); end
339
412
 
413
+ # sord warn - ActiveModel::Error wasn't able to be resolved to a constant in this project
340
414
  # _@param_ `model`
341
- sig { params(model: Handlers::Model).void }
415
+ sig { params(model: Handlers::Model).returns(ActiveModel::Error) }
342
416
  def apply(model); end
343
417
 
344
418
  sig { returns(Symbol) }
@@ -377,8 +451,9 @@ module Iry
377
451
  end
378
452
  def initialize(keys, name:, error_key:, message: :taken); end
379
453
 
454
+ # sord warn - ActiveModel::Error wasn't able to be resolved to a constant in this project
380
455
  # _@param_ `model`
381
- sig { params(model: Handlers::Model).void }
456
+ sig { params(model: Handlers::Model).returns(ActiveModel::Error) }
382
457
  def apply(model); end
383
458
 
384
459
  sig { returns(T::Array[Symbol]) }
@@ -411,8 +486,9 @@ module Iry
411
486
  sig { params(key: Symbol, name: String, message: T.any(Symbol, String)).void }
412
487
  def initialize(key, name:, message: :taken); end
413
488
 
489
+ # sord warn - ActiveModel::Error wasn't able to be resolved to a constant in this project
414
490
  # _@param_ `model`
415
- sig { params(model: Handlers::Model).void }
491
+ sig { params(model: Handlers::Model).returns(ActiveModel::Error) }
416
492
  def apply(model); end
417
493
 
418
494
  sig { returns(Symbol) }
@@ -451,8 +527,9 @@ module Iry
451
527
  end
452
528
  def initialize(keys, name:, error_key:, message: :required); end
453
529
 
530
+ # sord warn - ActiveModel::Error wasn't able to be resolved to a constant in this project
454
531
  # _@param_ `model`
455
- sig { params(model: Handlers::Model).void }
532
+ sig { params(model: Handlers::Model).returns(ActiveModel::Error) }
456
533
  def apply(model); end
457
534
 
458
535
  sig { returns(T::Array[Symbol]) }
@@ -468,4 +545,66 @@ module Iry
468
545
  attr_accessor :error_key
469
546
  end
470
547
  end
548
+
549
+ # Implementation of {Iry} methods, helps ensuring the main module focus on
550
+ # documentation
551
+ module TransformConstraints
552
+ extend Iry::TransformConstraints
553
+
554
+ # _@param_ `model`
555
+ sig { params(model: Handlers::Model, block: T.untyped).void }
556
+ def handle_constraints(model, &block); end
557
+
558
+ # _@param_ `model`
559
+ sig { params(model: Handlers::Model, block: T.untyped).returns(Handlers::Model) }
560
+ def handle_constraints!(model, &block); end
561
+
562
+ # Tracks constraints of models saved as a consequence of saving another
563
+ # model. This usually represents a situation of model using
564
+ # `accepts_nested_attributes_for`
565
+ #
566
+ # _@param_ `model`
567
+ sig { params(model: Handlers::Model, block: T.untyped).returns(Handlers::Model) }
568
+ def nested_constraints!(model, &block); end
569
+
570
+ # _@param_ `model`
571
+ sig { params(model: Handlers::Model).returns(T::Boolean) }
572
+ def save(model); end
573
+
574
+ # _@param_ `model`
575
+ sig { params(model: Handlers::Model).returns(T::Boolean) }
576
+ def save!(model); end
577
+
578
+ # _@param_ `model`
579
+ sig { params(model: Handlers::Model).returns(Handlers::Model) }
580
+ def destroy(model); end
581
+
582
+ # _@param_ `model`
583
+ sig { params(model: Handlers::Model, block: T.untyped).void }
584
+ def self.handle_constraints(model, &block); end
585
+
586
+ # _@param_ `model`
587
+ sig { params(model: Handlers::Model, block: T.untyped).returns(Handlers::Model) }
588
+ def self.handle_constraints!(model, &block); end
589
+
590
+ # Tracks constraints of models saved as a consequence of saving another
591
+ # model. This usually represents a situation of model using
592
+ # `accepts_nested_attributes_for`
593
+ #
594
+ # _@param_ `model`
595
+ sig { params(model: Handlers::Model, block: T.untyped).returns(Handlers::Model) }
596
+ def self.nested_constraints!(model, &block); end
597
+
598
+ # _@param_ `model`
599
+ sig { params(model: Handlers::Model).returns(T::Boolean) }
600
+ def self.save(model); end
601
+
602
+ # _@param_ `model`
603
+ sig { params(model: Handlers::Model).returns(T::Boolean) }
604
+ def self.save!(model); end
605
+
606
+ # _@param_ `model`
607
+ sig { params(model: Handlers::Model).returns(Handlers::Model) }
608
+ def self.destroy(model); end
609
+ end
471
610
  end
data/sig/iry.rbs CHANGED
@@ -53,6 +53,14 @@ module Iry
53
53
  # ```
54
54
  def self.handle_constraints: (Handlers::Model model) -> void
55
55
 
56
+ # Executes block and in case of constraints violations on `model`, block is
57
+ # halted, errors are appended to `model` and {StatementInvalid} is raised
58
+ #
59
+ # _@param_ `model` — model object for which constraints should be monitored and for which errors should be added to
60
+ #
61
+ # _@return_ — returns `model` parameter
62
+ def self.handle_constraints!: (Handlers::Model model) -> Handlers::Model
63
+
56
64
  # Similar to {ActiveRecord::Base#save} but in case of constraint violations,
57
65
  # `false` is returned and `errors` are populated.
58
66
  # Aside from `model`, it takes the same arguments as
@@ -71,17 +79,64 @@ module Iry
71
79
  # _@param_ `model` — model to save
72
80
  def self.save!: (Handlers::Model model) -> bool
73
81
 
82
+ # Similar to {ActiveRecord::Base#destroy} but in case of constraint
83
+ # violations, `false` is returned and `errors` are populated.
84
+ #
85
+ # _@param_ `model` — model to destroy
86
+ #
87
+ # _@return_ — the destroyed model
88
+ def self.destroy: (Handlers::Model model) -> Handlers::Model
89
+
74
90
  # Included in all exceptions triggered by Iry, this allows to rescue any
75
91
  # gem-related exception by rescuing {Iry::Error}
76
92
  module Error
77
93
  end
78
94
 
79
95
  # Raised when constraints have been violated and have been converted to
80
- # model errors
96
+ # model errors, on {ActiveRecord::Base#save!} calls, to simulate a behavior
97
+ # similar to {ActiveRecord::RecordInvalid} when it's raised
81
98
  class ConstraintViolation < ActiveRecord::RecordInvalid
82
99
  include Iry::Error
83
100
  end
84
101
 
102
+ # Raised when constraints errors happen and go through Iry, even if these
103
+ # are not handled. This class inherits from {ActiveRecord::StatementInvalid}
104
+ # to maximize compatibility with existing code
105
+ class StatementInvalid < ActiveRecord::StatementInvalid
106
+ include Iry::Error
107
+
108
+ # sord warn - ActiveModel::Error wasn't able to be resolved to a constant in this project
109
+ # sord omit - no YARD type given for "**kwargs", using untyped
110
+ # _@param_ `message`
111
+ #
112
+ # _@param_ `record`
113
+ #
114
+ # _@param_ `error`
115
+ def initialize: (
116
+ ?String? message,
117
+ record: Handlers::Model,
118
+ error: ActiveModel::Error,
119
+ **untyped kwargs
120
+ ) -> void
121
+
122
+ # _@return_ — model affected by the constraint violation
123
+ attr_reader record: Handlers::Model
124
+
125
+ # sord warn - ActiveModel::Error wasn't able to be resolved to a constant in this project
126
+ # _@return_ — error attached to the `record` for the
127
+ # constraint violation
128
+ attr_reader error: ActiveModel::Error
129
+ end
130
+
131
+ # Overrides private API method {ActiveRecord#create_or_update} to handle
132
+ # constraints and attach errors to the including model
133
+ module Patch
134
+ # Takes attributes as named arguments
135
+ #
136
+ # _@return_ — true if successful
137
+ def create_or_update: () -> bool
138
+ end
139
+
85
140
  # Class-level methods available to classes executing `include Iry`
86
141
  module Macros
87
142
  # Constraints by name
@@ -153,8 +208,9 @@ module Iry
153
208
  #
154
209
  # _@param_ `model`
155
210
  #
156
- # _@return_ — true if this database handler handled the constraint error
157
- def handle: (ActiveRecord::StatementInvalid err, Model model) -> bool
211
+ # _@return_ — `nil` if couldn't handle the error,
212
+ # otherwise the {ActiveModel::Error} added to the model
213
+ def handle: (ActiveRecord::StatementInvalid err, Model model) -> void
158
214
  end
159
215
 
160
216
  # Interface of the model class. This class is usually inherits from {ActiveRecord::Base}
@@ -195,6 +251,10 @@ module Iry
195
251
  # _@param_ `err`
196
252
  #
197
253
  # _@param_ `model` — should inherit {ActiveRecord::Base} and`include Iry` to match the interface
254
+ #
255
+ # _@return_ — if handled constraint, returns the
256
+ # error attached to the model. If constraint wasn't handled or handling
257
+ # failed, `nil` is returned
198
258
  def handle: (ActiveRecord::StatementInvalid err, Model model) -> void
199
259
 
200
260
  # sord warn - ActiveRecord::StatementInvalid wasn't able to be resolved to a constant in this project
@@ -211,6 +271,10 @@ module Iry
211
271
  # _@param_ `err`
212
272
  #
213
273
  # _@param_ `model` — should inherit {ActiveRecord::Base} and`include Iry` to match the interface
274
+ #
275
+ # _@return_ — if handled constraint, returns the
276
+ # error attached to the model. If constraint wasn't handled or handling
277
+ # failed, `nil` is returned
214
278
  def self.handle: (ActiveRecord::StatementInvalid err, Model model) -> void
215
279
  end
216
280
 
@@ -231,7 +295,7 @@ module Iry
231
295
  # _@param_ `err`
232
296
  #
233
297
  # _@param_ `model` — should inherit {ActiveRecord::Base} and`include Iry` to match the interface
234
- def handle: (ActiveRecord::StatementInvalid err, Model model) -> bool
298
+ def handle: (ActiveRecord::StatementInvalid err, Model model) -> void
235
299
 
236
300
  # sord warn - ActiveRecord::StatementInvalid wasn't able to be resolved to a constant in this project
237
301
  # Returns always true, catching any unhandled database exception
@@ -245,7 +309,7 @@ module Iry
245
309
  # _@param_ `err`
246
310
  #
247
311
  # _@param_ `model` — should inherit {ActiveRecord::Base} and`include Iry` to match the interface
248
- def self.handle: (ActiveRecord::StatementInvalid err, Model model) -> bool
312
+ def self.handle: (ActiveRecord::StatementInvalid err, Model model) -> void
249
313
  end
250
314
  end
251
315
 
@@ -266,10 +330,11 @@ module Iry
266
330
  # A constraint has a name and can apply errors to an object inheriting from {ActiveRecord::Base}
267
331
  # @abstract
268
332
  module Constraint
333
+ # sord warn - ActiveModel::Error wasn't able to be resolved to a constant in this project
269
334
  # Sets validation errors on the model
270
335
  #
271
336
  # _@param_ `model`
272
- def apply: (Handlers::Model model) -> void
337
+ def apply: (Handlers::Model model) -> ActiveModel::Error
273
338
 
274
339
  # Name of the constraint to be caught from the database
275
340
  def name: () -> String
@@ -293,8 +358,9 @@ module Iry
293
358
  # _@param_ `name` — constraint name
294
359
  def initialize: (Symbol key, name: String, ?message: (Symbol | String)) -> void
295
360
 
361
+ # sord warn - ActiveModel::Error wasn't able to be resolved to a constant in this project
296
362
  # _@param_ `model`
297
- def apply: (Handlers::Model model) -> void
363
+ def apply: (Handlers::Model model) -> ActiveModel::Error
298
364
 
299
365
  attr_accessor key: Symbol
300
366
 
@@ -325,8 +391,9 @@ module Iry
325
391
  ?message: (Symbol | String)
326
392
  ) -> void
327
393
 
394
+ # sord warn - ActiveModel::Error wasn't able to be resolved to a constant in this project
328
395
  # _@param_ `model`
329
- def apply: (Handlers::Model model) -> void
396
+ def apply: (Handlers::Model model) -> ActiveModel::Error
330
397
 
331
398
  attr_accessor keys: ::Array[Symbol]
332
399
 
@@ -352,8 +419,9 @@ module Iry
352
419
  # _@param_ `name` — constraint name
353
420
  def initialize: (Symbol key, name: String, ?message: (Symbol | String)) -> void
354
421
 
422
+ # sord warn - ActiveModel::Error wasn't able to be resolved to a constant in this project
355
423
  # _@param_ `model`
356
- def apply: (Handlers::Model model) -> void
424
+ def apply: (Handlers::Model model) -> ActiveModel::Error
357
425
 
358
426
  attr_accessor key: Symbol
359
427
 
@@ -384,8 +452,9 @@ module Iry
384
452
  ?message: (Symbol | String)
385
453
  ) -> void
386
454
 
455
+ # sord warn - ActiveModel::Error wasn't able to be resolved to a constant in this project
387
456
  # _@param_ `model`
388
- def apply: (Handlers::Model model) -> void
457
+ def apply: (Handlers::Model model) -> ActiveModel::Error
389
458
 
390
459
  attr_accessor keys: ::Array[Symbol]
391
460
 
@@ -396,4 +465,54 @@ module Iry
396
465
  attr_accessor error_key: Symbol
397
466
  end
398
467
  end
468
+
469
+ # Implementation of {Iry} methods, helps ensuring the main module focus on
470
+ # documentation
471
+ module TransformConstraints
472
+ extend Iry::TransformConstraints
473
+
474
+ # _@param_ `model`
475
+ def handle_constraints: (Handlers::Model model) -> void
476
+
477
+ # _@param_ `model`
478
+ def handle_constraints!: (Handlers::Model model) -> Handlers::Model
479
+
480
+ # Tracks constraints of models saved as a consequence of saving another
481
+ # model. This usually represents a situation of model using
482
+ # `accepts_nested_attributes_for`
483
+ #
484
+ # _@param_ `model`
485
+ def nested_constraints!: (Handlers::Model model) -> Handlers::Model
486
+
487
+ # _@param_ `model`
488
+ def save: (Handlers::Model model) -> bool
489
+
490
+ # _@param_ `model`
491
+ def save!: (Handlers::Model model) -> bool
492
+
493
+ # _@param_ `model`
494
+ def destroy: (Handlers::Model model) -> Handlers::Model
495
+
496
+ # _@param_ `model`
497
+ def self.handle_constraints: (Handlers::Model model) -> void
498
+
499
+ # _@param_ `model`
500
+ def self.handle_constraints!: (Handlers::Model model) -> Handlers::Model
501
+
502
+ # Tracks constraints of models saved as a consequence of saving another
503
+ # model. This usually represents a situation of model using
504
+ # `accepts_nested_attributes_for`
505
+ #
506
+ # _@param_ `model`
507
+ def self.nested_constraints!: (Handlers::Model model) -> Handlers::Model
508
+
509
+ # _@param_ `model`
510
+ def self.save: (Handlers::Model model) -> bool
511
+
512
+ # _@param_ `model`
513
+ def self.save!: (Handlers::Model model) -> bool
514
+
515
+ # _@param_ `model`
516
+ def self.destroy: (Handlers::Model model) -> Handlers::Model
517
+ end
399
518
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: iry
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Francesco Belladonna
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-07-11 00:00:00.000000000 Z
11
+ date: 2023-07-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -107,6 +107,7 @@ files:
107
107
  - lib/iry/handlers/pg.rb
108
108
  - lib/iry/macros.rb
109
109
  - lib/iry/patch.rb
110
+ - lib/iry/transform_constraints.rb
110
111
  - lib/iry/version.rb
111
112
  - package-lock.json
112
113
  - package.json