iry 0.3.0 → 0.4.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: 1cabc0965e3b746552dee09d6fcf0356bb0f166114e3cb9d696c00c033742016
4
- data.tar.gz: 0171d5388f8a7ee89d942e1ea959e2e309f3ef15fc869dba804dba8e6de0877f
3
+ metadata.gz: 4e4fc1d3dfa1e63a0ac7c38443301f2635ac7d60866d5799a1a6a78b5cdb9326
4
+ data.tar.gz: 7b4a3e8b2118dfb0788f15579e61ca35b1e32277cd571fb53e83c2905e23cc49
5
5
  SHA512:
6
- metadata.gz: ef0954dca71dbdeacd8588256939faa6a18b16ff85d1b913980c23e5058c54ceaf946959de3bbb0b9f421b5b5b3f57dea5d4d001295d6e2ad17fbfeda688771d
7
- data.tar.gz: cd3117ad1717f98fbe96d823920a25742aca1a3f10948f60ae47f9567a31c7e55633d7b2dc3ee8d151cb60e7416bd9cdb840211e8e81cd44d63ced15b199bdc1
6
+ metadata.gz: 27cc273db8893f605a6d3480305a0e575f214a4c88562a701b0ba1b00b4a98b004735f7f776428b246722c0a26706ba2681405028f0454658085cf562e94fd7b
7
+ data.tar.gz: 01b82838cf8c8e02432fe038086fa3a96db7ff83a128e5e3ac1aacc5524e2e9a76c908f0af72e85eac19bbed6ccc0972718ba9f49e69ab5d81775fc508a946e6
data/README.md CHANGED
@@ -6,7 +6,7 @@ Convert constraint errors into Rails model validation errors.
6
6
 
7
7
  ## Documentation
8
8
 
9
- https://rubydoc.info/gems/iry/frames
9
+ https://rubydoc.info/gems/iry
10
10
 
11
11
  ## Usage
12
12
 
@@ -14,7 +14,6 @@ Given the following database schema:
14
14
 
15
15
  ```sql
16
16
  create extension if not exists "pgcrypto";
17
- create extension if not exists "btree_gist";
18
17
 
19
18
  create table if not exists users (
20
19
  id uuid primary key default gen_random_uuid(),
@@ -24,7 +23,7 @@ create table if not exists users (
24
23
  );
25
24
  ```
26
25
 
27
- The following constraint can be used on the `User` class:
26
+ Set the following constraint on the `User` class:
28
27
 
29
28
  ```ruby
30
29
  class User < ActiveRecord::Base
@@ -36,14 +35,21 @@ class User < ActiveRecord::Base
36
35
  end
37
36
  ```
38
37
 
38
+ Now one of the saving mechanisms can be used:
39
+ - [`handle_constraints`](#handle_constraints)
40
+ - [`save`](#save)
41
+ - [`save!`](#save!)
42
+
39
43
  When saving a new `User` record or updating it, in case constraint exceptions are raised, these will be rescued and
40
44
  validation errors will be applied to the record, like in the following example:
41
45
 
42
46
  ```ruby
43
47
  user = User.create!(unique_text: "some unique text")
48
+ fail_user = User.new(unique_text: "some unique text")
44
49
 
45
- fail_user = User.create(unique_text: "some unique text")
50
+ success = Iry.save(fail_user)
46
51
 
52
+ success #=> false
47
53
  fail_user.errors.details.fetch(:unique_text) #=> [{error: :taken}]
48
54
  ```
49
55
 
@@ -62,7 +68,7 @@ The class method `.constraints` is also available, that returns all the constrai
62
68
 
63
69
  ### [`check_constraint`](https://rubydoc.info/gems/iry/Iry%2FMacros:check_constraint)
64
70
 
65
- ```rbs
71
+ ```ruby
66
72
  check_constraint(key, name: nil, message: :invalid) ⇒ void
67
73
  ```
68
74
 
@@ -74,7 +80,7 @@ Catches a specific check constraint violation.
74
80
 
75
81
  ### [`exclusion_constraint`](https://rubydoc.info/gems/iry/Iry%2FMacros:exclusion_constraint)
76
82
 
77
- ```rbs
83
+ ```ruby
78
84
  exclusion_constraint(key, name: nil, message: :taken) ⇒ void
79
85
  ```
80
86
 
@@ -86,7 +92,7 @@ Catches a specific exclusion constraint violation.
86
92
 
87
93
  ### [`foreign_key_constraint`](https://rubydoc.info/gems/iry/Iry%2FMacros:foreign_key_constraint)
88
94
 
89
- ```rbs
95
+ ```ruby
90
96
  foreign_key_constraint(key_or_keys, name: nil, message: :required, error_key: nil) ⇒ void
91
97
  ```
92
98
 
@@ -99,7 +105,7 @@ Catches a specific foreign key constraint violation.
99
105
 
100
106
  ### [`unique_constraint`](https://rubydoc.info/gems/iry/Iry%2FMacros:unique_constraint)
101
107
 
102
- ```rbs
108
+ ```ruby
103
109
  unique_constraint(key_or_keys, name: nil, message: :taken, error_key: nil) ⇒ void
104
110
  ```
105
111
 
@@ -110,12 +116,36 @@ Catches a specific foreign key constraint violation.
110
116
  - **message** (optional `String` or `Symbol`) error message, defaults to `:taken`
111
117
  - **error_key** (optional `Symbol`) which key will have validation errors added to
112
118
 
119
+ ## Advanced Usage
120
+
121
+ ### [`handle_constraints!`](https://rubydoc.info/gems/iry/Iry.handle_constraints)
122
+
123
+ ```ruby
124
+ .handle_constraints(model) { ... } ⇒ nil, Handlers::Model
125
+ ```
126
+
127
+ Serving as base for `save` and `save!`, it will detects constraint violations, halt the execution of the block, convert
128
+ violations to validation errors and return `nil` when violations are detected, otherwise the model object provided as
129
+ argument.
130
+
131
+ ### [`save`](https://rubydoc.info/gems/iry/Iry.save)
132
+
133
+ Acts the same as `ActiveRecord::Base#save`, accepting the same arguments and returning the same values.
134
+ In addition, it will return `false` if a constraint violation of the tracked constraints is detected and validation
135
+ errors will be added to `errors`.
136
+
137
+ ### [`save!`](https://rubydoc.info/gems/iry/Iry.save!)
138
+
139
+ Acts the same as `ActiveRecord::Base#save!`, accepting the same arguments and returning the same values.
140
+ In addition, it will raise `Iry::ConstraintViolation` when constraint violations are detected.
141
+
113
142
  ## Limitations
114
143
 
115
144
  - `valid?` will not check for constraints. If calling `valid?` right after a `save` operation, keep in mind `errors`
116
145
  are cleared
117
- - `create!` and `update!` will raise `ActiveRecord::RecordNotSaved` for constraints that are caught by `iry`, instead
118
- of `ActiveModel::ValidationError`
146
+ - It is recommended to avoid transactions when using `Iry`, because if a violation is detected, anything after
147
+ `Iry.save/save!/handle_constraints` will result in `ActiveRecord::StatementInvalid`, since the transaction is
148
+ aborted
119
149
  - Currently only PostgreSQL is supported with the `pg` gem, but it should be simple to add support for other databases.
120
150
 
121
151
  ## Installation
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.0
1
+ 0.4.0
data/db/schema.pgsql CHANGED
@@ -7,6 +7,7 @@ create table if not exists users (
7
7
  exclude_text text not null default gen_random_uuid()::text,
8
8
  user_id uuid references users (id),
9
9
  untracked_text text unique not null default gen_random_uuid()::text,
10
+ free_text text not null default '',
10
11
  friend_user_id uuid references users (id),
11
12
  created_at timestamp(6) not null,
12
13
  updated_at timestamp(6) not null,
data/lib/iry.rb CHANGED
@@ -29,12 +29,24 @@ require_relative "iry/constraint/unique"
29
29
  # end
30
30
  #
31
31
  # user = User.create!(email: "user@example.com")
32
- # fail_user = User.create(email: "user@example.com")
32
+ # fail_user = User.new(email: "user@example.com")
33
+ # Iry.save(fail_user)
33
34
  # fail_user.errors.details.fetch(:email) #=> [{error: :taken}]
34
35
  module Iry
35
- # Raised when constraint errors have been violated and have been converted to
36
+ # Included in all exceptions triggered by Iry, this allows to rescue any
37
+ # gem-related exception by rescuing {Iry::Error}
38
+ module Error
39
+ end
40
+
41
+ # Raised when constraints have been violated and have been converted to
36
42
  # model errors
37
- class RecordInvalid < ActiveRecord::RecordInvalid
43
+ class ConstraintViolation < ActiveRecord::RecordInvalid
44
+ include Error
45
+
46
+ # @!method record
47
+ # Inherited from {ActiveRecord::RecordInvalid}, returns the model for
48
+ # which the constraint violations have been detected
49
+ # @return [Handlers::Model]
38
50
  end
39
51
 
40
52
  # @param klass [Module]
@@ -56,6 +68,19 @@ module Iry
56
68
  # @yield block must perform the save operation, usually with `save`
57
69
  # @return [nil, Handlers::Model] the `model` or `nil` if a a constraint is
58
70
  # violated
71
+ # @example Handle constraints for unique user
72
+ # # The database schema has a unique constraint on email field
73
+ # class User < ActiveRecord::Base
74
+ # include Iry
75
+ #
76
+ # unique_constraint :email
77
+ # end
78
+ #
79
+ # user = User.create!(email: "user@example.com")
80
+ # fail_user = User.new(email: "user@example.com")
81
+ # result = Iry.handle_constraints(fail_user) { fail_user.save }
82
+ # result #=> nil
83
+ # fail_user.errors.details.fetch(:email) #=> [{error: :taken}]
59
84
  def self.handle_constraints(model, &block)
60
85
  raise ArgumentError, "Block required" if block.nil?
61
86
 
@@ -71,6 +96,7 @@ module Iry
71
96
 
72
97
  is_handled = handler.handle(err, model)
73
98
 
99
+ # This constraint is not handled by Iry and should raise normally
74
100
  if !is_handled
75
101
  raise
76
102
  end
@@ -96,12 +122,12 @@ module Iry
96
122
  end
97
123
 
98
124
  # Similar to {ActiveRecord::Base#save!} but in case of constraint violations,
99
- # it raises {RecordInvalid} and `errors` are populated.
125
+ # it raises {ConstraintViolation} and `errors` are populated.
100
126
  # Aside from `model`, it takes the same arguments as
101
127
  # {ActiveRecord::Base#save!}
102
128
  # @param model [Handlers::Model] model to save
103
129
  # @return [true]
104
- # @raise [RecordInvalid] {RecordInvalid} inherits from
130
+ # @raise [ConstraintViolation] {ConstraintViolation} inherits from
105
131
  # {ActiveRecord::RecordInvalid} but it's triggered only when a constraint
106
132
  # violation happens
107
133
  # @raise [ActiveRecord::RecordInvalid] triggered when a validation error is
@@ -113,6 +139,6 @@ module Iry
113
139
  return true
114
140
  end
115
141
 
116
- raise RecordInvalid.new(model)
142
+ raise ConstraintViolation.new(model)
117
143
  end
118
144
  end
data/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "iry",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "iry",
9
- "version": "0.3.0",
9
+ "version": "0.4.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.3.0",
3
+ "version": "0.4.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
@@ -16,15 +16,78 @@
16
16
  # end
17
17
  #
18
18
  # user = User.create!(email: "user@example.com")
19
- # fail_user = User.create(email: "user@example.com")
19
+ # fail_user = User.new(email: "user@example.com")
20
+ # Iry.save(fail_user)
20
21
  # fail_user.errors.details.fetch(:email) #=> [{error: :taken}]
21
22
  module Iry
22
23
  VERSION = T.let(File.read(File.expand_path("../../VERSION", __dir__)).strip.freeze, T.untyped)
23
24
 
25
+ # Inherited from {ActiveRecord::RecordInvalid}, returns the model for
26
+ # which the constraint violations have been detected
27
+ sig { returns(Handlers::Model) }
28
+ def record; end
29
+
24
30
  # _@param_ `klass`
25
31
  sig { params(klass: Module).void }
26
32
  def self.included(klass); end
27
33
 
34
+ # Executes block and in case of constraints violations on `model`, block is
35
+ # halted and errors are appended to `model`
36
+ #
37
+ # _@param_ `model` — model object for which constraints should be monitored and for which errors should be added to
38
+ #
39
+ # _@return_ — the `model` or `nil` if a a constraint is
40
+ # violated
41
+ #
42
+ # Handle constraints for unique user
43
+ # ```ruby
44
+ # # The database schema has a unique constraint on email field
45
+ # class User < ActiveRecord::Base
46
+ # include Iry
47
+ #
48
+ # unique_constraint :email
49
+ # end
50
+ #
51
+ # user = User.create!(email: "user@example.com")
52
+ # fail_user = User.new(email: "user@example.com")
53
+ # result = Iry.handle_constraints(fail_user) { fail_user.save }
54
+ # result #=> nil
55
+ # fail_user.errors.details.fetch(:email) #=> [{error: :taken}]
56
+ # ```
57
+ sig { params(model: Handlers::Model, block: T.untyped).void }
58
+ def self.handle_constraints(model, &block); end
59
+
60
+ # Similar to {ActiveRecord::Base#save} but in case of constraint violations,
61
+ # `false` is returned and `errors` are populated.
62
+ # Aside from `model`, it takes the same arguments as
63
+ # {ActiveRecord::Base#save}
64
+ #
65
+ # _@param_ `model` — model to save
66
+ #
67
+ # _@return_ — `true` if successful
68
+ sig { params(model: Handlers::Model).returns(T::Boolean) }
69
+ def self.save(model); end
70
+
71
+ # Similar to {ActiveRecord::Base#save!} but in case of constraint violations,
72
+ # it raises {ConstraintViolation} and `errors` are populated.
73
+ # Aside from `model`, it takes the same arguments as
74
+ # {ActiveRecord::Base#save!}
75
+ #
76
+ # _@param_ `model` — model to save
77
+ sig { params(model: Handlers::Model).returns(T::Boolean) }
78
+ def self.save!(model); end
79
+
80
+ # Included in all exceptions triggered by Iry, this allows to rescue any
81
+ # gem-related exception by rescuing {Iry::Error}
82
+ module Error
83
+ end
84
+
85
+ # Raised when constraints have been violated and have been converted to
86
+ # model errors
87
+ class ConstraintViolation < ActiveRecord::RecordInvalid
88
+ include Iry::Error
89
+ end
90
+
28
91
  # Class-level methods available to classes executing `include Iry`
29
92
  module Macros
30
93
  # Constraints by name
data/sig/iry.rbs CHANGED
@@ -15,14 +15,73 @@
15
15
  # end
16
16
  #
17
17
  # user = User.create!(email: "user@example.com")
18
- # fail_user = User.create(email: "user@example.com")
18
+ # fail_user = User.new(email: "user@example.com")
19
+ # Iry.save(fail_user)
19
20
  # fail_user.errors.details.fetch(:email) #=> [{error: :taken}]
20
21
  module Iry
21
22
  VERSION: String
22
23
 
24
+ # Inherited from {ActiveRecord::RecordInvalid}, returns the model for
25
+ # which the constraint violations have been detected
26
+ def record: () -> Handlers::Model
27
+
23
28
  # _@param_ `klass`
24
29
  def self.included: (Module klass) -> void
25
30
 
31
+ # Executes block and in case of constraints violations on `model`, block is
32
+ # halted and errors are appended to `model`
33
+ #
34
+ # _@param_ `model` — model object for which constraints should be monitored and for which errors should be added to
35
+ #
36
+ # _@return_ — the `model` or `nil` if a a constraint is
37
+ # violated
38
+ #
39
+ # Handle constraints for unique user
40
+ # ```ruby
41
+ # # The database schema has a unique constraint on email field
42
+ # class User < ActiveRecord::Base
43
+ # include Iry
44
+ #
45
+ # unique_constraint :email
46
+ # end
47
+ #
48
+ # user = User.create!(email: "user@example.com")
49
+ # fail_user = User.new(email: "user@example.com")
50
+ # result = Iry.handle_constraints(fail_user) { fail_user.save }
51
+ # result #=> nil
52
+ # fail_user.errors.details.fetch(:email) #=> [{error: :taken}]
53
+ # ```
54
+ def self.handle_constraints: (Handlers::Model model) -> void
55
+
56
+ # Similar to {ActiveRecord::Base#save} but in case of constraint violations,
57
+ # `false` is returned and `errors` are populated.
58
+ # Aside from `model`, it takes the same arguments as
59
+ # {ActiveRecord::Base#save}
60
+ #
61
+ # _@param_ `model` — model to save
62
+ #
63
+ # _@return_ — `true` if successful
64
+ def self.save: (Handlers::Model model) -> bool
65
+
66
+ # Similar to {ActiveRecord::Base#save!} but in case of constraint violations,
67
+ # it raises {ConstraintViolation} and `errors` are populated.
68
+ # Aside from `model`, it takes the same arguments as
69
+ # {ActiveRecord::Base#save!}
70
+ #
71
+ # _@param_ `model` — model to save
72
+ def self.save!: (Handlers::Model model) -> bool
73
+
74
+ # Included in all exceptions triggered by Iry, this allows to rescue any
75
+ # gem-related exception by rescuing {Iry::Error}
76
+ module Error
77
+ end
78
+
79
+ # Raised when constraints have been violated and have been converted to
80
+ # model errors
81
+ class ConstraintViolation < ActiveRecord::RecordInvalid
82
+ include Iry::Error
83
+ end
84
+
26
85
  # Class-level methods available to classes executing `include Iry`
27
86
  module Macros
28
87
  # Constraints by name
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: iry
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Francesco Belladonna