iry 0.5.0 → 0.6.0

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