iry 0.5.1 → 0.7.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: 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