iry 0.3.0 → 0.4.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: 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