iry 0.5.1 → 0.7.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: 5ca6d5b931545ba2bb0b9f1e9e7ad77da308e7d0ffe8a64d1df2c1e6378cd1a7
4
- data.tar.gz: 4747d18e4a249735907597277bad301e9177dffa5aa00e3410355be13d523683
3
+ metadata.gz: 7ba546c69426b3b1d4c530d79ed5cfed56b5f3ea5dfe629a37b5061a90213b33
4
+ data.tar.gz: 65aa3e75c36b7fae90d81e20126c0bc134ff16ee7ddc009349754cd17cc14c29
5
5
  SHA512:
6
- metadata.gz: c2b72c6a64b044c606b3940181971a28fd90fdb1ba7859566adb9f5b7ac17f58aa46544908d06715dbc15105972ee0b272ac65d608dd06ee42fd829799c3ff95
7
- data.tar.gz: 2f5c703a19785a8e7fc5689354c80b91d3d3658f00a031edbddad7ca06c36094959f494bbc157c8b40bce4fd3655fd37fa5769ec1fc02e6b7c74524118ebc37b
6
+ metadata.gz: ef79029f7a9f7a51dbb20964ad407ea146417a10de1c7c4d9149a9d83acece6e9db2abe2f3ca6779c684e31554bea9e22e6496579b5cc16eaf53ec787d6b4d17
7
+ data.tar.gz: a7daca1aca5c175224affa28884e83c19fcdfd4b01369c8b28f6cea2cfd5cea476fca77d433eb84fa53518a3812c03bc12448617bbd2a104b49dcaec1757cf44
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.1
1
+ 0.7.0
data/db/schema.pgsql CHANGED
@@ -3,14 +3,27 @@ create extension if not exists "btree_gist";
3
3
 
4
4
  create table if not exists users (
5
5
  id uuid primary key default gen_random_uuid(),
6
- unique_text text unique not null default gen_random_uuid()::text check (unique_text != 'invalid'),
6
+ unique_text text not null default gen_random_uuid()::text,
7
7
  exclude_text text not null default gen_random_uuid()::text,
8
- user_id uuid references users (id),
9
- untracked_text text unique not null default gen_random_uuid()::text,
8
+ user_id uuid,
9
+ untracked_text text not null default gen_random_uuid()::text,
10
10
  free_text text not null default '',
11
- friend_user_id uuid references users (id),
11
+ friend_user_id uuid,
12
12
  created_at timestamp(6) not null,
13
13
  updated_at timestamp(6) not null,
14
14
  -- acts similar to unique constraint
15
15
  exclude using gist (exclude_text with =)
16
16
  );
17
+
18
+ create unique index if not exists
19
+ index_users_on_unique_text on users(unique_text);
20
+ create unique index if not exists
21
+ index_users_on_untracked_text on users(untracked_text);
22
+ alter table users
23
+ add constraint chk_rails_15df0d7772 check (unique_text != 'invalid');
24
+ alter table users
25
+ add constraint fk_rails_6d0b8b3c2f
26
+ foreign key (user_id) references users(id);
27
+ alter table users
28
+ add constraint fk_rails_d3f200176b
29
+ foreign key (friend_user_id) references users(id);
@@ -6,7 +6,13 @@ module Iry
6
6
  # @param table_name [String]
7
7
  # @return [String]
8
8
  def self.infer_name(key, table_name)
9
- "#{table_name}_#{key}_check"
9
+ # PostgreSQL convention:
10
+ # "#{table_name}_#{key}_check"
11
+ # Rails convention
12
+ id = "#{table_name}_#{key}_chk"
13
+ hashed_id = OpenSSL::Digest::SHA256.hexdigest(id)[0..9]
14
+
15
+ "chk_rails_#{hashed_id}"
10
16
  end
11
17
 
12
18
  # @return [Symbol]
@@ -30,7 +36,7 @@ module Iry
30
36
  end
31
37
 
32
38
  # @param model [Handlers::Model]
33
- # @return [void]
39
+ # @return [ActiveModel::Error]
34
40
  def apply(model)
35
41
  model.errors.add(key, message)
36
42
  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
@@ -6,7 +6,17 @@ module Iry
6
6
  # @param table_name [String]
7
7
  # @return [String]
8
8
  def self.infer_name(keys, table_name)
9
- "#{table_name}_#{keys.join("_")}_fkey"
9
+ if keys.size > 1
10
+ # PostgreSQL convention:
11
+ return "#{table_name}_#{keys.join("_")}_fkey"
12
+ end
13
+
14
+ # Rails convention:
15
+ column = keys.first
16
+ id = "#{table_name}_#{column}_fk"
17
+ hashed_id = OpenSSL::Digest::SHA256.hexdigest(id)[0..9]
18
+
19
+ "fk_rails_#{hashed_id}"
10
20
  end
11
21
 
12
22
  # @return [<Symbol>]
@@ -35,7 +45,7 @@ module Iry
35
45
  end
36
46
 
37
47
  # @param model [Handlers::Model]
38
- # @return [void]
48
+ # @return [ActiveModel::Error]
39
49
  def apply(model)
40
50
  model.errors.add(error_key, message)
41
51
  end
@@ -1,12 +1,32 @@
1
1
  module Iry
2
2
  module Constraint
3
3
  class Unique
4
+ MAX_INFER_NAME_BYTE_SIZE = 62
5
+
4
6
  # Infers the unique constraint name based on keys and table name
5
7
  # @param keys [<Symbol>]
6
8
  # @param table_name [String]
7
9
  # @return [String]
8
10
  def self.infer_name(keys, table_name)
9
- "#{table_name}_#{keys.join("_")}_key"
11
+ # PostgreSQL convention:
12
+ # "#{table_name}_#{keys.join("_")}_key"
13
+
14
+ # Rails convention:
15
+ # index_trip_hikers_on_trip_id_and_hiker_card_id
16
+ # index_TABLENAME_on_COLUMN1_and_COLUMN2
17
+ name = "index_#{table_name}_on_#{keys.join("_and_")}"
18
+ if name.bytesize <= MAX_INFER_NAME_BYTE_SIZE
19
+ return name
20
+ end
21
+
22
+ digest = OpenSSL::Digest::SHA256.hexdigest(name)[0..9]
23
+ hashed_id = "_#{digest}"
24
+ name = "idx_on_#{keys.join("_")}"
25
+
26
+ short_limit = max_index_name_size - hashed_identifier.bytesize
27
+ short_name = name.mb_chars.limit(short_limit).to_s
28
+
29
+ "#{short_name}#{hashed_identifier}"
10
30
  end
11
31
 
12
32
  # @return [<Symbol>]
@@ -35,7 +55,7 @@ module Iry
35
55
  end
36
56
 
37
57
  # @param model [Handlers::Model]
38
- # @return [void]
58
+ # @return [ActiveModel::Error]
39
59
  def apply(model)
40
60
  model.errors.add(error_key, message)
41
61
  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
@@ -1,4 +1,5 @@
1
1
  require "active_record"
2
+ require "openssl"
2
3
 
3
4
  require_relative "iry/version"
4
5
  require_relative "iry/handlers"
@@ -12,6 +13,7 @@ require_relative "iry/constraint/exclusion"
12
13
  require_relative "iry/constraint/foreign_key"
13
14
  require_relative "iry/constraint/unique"
14
15
  require_relative "iry/patch"
16
+ require_relative "iry/transform_constraints"
15
17
 
16
18
  # Entrypoint of constraint validation, include in a class inheriting {ActiveRecord::Base} and the following class-level
17
19
  # methods will be available:
@@ -40,7 +42,8 @@ module Iry
40
42
  end
41
43
 
42
44
  # Raised when constraints have been violated and have been converted to
43
- # model errors
45
+ # model errors, on {ActiveRecord::Base#save!} calls, to simulate a behavior
46
+ # similar to {ActiveRecord::RecordInvalid} when it's raised
44
47
  class ConstraintViolation < ActiveRecord::RecordInvalid
45
48
  include Error
46
49
 
@@ -50,8 +53,26 @@ module Iry
50
53
  # @return [Handlers::Model]
51
54
  end
52
55
 
56
+ # Raised when constraints errors happen and go through Iry, even if these
57
+ # are not handled. This class inherits from {ActiveRecord::StatementInvalid}
58
+ # to maximize compatibility with existing code
53
59
  class StatementInvalid < ActiveRecord::StatementInvalid
54
60
  include Error
61
+
62
+ # @return [Handlers::Model] model affected by the constraint violation
63
+ attr_reader :record
64
+ # @return [ActiveModel::Error] error attached to the `record` for the
65
+ # constraint violation
66
+ attr_reader :error
67
+
68
+ # @param message [nil, String]
69
+ # @param record [Handlers::Model]
70
+ # @param error [ActiveModel::Error]
71
+ def initialize(message = nil, record:, error:, **kwargs)
72
+ @record = record
73
+ @error = error
74
+ super(message, **kwargs)
75
+ end
55
76
  end
56
77
 
57
78
  # @param klass [Module]
@@ -88,9 +109,7 @@ module Iry
88
109
  # result #=> nil
89
110
  # fail_user.errors.details.fetch(:email) #=> [{error: :taken}]
90
111
  def self.handle_constraints(model, &block)
91
- handle_constraints!(model, &block)
92
- rescue StatementInvalid
93
- return nil
112
+ TransformConstraints.handle_constraints(model, &block)
94
113
  end
95
114
 
96
115
  # Executes block and in case of constraints violations on `model`, block is
@@ -100,26 +119,7 @@ module Iry
100
119
  # @yield block must perform the save operation, usually with `save`
101
120
  # @return [Handlers::Model] returns `model` parameter
102
121
  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)
122
+ TransformConstraints.handle_constraints!(model, &block)
123
123
  end
124
124
 
125
125
  # Similar to {ActiveRecord::Base#save} but in case of constraint violations,
@@ -129,14 +129,7 @@ module Iry
129
129
  # @param model [Handlers::Model] model to save
130
130
  # @return [Boolean] `true` if successful
131
131
  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
132
+ TransformConstraints.save(model, ...)
140
133
  end
141
134
 
142
135
  # Similar to {ActiveRecord::Base#save!} but in case of constraint violations,
@@ -151,13 +144,7 @@ module Iry
151
144
  # @raise [ActiveRecord::RecordInvalid] triggered when a validation error is
152
145
  # raised, but not a constraint violation
153
146
  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)
147
+ TransformConstraints.save!(model, ...)
161
148
  end
162
149
 
163
150
  # Similar to {ActiveRecord::Base#destroy} but in case of constraint
@@ -165,12 +152,6 @@ module Iry
165
152
  # @param model [Handlers::Model] model to destroy
166
153
  # @return [Handlers::Model] the destroyed model
167
154
  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
155
+ TransformConstraints.destroy(model)
175
156
  end
176
157
  end
data/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "iry",
3
- "version": "0.5.1",
3
+ "version": "0.7.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "iry",
9
- "version": "0.5.1",
9
+ "version": "0.7.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.1",
3
+ "version": "0.7.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
@@ -101,13 +101,44 @@ module Iry
101
101
  end
102
102
 
103
103
  # Raised when constraints have been violated and have been converted to
104
- # model errors
104
+ # model errors, on {ActiveRecord::Base#save!} calls, to simulate a behavior
105
+ # similar to {ActiveRecord::RecordInvalid} when it's raised
105
106
  class ConstraintViolation < ActiveRecord::RecordInvalid
106
107
  include Iry::Error
107
108
  end
108
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
109
113
  class StatementInvalid < ActiveRecord::StatementInvalid
110
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
111
142
  end
112
143
 
113
144
  # Overrides private API method {ActiveRecord#create_or_update} to handle
@@ -201,8 +232,9 @@ module Iry
201
232
  #
202
233
  # _@param_ `model`
203
234
  #
204
- # _@return_ — true if this database handler handled the constraint error
205
- 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 }
206
238
  def handle(err, model); end
207
239
  end
208
240
 
@@ -257,6 +289,10 @@ module Iry
257
289
  # _@param_ `err`
258
290
  #
259
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
260
296
  sig { params(err: ActiveRecord::StatementInvalid, model: Model).void }
261
297
  def handle(err, model); end
262
298
 
@@ -275,6 +311,10 @@ module Iry
275
311
  # _@param_ `err`
276
312
  #
277
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
278
318
  sig { params(err: ActiveRecord::StatementInvalid, model: Model).void }
279
319
  def self.handle(err, model); end
280
320
  end
@@ -297,7 +337,7 @@ module Iry
297
337
  # _@param_ `err`
298
338
  #
299
339
  # _@param_ `model` — should inherit {ActiveRecord::Base} and`include Iry` to match the interface
300
- sig { params(err: ActiveRecord::StatementInvalid, model: Model).returns(T::Boolean) }
340
+ sig { params(err: ActiveRecord::StatementInvalid, model: Model).void }
301
341
  def handle(err, model); end
302
342
 
303
343
  # sord warn - ActiveRecord::StatementInvalid wasn't able to be resolved to a constant in this project
@@ -313,7 +353,7 @@ module Iry
313
353
  # _@param_ `err`
314
354
  #
315
355
  # _@param_ `model` — should inherit {ActiveRecord::Base} and`include Iry` to match the interface
316
- sig { params(err: ActiveRecord::StatementInvalid, model: Model).returns(T::Boolean) }
356
+ sig { params(err: ActiveRecord::StatementInvalid, model: Model).void }
317
357
  def self.handle(err, model); end
318
358
  end
319
359
  end
@@ -337,10 +377,11 @@ module Iry
337
377
  # A constraint has a name and can apply errors to an object inheriting from {ActiveRecord::Base}
338
378
  # @abstract
339
379
  module Constraint
380
+ # sord warn - ActiveModel::Error wasn't able to be resolved to a constant in this project
340
381
  # Sets validation errors on the model
341
382
  #
342
383
  # _@param_ `model`
343
- sig { params(model: Handlers::Model).void }
384
+ sig { params(model: Handlers::Model).returns(ActiveModel::Error) }
344
385
  def apply(model); end
345
386
 
346
387
  # Name of the constraint to be caught from the database
@@ -369,8 +410,9 @@ module Iry
369
410
  sig { params(key: Symbol, name: String, message: T.any(Symbol, String)).void }
370
411
  def initialize(key, name:, message: :invalid); end
371
412
 
413
+ # sord warn - ActiveModel::Error wasn't able to be resolved to a constant in this project
372
414
  # _@param_ `model`
373
- sig { params(model: Handlers::Model).void }
415
+ sig { params(model: Handlers::Model).returns(ActiveModel::Error) }
374
416
  def apply(model); end
375
417
 
376
418
  sig { returns(Symbol) }
@@ -384,6 +426,8 @@ module Iry
384
426
  end
385
427
 
386
428
  class Unique
429
+ MAX_INFER_NAME_BYTE_SIZE = T.let(62, T.untyped)
430
+
387
431
  # Infers the unique constraint name based on keys and table name
388
432
  #
389
433
  # _@param_ `keys`
@@ -409,8 +453,9 @@ module Iry
409
453
  end
410
454
  def initialize(keys, name:, error_key:, message: :taken); end
411
455
 
456
+ # sord warn - ActiveModel::Error wasn't able to be resolved to a constant in this project
412
457
  # _@param_ `model`
413
- sig { params(model: Handlers::Model).void }
458
+ sig { params(model: Handlers::Model).returns(ActiveModel::Error) }
414
459
  def apply(model); end
415
460
 
416
461
  sig { returns(T::Array[Symbol]) }
@@ -443,8 +488,9 @@ module Iry
443
488
  sig { params(key: Symbol, name: String, message: T.any(Symbol, String)).void }
444
489
  def initialize(key, name:, message: :taken); end
445
490
 
491
+ # sord warn - ActiveModel::Error wasn't able to be resolved to a constant in this project
446
492
  # _@param_ `model`
447
- sig { params(model: Handlers::Model).void }
493
+ sig { params(model: Handlers::Model).returns(ActiveModel::Error) }
448
494
  def apply(model); end
449
495
 
450
496
  sig { returns(Symbol) }
@@ -483,8 +529,9 @@ module Iry
483
529
  end
484
530
  def initialize(keys, name:, error_key:, message: :required); end
485
531
 
532
+ # sord warn - ActiveModel::Error wasn't able to be resolved to a constant in this project
486
533
  # _@param_ `model`
487
- sig { params(model: Handlers::Model).void }
534
+ sig { params(model: Handlers::Model).returns(ActiveModel::Error) }
488
535
  def apply(model); end
489
536
 
490
537
  sig { returns(T::Array[Symbol]) }
@@ -500,4 +547,66 @@ module Iry
500
547
  attr_accessor :error_key
501
548
  end
502
549
  end
550
+
551
+ # Implementation of {Iry} methods, helps ensuring the main module focus on
552
+ # documentation
553
+ module TransformConstraints
554
+ extend Iry::TransformConstraints
555
+
556
+ # _@param_ `model`
557
+ sig { params(model: Handlers::Model, block: T.untyped).void }
558
+ def handle_constraints(model, &block); end
559
+
560
+ # _@param_ `model`
561
+ sig { params(model: Handlers::Model, block: T.untyped).returns(Handlers::Model) }
562
+ def handle_constraints!(model, &block); end
563
+
564
+ # Tracks constraints of models saved as a consequence of saving another
565
+ # model. This usually represents a situation of model using
566
+ # `accepts_nested_attributes_for`
567
+ #
568
+ # _@param_ `model`
569
+ sig { params(model: Handlers::Model, block: T.untyped).returns(Handlers::Model) }
570
+ def nested_constraints!(model, &block); end
571
+
572
+ # _@param_ `model`
573
+ sig { params(model: Handlers::Model).returns(T::Boolean) }
574
+ def save(model); end
575
+
576
+ # _@param_ `model`
577
+ sig { params(model: Handlers::Model).returns(T::Boolean) }
578
+ def save!(model); end
579
+
580
+ # _@param_ `model`
581
+ sig { params(model: Handlers::Model).returns(Handlers::Model) }
582
+ def destroy(model); end
583
+
584
+ # _@param_ `model`
585
+ sig { params(model: Handlers::Model, block: T.untyped).void }
586
+ def self.handle_constraints(model, &block); end
587
+
588
+ # _@param_ `model`
589
+ sig { params(model: Handlers::Model, block: T.untyped).returns(Handlers::Model) }
590
+ def self.handle_constraints!(model, &block); end
591
+
592
+ # Tracks constraints of models saved as a consequence of saving another
593
+ # model. This usually represents a situation of model using
594
+ # `accepts_nested_attributes_for`
595
+ #
596
+ # _@param_ `model`
597
+ sig { params(model: Handlers::Model, block: T.untyped).returns(Handlers::Model) }
598
+ def self.nested_constraints!(model, &block); end
599
+
600
+ # _@param_ `model`
601
+ sig { params(model: Handlers::Model).returns(T::Boolean) }
602
+ def self.save(model); end
603
+
604
+ # _@param_ `model`
605
+ sig { params(model: Handlers::Model).returns(T::Boolean) }
606
+ def self.save!(model); end
607
+
608
+ # _@param_ `model`
609
+ sig { params(model: Handlers::Model).returns(Handlers::Model) }
610
+ def self.destroy(model); end
611
+ end
503
612
  end
data/sig/iry.rbs CHANGED
@@ -93,13 +93,39 @@ module Iry
93
93
  end
94
94
 
95
95
  # Raised when constraints have been violated and have been converted to
96
- # model errors
96
+ # model errors, on {ActiveRecord::Base#save!} calls, to simulate a behavior
97
+ # similar to {ActiveRecord::RecordInvalid} when it's raised
97
98
  class ConstraintViolation < ActiveRecord::RecordInvalid
98
99
  include Iry::Error
99
100
  end
100
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
101
105
  class StatementInvalid < ActiveRecord::StatementInvalid
102
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
103
129
  end
104
130
 
105
131
  # Overrides private API method {ActiveRecord#create_or_update} to handle
@@ -182,8 +208,9 @@ module Iry
182
208
  #
183
209
  # _@param_ `model`
184
210
  #
185
- # _@return_ — true if this database handler handled the constraint error
186
- 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
187
214
  end
188
215
 
189
216
  # Interface of the model class. This class is usually inherits from {ActiveRecord::Base}
@@ -224,6 +251,10 @@ module Iry
224
251
  # _@param_ `err`
225
252
  #
226
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
227
258
  def handle: (ActiveRecord::StatementInvalid err, Model model) -> void
228
259
 
229
260
  # sord warn - ActiveRecord::StatementInvalid wasn't able to be resolved to a constant in this project
@@ -240,6 +271,10 @@ module Iry
240
271
  # _@param_ `err`
241
272
  #
242
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
243
278
  def self.handle: (ActiveRecord::StatementInvalid err, Model model) -> void
244
279
  end
245
280
 
@@ -260,7 +295,7 @@ module Iry
260
295
  # _@param_ `err`
261
296
  #
262
297
  # _@param_ `model` — should inherit {ActiveRecord::Base} and`include Iry` to match the interface
263
- def handle: (ActiveRecord::StatementInvalid err, Model model) -> bool
298
+ def handle: (ActiveRecord::StatementInvalid err, Model model) -> void
264
299
 
265
300
  # sord warn - ActiveRecord::StatementInvalid wasn't able to be resolved to a constant in this project
266
301
  # Returns always true, catching any unhandled database exception
@@ -274,7 +309,7 @@ module Iry
274
309
  # _@param_ `err`
275
310
  #
276
311
  # _@param_ `model` — should inherit {ActiveRecord::Base} and`include Iry` to match the interface
277
- def self.handle: (ActiveRecord::StatementInvalid err, Model model) -> bool
312
+ def self.handle: (ActiveRecord::StatementInvalid err, Model model) -> void
278
313
  end
279
314
  end
280
315
 
@@ -295,10 +330,11 @@ module Iry
295
330
  # A constraint has a name and can apply errors to an object inheriting from {ActiveRecord::Base}
296
331
  # @abstract
297
332
  module Constraint
333
+ # sord warn - ActiveModel::Error wasn't able to be resolved to a constant in this project
298
334
  # Sets validation errors on the model
299
335
  #
300
336
  # _@param_ `model`
301
- def apply: (Handlers::Model model) -> void
337
+ def apply: (Handlers::Model model) -> ActiveModel::Error
302
338
 
303
339
  # Name of the constraint to be caught from the database
304
340
  def name: () -> String
@@ -322,8 +358,9 @@ module Iry
322
358
  # _@param_ `name` — constraint name
323
359
  def initialize: (Symbol key, name: String, ?message: (Symbol | String)) -> void
324
360
 
361
+ # sord warn - ActiveModel::Error wasn't able to be resolved to a constant in this project
325
362
  # _@param_ `model`
326
- def apply: (Handlers::Model model) -> void
363
+ def apply: (Handlers::Model model) -> ActiveModel::Error
327
364
 
328
365
  attr_accessor key: Symbol
329
366
 
@@ -333,6 +370,8 @@ module Iry
333
370
  end
334
371
 
335
372
  class Unique
373
+ MAX_INFER_NAME_BYTE_SIZE: untyped
374
+
336
375
  # Infers the unique constraint name based on keys and table name
337
376
  #
338
377
  # _@param_ `keys`
@@ -354,8 +393,9 @@ module Iry
354
393
  ?message: (Symbol | String)
355
394
  ) -> void
356
395
 
396
+ # sord warn - ActiveModel::Error wasn't able to be resolved to a constant in this project
357
397
  # _@param_ `model`
358
- def apply: (Handlers::Model model) -> void
398
+ def apply: (Handlers::Model model) -> ActiveModel::Error
359
399
 
360
400
  attr_accessor keys: ::Array[Symbol]
361
401
 
@@ -381,8 +421,9 @@ module Iry
381
421
  # _@param_ `name` — constraint name
382
422
  def initialize: (Symbol key, name: String, ?message: (Symbol | String)) -> void
383
423
 
424
+ # sord warn - ActiveModel::Error wasn't able to be resolved to a constant in this project
384
425
  # _@param_ `model`
385
- def apply: (Handlers::Model model) -> void
426
+ def apply: (Handlers::Model model) -> ActiveModel::Error
386
427
 
387
428
  attr_accessor key: Symbol
388
429
 
@@ -413,8 +454,9 @@ module Iry
413
454
  ?message: (Symbol | String)
414
455
  ) -> void
415
456
 
457
+ # sord warn - ActiveModel::Error wasn't able to be resolved to a constant in this project
416
458
  # _@param_ `model`
417
- def apply: (Handlers::Model model) -> void
459
+ def apply: (Handlers::Model model) -> ActiveModel::Error
418
460
 
419
461
  attr_accessor keys: ::Array[Symbol]
420
462
 
@@ -425,4 +467,54 @@ module Iry
425
467
  attr_accessor error_key: Symbol
426
468
  end
427
469
  end
470
+
471
+ # Implementation of {Iry} methods, helps ensuring the main module focus on
472
+ # documentation
473
+ module TransformConstraints
474
+ extend Iry::TransformConstraints
475
+
476
+ # _@param_ `model`
477
+ def handle_constraints: (Handlers::Model model) -> void
478
+
479
+ # _@param_ `model`
480
+ def handle_constraints!: (Handlers::Model model) -> Handlers::Model
481
+
482
+ # Tracks constraints of models saved as a consequence of saving another
483
+ # model. This usually represents a situation of model using
484
+ # `accepts_nested_attributes_for`
485
+ #
486
+ # _@param_ `model`
487
+ def nested_constraints!: (Handlers::Model model) -> Handlers::Model
488
+
489
+ # _@param_ `model`
490
+ def save: (Handlers::Model model) -> bool
491
+
492
+ # _@param_ `model`
493
+ def save!: (Handlers::Model model) -> bool
494
+
495
+ # _@param_ `model`
496
+ def destroy: (Handlers::Model model) -> Handlers::Model
497
+
498
+ # _@param_ `model`
499
+ def self.handle_constraints: (Handlers::Model model) -> void
500
+
501
+ # _@param_ `model`
502
+ def self.handle_constraints!: (Handlers::Model model) -> Handlers::Model
503
+
504
+ # Tracks constraints of models saved as a consequence of saving another
505
+ # model. This usually represents a situation of model using
506
+ # `accepts_nested_attributes_for`
507
+ #
508
+ # _@param_ `model`
509
+ def self.nested_constraints!: (Handlers::Model model) -> Handlers::Model
510
+
511
+ # _@param_ `model`
512
+ def self.save: (Handlers::Model model) -> bool
513
+
514
+ # _@param_ `model`
515
+ def self.save!: (Handlers::Model model) -> bool
516
+
517
+ # _@param_ `model`
518
+ def self.destroy: (Handlers::Model model) -> Handlers::Model
519
+ end
428
520
  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.1
4
+ version: 0.7.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-14 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