after_commit_everywhere 1.1.0 → 1.3.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 +4 -4
- data/.github/workflows/test.yml +3 -9
- data/Appraisals +7 -1
- data/CHANGELOG.md +61 -1
- data/Gemfile.lock +136 -121
- data/README.md +79 -3
- data/after_commit_everywhere.gemspec +2 -0
- data/gemfiles/activerecord_7_0.gemfile +9 -0
- data/gemfiles/activerecord_master.gemfile +1 -1
- data/lib/after_commit_everywhere/version.rb +1 -1
- data/lib/after_commit_everywhere.rb +77 -19
- metadata +31 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f406689608dbc8a4a3c904dffd3f090ad6e284f037a4316728e7cadd5ccdb9ed
|
4
|
+
data.tar.gz: 9d70381c05b482fee5f7f1f705bd263c9e43bdfac2d7558dff2ac82e98c9201e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 28bc55a3725c4baaca95574bf0b3631a411466dbcdee4e7c148c4d00c30f0ecd8a476863c65497a2c81df85978aa0705838376cc02f0a40fd4ff9f79d062db1c
|
7
|
+
data.tar.gz: 3013886554bb909368399f258a0047f55b87a4e9420eb2e923569b4c8cddfca438fb6ca2c470ae109f93fe668318efdf4cd59e785d858754f7df9df032f1b211
|
data/.github/workflows/test.yml
CHANGED
@@ -18,15 +18,6 @@ jobs:
|
|
18
18
|
fail-fast: false
|
19
19
|
matrix:
|
20
20
|
include:
|
21
|
-
- ruby: '2.5'
|
22
|
-
activerecord: '4.2'
|
23
|
-
gemfile: 'activerecord_4_2.gemfile'
|
24
|
-
- ruby: '2.6'
|
25
|
-
activerecord: '5.0'
|
26
|
-
gemfile: 'activerecord_5_0.gemfile'
|
27
|
-
- ruby: '2.6'
|
28
|
-
activerecord: '5.1'
|
29
|
-
gemfile: 'activerecord_5_1.gemfile'
|
30
21
|
- ruby: '2.6'
|
31
22
|
activerecord: '5.2'
|
32
23
|
gemfile: 'activerecord_5_2.gemfile'
|
@@ -37,6 +28,9 @@ jobs:
|
|
37
28
|
activerecord: '6.1'
|
38
29
|
gemfile: 'activerecord_6_1.gemfile'
|
39
30
|
- ruby: '3.0'
|
31
|
+
activerecord: '7.0'
|
32
|
+
gemfile: 'activerecord_7_0.gemfile'
|
33
|
+
- ruby: '3.1'
|
40
34
|
activerecord: 'HEAD'
|
41
35
|
gemfile: 'activerecord_master.gemfile'
|
42
36
|
container:
|
data/Appraisals
CHANGED
@@ -31,6 +31,12 @@ appraise "activerecord-6-1" do
|
|
31
31
|
gem "rspec-rails", "~> 4.0"
|
32
32
|
end
|
33
33
|
|
34
|
+
appraise "activerecord-7-0" do
|
35
|
+
gem "activerecord", "~> 7.0.0"
|
36
|
+
gem "sqlite3", "~> 1.4"
|
37
|
+
gem "rspec-rails", "~> 5.0"
|
38
|
+
end
|
39
|
+
|
34
40
|
appraise "activerecord-master" do
|
35
41
|
git "https://github.com/rails/rails.git" do
|
36
42
|
gem "rails"
|
@@ -38,5 +44,5 @@ appraise "activerecord-master" do
|
|
38
44
|
end
|
39
45
|
|
40
46
|
gem "sqlite3", "~> 1.4"
|
41
|
-
gem "rspec-rails", "~>
|
47
|
+
gem "rspec-rails", "~> 5.0"
|
42
48
|
end
|
data/CHANGELOG.md
CHANGED
@@ -4,7 +4,65 @@ All notable changes to this project will be documented in this file.
|
|
4
4
|
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
5
5
|
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
6
6
|
|
7
|
-
##
|
7
|
+
## 1.3.0 (2022-10-28)
|
8
|
+
|
9
|
+
### Added
|
10
|
+
|
11
|
+
- `in_transaction` helper method to execute code within existing transaction or start a new one if there is no tx open.
|
12
|
+
|
13
|
+
It is similar to `ActiveRecord::Base.transaction`, but it doesn't swallow `ActiveRecord::Rollback` exception in case when there is no transaction open.
|
14
|
+
|
15
|
+
See discussion at [#23](https://github.com/Envek/after_commit_everywhere/pull/23) for details.
|
16
|
+
|
17
|
+
[Pull request #23](https://github.com/Envek/after_commit_everywhere/pull/23) by [@jpcamara][].
|
18
|
+
|
19
|
+
- Ability to call `in_transaction` helper with the same arguments as [`ActiveRecord::Base.transaction`](https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/DatabaseStatements.html#method-i-transaction). [@Envek][].
|
20
|
+
|
21
|
+
## 1.2.2 (2022-06-20)
|
22
|
+
|
23
|
+
### Fixed
|
24
|
+
|
25
|
+
- Connection leak from the connection pool when `after_commit` called outside Rails executor without connection checked out *and* some connections were already checked out from another threads.
|
26
|
+
|
27
|
+
See discussion at [issue #20](https://github.com/Envek/after_commit_everywhere/issues/20) for details.
|
28
|
+
|
29
|
+
[Pull request #22](https://github.com/Envek/after_commit_everywhere/pull/22) by [@Envek][].
|
30
|
+
|
31
|
+
## 1.2.1 (2022-06-10)
|
32
|
+
|
33
|
+
### Fixed
|
34
|
+
|
35
|
+
- Connection leak from the connection pool when `after_commit` called outside Rails executor without connection checked out
|
36
|
+
|
37
|
+
Usually all invocations of `after_commit` (whether it happens during serving HTTP request in Rails controller or performing job in Sidekiq worker process) are made inside [Rails executor](https://guides.rubyonrails.org/threading_and_code_execution.html#executor) which checks in any connections back to the connection pool that were checked out inside its block.
|
38
|
+
|
39
|
+
However, in cases when a) `after_commit` was called outside of Rails executor (3-rd party gems or non-Rails apps using ActiveRecord) **and** b) database connection hasn't been checked out yet, then connection will be checked out by `after_commit` implicitly by call to `ActiveRecord::Base.connection` and not checked in back afterwards causing it to _leak_ from the connection pool.
|
40
|
+
|
41
|
+
But in that case we can be sure that there is no transaction in progress ('cause one need to checkout connection and issue `BEGIN` to it), so we don't need to check it out at all and can fast-forward to `without_tx` action.
|
42
|
+
|
43
|
+
See discussion at [issue #20](https://github.com/Envek/after_commit_everywhere/issues/20) for details.
|
44
|
+
|
45
|
+
[Pull request #21](https://github.com/Envek/after_commit_everywhere/pull/21) by [@Envek][].
|
46
|
+
|
47
|
+
## 1.2.0 (2022-03-26)
|
48
|
+
|
49
|
+
### Added
|
50
|
+
|
51
|
+
- Allow to change callbacks' behavior when they are called outside transaction:
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
AfterCommitEverywhere.after_commit(without_tx: :raise) do
|
55
|
+
# Will be executed only if was called within transaction
|
56
|
+
# Error will be raised otherwise
|
57
|
+
end
|
58
|
+
```
|
59
|
+
|
60
|
+
Available values for `without_tx` keyword argument:
|
61
|
+
- `:execute` to execute callback immediately
|
62
|
+
- `:warn_and_execute` to print warning and execute immediately
|
63
|
+
- `:raise` to raise an exception instead of executing
|
64
|
+
|
65
|
+
[Pull request #18](https://github.com/Envek/after_commit_everywhere/pull/18) by [@lolripgg][].
|
8
66
|
|
9
67
|
## 1.1.0 (2021-08-05)
|
10
68
|
|
@@ -54,3 +112,5 @@ See [#11](https://github.com/Envek/after_commit_everywhere/issues/11) for discus
|
|
54
112
|
[@arjun810]: https://github.com/arjun810 "Arjun Singh"
|
55
113
|
[@joevandyk]: https://github.com/joevandyk "Joe Van Dyk"
|
56
114
|
[@stokarenko]: https://github.com/stokarenko "Sergey Tokarenko"
|
115
|
+
[@lolripgg]: https://github.com/lolripgg "James Brewer"
|
116
|
+
[@jpcamara]: https://github.com/jpcamara "JP Camara"
|
data/Gemfile.lock
CHANGED
@@ -1,170 +1,185 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
after_commit_everywhere (1.
|
4
|
+
after_commit_everywhere (1.3.0)
|
5
5
|
activerecord (>= 4.2)
|
6
6
|
activesupport
|
7
7
|
|
8
8
|
GEM
|
9
9
|
remote: https://rubygems.org/
|
10
10
|
specs:
|
11
|
-
actioncable (
|
12
|
-
actionpack (=
|
13
|
-
activesupport (=
|
11
|
+
actioncable (7.0.4)
|
12
|
+
actionpack (= 7.0.4)
|
13
|
+
activesupport (= 7.0.4)
|
14
14
|
nio4r (~> 2.0)
|
15
15
|
websocket-driver (>= 0.6.1)
|
16
|
-
actionmailbox (
|
17
|
-
actionpack (=
|
18
|
-
activejob (=
|
19
|
-
activerecord (=
|
20
|
-
activestorage (=
|
21
|
-
activesupport (=
|
16
|
+
actionmailbox (7.0.4)
|
17
|
+
actionpack (= 7.0.4)
|
18
|
+
activejob (= 7.0.4)
|
19
|
+
activerecord (= 7.0.4)
|
20
|
+
activestorage (= 7.0.4)
|
21
|
+
activesupport (= 7.0.4)
|
22
22
|
mail (>= 2.7.1)
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
23
|
+
net-imap
|
24
|
+
net-pop
|
25
|
+
net-smtp
|
26
|
+
actionmailer (7.0.4)
|
27
|
+
actionpack (= 7.0.4)
|
28
|
+
actionview (= 7.0.4)
|
29
|
+
activejob (= 7.0.4)
|
30
|
+
activesupport (= 7.0.4)
|
28
31
|
mail (~> 2.5, >= 2.5.4)
|
32
|
+
net-imap
|
33
|
+
net-pop
|
34
|
+
net-smtp
|
29
35
|
rails-dom-testing (~> 2.0)
|
30
|
-
actionpack (
|
31
|
-
actionview (=
|
32
|
-
activesupport (=
|
33
|
-
rack (~> 2.0, >= 2.0
|
36
|
+
actionpack (7.0.4)
|
37
|
+
actionview (= 7.0.4)
|
38
|
+
activesupport (= 7.0.4)
|
39
|
+
rack (~> 2.0, >= 2.2.0)
|
34
40
|
rack-test (>= 0.6.3)
|
35
41
|
rails-dom-testing (~> 2.0)
|
36
42
|
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
37
|
-
actiontext (
|
38
|
-
actionpack (=
|
39
|
-
activerecord (=
|
40
|
-
activestorage (=
|
41
|
-
activesupport (=
|
43
|
+
actiontext (7.0.4)
|
44
|
+
actionpack (= 7.0.4)
|
45
|
+
activerecord (= 7.0.4)
|
46
|
+
activestorage (= 7.0.4)
|
47
|
+
activesupport (= 7.0.4)
|
48
|
+
globalid (>= 0.6.0)
|
42
49
|
nokogiri (>= 1.8.5)
|
43
|
-
actionview (
|
44
|
-
activesupport (=
|
50
|
+
actionview (7.0.4)
|
51
|
+
activesupport (= 7.0.4)
|
45
52
|
builder (~> 3.1)
|
46
53
|
erubi (~> 1.4)
|
47
54
|
rails-dom-testing (~> 2.0)
|
48
55
|
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
49
|
-
|
50
|
-
|
51
|
-
activemodel (>= 3.0.2, < 7.0)
|
52
|
-
activesupport (>= 3.0.2, < 7.0)
|
53
|
-
activejob (6.1.4)
|
54
|
-
activesupport (= 6.1.4)
|
56
|
+
activejob (7.0.4)
|
57
|
+
activesupport (= 7.0.4)
|
55
58
|
globalid (>= 0.3.6)
|
56
|
-
activemodel (
|
57
|
-
activesupport (=
|
58
|
-
activerecord (
|
59
|
-
activemodel (=
|
60
|
-
activesupport (=
|
61
|
-
activestorage (
|
62
|
-
actionpack (=
|
63
|
-
activejob (=
|
64
|
-
activerecord (=
|
65
|
-
activesupport (=
|
66
|
-
marcel (~> 1.0
|
59
|
+
activemodel (7.0.4)
|
60
|
+
activesupport (= 7.0.4)
|
61
|
+
activerecord (7.0.4)
|
62
|
+
activemodel (= 7.0.4)
|
63
|
+
activesupport (= 7.0.4)
|
64
|
+
activestorage (7.0.4)
|
65
|
+
actionpack (= 7.0.4)
|
66
|
+
activejob (= 7.0.4)
|
67
|
+
activerecord (= 7.0.4)
|
68
|
+
activesupport (= 7.0.4)
|
69
|
+
marcel (~> 1.0)
|
67
70
|
mini_mime (>= 1.1.0)
|
68
|
-
activesupport (
|
71
|
+
activesupport (7.0.4)
|
69
72
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
70
73
|
i18n (>= 1.6, < 2)
|
71
74
|
minitest (>= 5.1)
|
72
75
|
tzinfo (~> 2.0)
|
73
|
-
|
74
|
-
|
75
|
-
ruby-next-core (>= 0.11.0)
|
76
|
+
anyway_config (2.3.0)
|
77
|
+
ruby-next-core (>= 0.14.0)
|
76
78
|
appraisal (2.4.1)
|
77
79
|
bundler
|
78
80
|
rake
|
79
81
|
thor (>= 0.14.0)
|
80
82
|
ast (2.4.2)
|
81
83
|
builder (3.2.4)
|
84
|
+
byebug (11.1.3)
|
82
85
|
coderay (1.1.3)
|
83
|
-
concurrent-ruby (1.1.
|
86
|
+
concurrent-ruby (1.1.10)
|
84
87
|
crass (1.0.6)
|
85
|
-
diff-lcs (1.
|
86
|
-
|
87
|
-
|
88
|
+
diff-lcs (1.5.0)
|
89
|
+
dry-initializer (3.1.1)
|
90
|
+
erubi (1.11.0)
|
91
|
+
globalid (1.0.0)
|
88
92
|
activesupport (>= 5.0)
|
89
|
-
i18n (1.
|
93
|
+
i18n (1.12.0)
|
90
94
|
concurrent-ruby (~> 1.0)
|
91
|
-
isolator (0.
|
95
|
+
isolator (0.8.0)
|
92
96
|
sniffer (>= 0.3.1)
|
93
97
|
jaro_winkler (1.5.4)
|
94
|
-
loofah (2.
|
98
|
+
loofah (2.19.0)
|
95
99
|
crass (~> 1.0.2)
|
96
100
|
nokogiri (>= 1.5.9)
|
97
101
|
mail (2.7.1)
|
98
102
|
mini_mime (>= 0.1.1)
|
99
|
-
marcel (1.0.
|
103
|
+
marcel (1.0.2)
|
100
104
|
method_source (1.0.0)
|
101
|
-
mini_mime (1.1.
|
102
|
-
mini_portile2 (2.
|
103
|
-
minitest (5.
|
105
|
+
mini_mime (1.1.2)
|
106
|
+
mini_portile2 (2.8.0)
|
107
|
+
minitest (5.16.3)
|
108
|
+
net-imap (0.3.1)
|
109
|
+
net-protocol
|
110
|
+
net-pop (0.1.2)
|
111
|
+
net-protocol
|
112
|
+
net-protocol (0.1.3)
|
113
|
+
timeout
|
114
|
+
net-smtp (0.3.2)
|
115
|
+
net-protocol
|
104
116
|
nio4r (2.5.8)
|
105
|
-
nokogiri (1.
|
106
|
-
mini_portile2 (~> 2.
|
117
|
+
nokogiri (1.13.9)
|
118
|
+
mini_portile2 (~> 2.8.0)
|
107
119
|
racc (~> 1.4)
|
108
|
-
parallel (1.
|
109
|
-
parser (3.
|
120
|
+
parallel (1.22.1)
|
121
|
+
parser (3.1.2.1)
|
110
122
|
ast (~> 2.4.1)
|
111
123
|
pry (0.14.1)
|
112
124
|
coderay (~> 1.1)
|
113
125
|
method_source (~> 1.0)
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
126
|
+
pry-byebug (3.10.1)
|
127
|
+
byebug (~> 11.0)
|
128
|
+
pry (>= 0.13, < 0.15)
|
129
|
+
racc (1.6.0)
|
130
|
+
rack (2.2.3.1)
|
131
|
+
rack-test (2.0.2)
|
132
|
+
rack (>= 1.3)
|
133
|
+
rails (7.0.4)
|
134
|
+
actioncable (= 7.0.4)
|
135
|
+
actionmailbox (= 7.0.4)
|
136
|
+
actionmailer (= 7.0.4)
|
137
|
+
actionpack (= 7.0.4)
|
138
|
+
actiontext (= 7.0.4)
|
139
|
+
actionview (= 7.0.4)
|
140
|
+
activejob (= 7.0.4)
|
141
|
+
activemodel (= 7.0.4)
|
142
|
+
activerecord (= 7.0.4)
|
143
|
+
activestorage (= 7.0.4)
|
144
|
+
activesupport (= 7.0.4)
|
130
145
|
bundler (>= 1.15.0)
|
131
|
-
railties (=
|
132
|
-
sprockets-rails (>= 2.0.0)
|
146
|
+
railties (= 7.0.4)
|
133
147
|
rails-dom-testing (2.0.3)
|
134
148
|
activesupport (>= 4.2.0)
|
135
149
|
nokogiri (>= 1.6)
|
136
|
-
rails-html-sanitizer (1.3
|
150
|
+
rails-html-sanitizer (1.4.3)
|
137
151
|
loofah (~> 2.3)
|
138
|
-
railties (
|
139
|
-
actionpack (=
|
140
|
-
activesupport (=
|
152
|
+
railties (7.0.4)
|
153
|
+
actionpack (= 7.0.4)
|
154
|
+
activesupport (= 7.0.4)
|
141
155
|
method_source
|
142
|
-
rake (>=
|
156
|
+
rake (>= 12.2)
|
143
157
|
thor (~> 1.0)
|
144
|
-
|
158
|
+
zeitwerk (~> 2.5)
|
159
|
+
rainbow (3.1.1)
|
145
160
|
rake (13.0.6)
|
146
161
|
rexml (3.2.5)
|
147
|
-
rspec (3.
|
148
|
-
rspec-core (~> 3.
|
149
|
-
rspec-expectations (~> 3.
|
150
|
-
rspec-mocks (~> 3.
|
151
|
-
rspec-core (3.
|
152
|
-
rspec-support (~> 3.
|
153
|
-
rspec-expectations (3.
|
162
|
+
rspec (3.12.0)
|
163
|
+
rspec-core (~> 3.12.0)
|
164
|
+
rspec-expectations (~> 3.12.0)
|
165
|
+
rspec-mocks (~> 3.12.0)
|
166
|
+
rspec-core (3.12.0)
|
167
|
+
rspec-support (~> 3.12.0)
|
168
|
+
rspec-expectations (3.12.0)
|
154
169
|
diff-lcs (>= 1.2.0, < 2.0)
|
155
|
-
rspec-support (~> 3.
|
156
|
-
rspec-mocks (3.
|
170
|
+
rspec-support (~> 3.12.0)
|
171
|
+
rspec-mocks (3.12.0)
|
157
172
|
diff-lcs (>= 1.2.0, < 2.0)
|
158
|
-
rspec-support (~> 3.
|
159
|
-
rspec-rails (
|
160
|
-
actionpack (>=
|
161
|
-
activesupport (>=
|
162
|
-
railties (>=
|
163
|
-
rspec-core (~> 3.
|
164
|
-
rspec-expectations (~> 3.
|
165
|
-
rspec-mocks (~> 3.
|
166
|
-
rspec-support (~> 3.
|
167
|
-
rspec-support (3.
|
173
|
+
rspec-support (~> 3.12.0)
|
174
|
+
rspec-rails (6.0.1)
|
175
|
+
actionpack (>= 6.1)
|
176
|
+
activesupport (>= 6.1)
|
177
|
+
railties (>= 6.1)
|
178
|
+
rspec-core (~> 3.11)
|
179
|
+
rspec-expectations (~> 3.11)
|
180
|
+
rspec-mocks (~> 3.11)
|
181
|
+
rspec-support (~> 3.11)
|
182
|
+
rspec-support (3.12.0)
|
168
183
|
rubocop (0.81.0)
|
169
184
|
jaro_winkler (~> 1.5.1)
|
170
185
|
parallel (~> 1.10)
|
@@ -173,27 +188,25 @@ GEM
|
|
173
188
|
rexml
|
174
189
|
ruby-progressbar (~> 1.7)
|
175
190
|
unicode-display_width (>= 1.4.0, < 2.0)
|
176
|
-
ruby-next-core (0.
|
191
|
+
ruby-next-core (0.15.3)
|
177
192
|
ruby-progressbar (1.11.0)
|
178
|
-
sniffer (0.
|
179
|
-
active_attr (>= 0.10.2)
|
193
|
+
sniffer (0.5.0)
|
180
194
|
anyway_config (>= 1.0)
|
181
|
-
|
195
|
+
dry-initializer (~> 3)
|
196
|
+
sqlite3 (1.5.3)
|
197
|
+
mini_portile2 (~> 2.8.0)
|
198
|
+
thor (1.2.1)
|
199
|
+
timeout (0.3.0)
|
200
|
+
tzinfo (2.0.5)
|
182
201
|
concurrent-ruby (~> 1.0)
|
183
|
-
|
184
|
-
|
185
|
-
actionpack (>= 4.0)
|
186
|
-
activesupport (>= 4.0)
|
187
|
-
sprockets (>= 3.0.0)
|
188
|
-
sqlite3 (1.4.2)
|
189
|
-
thor (1.1.0)
|
190
|
-
tzinfo (2.0.4)
|
191
|
-
concurrent-ruby (~> 1.0)
|
192
|
-
unicode-display_width (1.7.0)
|
202
|
+
unicode-display_width (1.8.0)
|
203
|
+
webrick (1.7.0)
|
193
204
|
websocket-driver (0.7.5)
|
194
205
|
websocket-extensions (>= 0.1.0)
|
195
206
|
websocket-extensions (0.1.5)
|
196
|
-
|
207
|
+
yard (0.9.28)
|
208
|
+
webrick (~> 1.7.0)
|
209
|
+
zeitwerk (2.6.1)
|
197
210
|
|
198
211
|
PLATFORMS
|
199
212
|
ruby
|
@@ -204,12 +217,14 @@ DEPENDENCIES
|
|
204
217
|
bundler (~> 2.0)
|
205
218
|
isolator (~> 0.7)
|
206
219
|
pry
|
220
|
+
pry-byebug
|
207
221
|
rails
|
208
222
|
rake (~> 13.0)
|
209
223
|
rspec (~> 3.0)
|
210
224
|
rspec-rails
|
211
225
|
rubocop (~> 0.81.0)
|
212
226
|
sqlite3 (~> 1.3, >= 1.3.6)
|
227
|
+
yard
|
213
228
|
|
214
229
|
BUNDLED WITH
|
215
|
-
2.
|
230
|
+
2.3.16
|
data/README.md
CHANGED
@@ -111,13 +111,89 @@ Will be executed right after transaction in which it have been declared was roll
|
|
111
111
|
|
112
112
|
If called outside transaction will raise an exception!
|
113
113
|
|
114
|
-
Please keep in mind ActiveRecord's [limitations for rolling back nested transactions](http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html#module-ActiveRecord::Transactions::ClassMethods-label-Nested+transactions).
|
114
|
+
Please keep in mind ActiveRecord's [limitations for rolling back nested transactions](http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html#module-ActiveRecord::Transactions::ClassMethods-label-Nested+transactions). See [`in_transaction`](#in_transaction) for a workaround to this limitation.
|
115
115
|
|
116
116
|
### Available helper methods
|
117
117
|
|
118
|
+
#### `in_transaction`
|
119
|
+
|
120
|
+
Makes sure the provided block is running in a transaction.
|
121
|
+
|
122
|
+
This method aims to provide clearer intention than a typical `ActiveRecord::Base.transaction` block - `in_transaction` only cares that _some_ transaction is present, not that a transaction is nested in any way.
|
123
|
+
|
124
|
+
If a transaction is present, it will yield without taking any action. Note that this means `ActiveRecord::Rollback` errors will not be trapped by `in_transaction` but will propagate up to the nearest parent transaction block.
|
125
|
+
|
126
|
+
If no transaction is present, the provided block will open a new transaction.
|
127
|
+
|
128
|
+
```rb
|
129
|
+
class ServiceObjectBtw
|
130
|
+
include AfterCommitEverywhere
|
131
|
+
|
132
|
+
def call
|
133
|
+
in_transaction do
|
134
|
+
an_update
|
135
|
+
another_update
|
136
|
+
after_commit { puts "We're all done!" }
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
```
|
141
|
+
|
142
|
+
Our service object can run its database operations safely when run in isolation.
|
143
|
+
|
144
|
+
```rb
|
145
|
+
ServiceObjectBtw.new.call # This opens a new #transaction block
|
146
|
+
```
|
147
|
+
|
148
|
+
If it is later called from code already wrapped in a transaction, the existing transaction will be utilized without any nesting:
|
149
|
+
|
150
|
+
```rb
|
151
|
+
ActiveRecord::Base.transaction do
|
152
|
+
new_update
|
153
|
+
next_update
|
154
|
+
# This no longer opens a new #transaction block, because one is already present
|
155
|
+
ServiceObjectBtw.new.call
|
156
|
+
end
|
157
|
+
```
|
158
|
+
|
159
|
+
This can be called directly on the module as well:
|
160
|
+
|
161
|
+
```rb
|
162
|
+
AfterCommitEverywhere.in_transaction do
|
163
|
+
AfterCommitEverywhere.after_commit { puts "We're all done!" }
|
164
|
+
end
|
165
|
+
```
|
166
|
+
|
118
167
|
#### `in_transaction?`
|
119
168
|
|
120
|
-
Returns `true` when called inside open transaction, `false` otherwise.
|
169
|
+
Returns `true` when called inside an open transaction, `false` otherwise.
|
170
|
+
|
171
|
+
```rb
|
172
|
+
def check_for_transaction
|
173
|
+
if in_transaction?
|
174
|
+
puts "We're in a transaction!"
|
175
|
+
else
|
176
|
+
puts "We're not in a transaction..."
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
check_for_transaction
|
181
|
+
# => prints "We're not in a transaction..."
|
182
|
+
|
183
|
+
in_transaction do
|
184
|
+
check_for_transaction
|
185
|
+
end
|
186
|
+
# => prints "We're in a transaction!"
|
187
|
+
```
|
188
|
+
|
189
|
+
### Available callback options
|
190
|
+
|
191
|
+
- `without_tx` allows to change default callback behavior if called without transaction open.
|
192
|
+
|
193
|
+
Available values:
|
194
|
+
- `:execute` to execute callback immediately
|
195
|
+
- `:warn_and_execute` to print warning and execute immediately
|
196
|
+
- `:raise` to raise an exception instead of executing
|
121
197
|
|
122
198
|
### FAQ
|
123
199
|
|
@@ -171,7 +247,7 @@ class Post < ActiveRecord::Base
|
|
171
247
|
end
|
172
248
|
```
|
173
249
|
|
174
|
-
However, if you do something in models that requires defining such ad-hoc transactional callbacks, it may indicate that your models have too many responsibilities and these methods should be extracted to separate
|
250
|
+
However, if you do something in models that requires defining such ad-hoc transactional callbacks, it may indicate that your models have too many responsibilities and these methods should be extracted to separate specialized layers (service objects, etc).
|
175
251
|
|
176
252
|
## Development
|
177
253
|
|
@@ -34,10 +34,12 @@ Gem::Specification.new do |spec|
|
|
34
34
|
spec.add_development_dependency "bundler", "~> 2.0"
|
35
35
|
spec.add_development_dependency "isolator", "~> 0.7"
|
36
36
|
spec.add_development_dependency "pry"
|
37
|
+
spec.add_development_dependency "pry-byebug"
|
37
38
|
spec.add_development_dependency "rails"
|
38
39
|
spec.add_development_dependency "rake", "~> 13.0"
|
39
40
|
spec.add_development_dependency "rspec", "~> 3.0"
|
40
41
|
spec.add_development_dependency "rspec-rails"
|
41
42
|
spec.add_development_dependency "rubocop", "~> 0.81.0"
|
42
43
|
spec.add_development_dependency "sqlite3", "~> 1.3", ">= 1.3.6"
|
44
|
+
spec.add_development_dependency "yard"
|
43
45
|
end
|
@@ -14,36 +14,60 @@ module AfterCommitEverywhere
|
|
14
14
|
class NotInTransaction < RuntimeError; end
|
15
15
|
|
16
16
|
delegate :after_commit, :before_commit, :after_rollback, to: AfterCommitEverywhere
|
17
|
-
delegate :in_transaction?, to: AfterCommitEverywhere
|
17
|
+
delegate :in_transaction?, :in_transaction, to: AfterCommitEverywhere
|
18
|
+
|
19
|
+
# Causes {before_commit} and {after_commit} to raise an exception when
|
20
|
+
# called outside a transaction.
|
21
|
+
RAISE = :raise
|
22
|
+
# Causes {before_commit} and {after_commit} to execute the given callback
|
23
|
+
# immediately when called outside a transaction.
|
24
|
+
EXECUTE = :execute
|
25
|
+
# Causes {before_commit} and {after_commit} to log a warning before calling
|
26
|
+
# the given callback immediately when called outside a transaction.
|
27
|
+
WARN_AND_EXECUTE = :warn_and_execute
|
18
28
|
|
19
29
|
class << self
|
20
30
|
# Runs +callback+ after successful commit of outermost transaction for
|
21
31
|
# database +connection+.
|
22
32
|
#
|
23
|
-
#
|
33
|
+
# @param connection [ActiveRecord::ConnectionAdapters::AbstractAdapter] Database connection to operate in. Defaults to +ActiveRecord::Base.connection+
|
34
|
+
# @param without_tx [Symbol] Determines the behavior of this function when
|
35
|
+
# called without an open transaction.
|
36
|
+
#
|
37
|
+
# Must be one of: {RAISE}, {EXECUTE}, or {WARN_AND_EXECUTE}.
|
24
38
|
#
|
25
|
-
# @param connection [ActiveRecord::ConnectionAdapters::AbstractAdapter]
|
26
39
|
# @param callback [#call] Callback to be executed
|
27
40
|
# @return void
|
28
|
-
def after_commit(
|
41
|
+
def after_commit(
|
42
|
+
connection: nil,
|
43
|
+
without_tx: EXECUTE,
|
44
|
+
&callback
|
45
|
+
)
|
29
46
|
register_callback(
|
30
47
|
connection: connection,
|
31
48
|
name: __method__,
|
32
49
|
callback: callback,
|
33
|
-
|
50
|
+
without_tx: without_tx,
|
34
51
|
)
|
35
52
|
end
|
36
53
|
|
37
54
|
# Runs +callback+ before committing of outermost transaction for +connection+.
|
38
55
|
#
|
39
|
-
# If called outside transaction it will execute callback immediately.
|
40
|
-
#
|
41
56
|
# Available only since Ruby on Rails 5.0. See https://github.com/rails/rails/pull/18936
|
42
57
|
#
|
43
|
-
# @param connection [ActiveRecord::ConnectionAdapters::AbstractAdapter]
|
58
|
+
# @param connection [ActiveRecord::ConnectionAdapters::AbstractAdapter] Database connection to operate in. Defaults to +ActiveRecord::Base.connection+
|
59
|
+
# @param without_tx [Symbol] Determines the behavior of this function when
|
60
|
+
# called without an open transaction.
|
61
|
+
#
|
62
|
+
# Must be one of: {RAISE}, {EXECUTE}, or {WARN_AND_EXECUTE}.
|
63
|
+
#
|
44
64
|
# @param callback [#call] Callback to be executed
|
45
65
|
# @return void
|
46
|
-
def before_commit(
|
66
|
+
def before_commit(
|
67
|
+
connection: nil,
|
68
|
+
without_tx: WARN_AND_EXECUTE,
|
69
|
+
&callback
|
70
|
+
)
|
47
71
|
if ActiveRecord::VERSION::MAJOR < 5
|
48
72
|
raise NotImplementedError, "#{__method__} works only with Rails 5.0+"
|
49
73
|
end
|
@@ -52,7 +76,7 @@ module AfterCommitEverywhere
|
|
52
76
|
connection: connection,
|
53
77
|
name: __method__,
|
54
78
|
callback: callback,
|
55
|
-
|
79
|
+
without_tx: without_tx,
|
56
80
|
)
|
57
81
|
end
|
58
82
|
|
@@ -62,42 +86,76 @@ module AfterCommitEverywhere
|
|
62
86
|
# Caveat: do not raise +ActivRecord::Rollback+ in nested transaction block!
|
63
87
|
# See http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html#module-ActiveRecord::Transactions::ClassMethods-label-Nested+transactions
|
64
88
|
#
|
65
|
-
# @param connection [ActiveRecord::ConnectionAdapters::AbstractAdapter]
|
89
|
+
# @param connection [ActiveRecord::ConnectionAdapters::AbstractAdapter] Database connection to operate in. Defaults to +ActiveRecord::Base.connection+
|
66
90
|
# @param callback [#call] Callback to be executed
|
67
91
|
# @return void
|
68
92
|
# @raise [NotInTransaction] if called outside transaction.
|
69
|
-
def after_rollback(connection:
|
93
|
+
def after_rollback(connection: nil, &callback)
|
70
94
|
register_callback(
|
71
95
|
connection: connection,
|
72
96
|
name: __method__,
|
73
97
|
callback: callback,
|
74
|
-
|
98
|
+
without_tx: RAISE,
|
75
99
|
)
|
76
100
|
end
|
77
101
|
|
78
102
|
# @api private
|
79
|
-
def register_callback(connection
|
103
|
+
def register_callback(connection: nil, name:, without_tx:, callback:)
|
80
104
|
raise ArgumentError, "Provide callback to #{name}" unless callback
|
81
105
|
|
82
106
|
unless in_transaction?(connection)
|
83
|
-
case
|
84
|
-
when
|
107
|
+
case without_tx
|
108
|
+
when WARN_AND_EXECUTE
|
85
109
|
warn "#{name}: No transaction open. Executing callback immediately."
|
86
110
|
return callback.call
|
87
|
-
when
|
111
|
+
when EXECUTE
|
88
112
|
return callback.call
|
89
|
-
when
|
113
|
+
when RAISE
|
90
114
|
raise NotInTransaction, "#{name} is useless outside transaction"
|
115
|
+
else
|
116
|
+
raise ArgumentError, "Invalid \"without_tx\": \"#{without_tx}\""
|
91
117
|
end
|
92
118
|
end
|
119
|
+
|
120
|
+
connection ||= default_connection
|
93
121
|
wrap = Wrap.new(connection: connection, "#{name}": callback)
|
94
122
|
connection.add_transaction_record(wrap)
|
95
123
|
end
|
96
124
|
|
97
125
|
# Helper method to determine whether we're currently in transaction or not
|
98
|
-
def in_transaction?(connection =
|
126
|
+
def in_transaction?(connection = nil)
|
127
|
+
# Don't establish new connection if not connected: we apparently not in transaction
|
128
|
+
return false unless connection || ActiveRecord::Base.connection_pool.active_connection?
|
129
|
+
|
130
|
+
connection ||= default_connection
|
99
131
|
# service transactions (tests and database_cleaner) are not joinable
|
100
132
|
connection.transaction_open? && connection.current_transaction.joinable?
|
101
133
|
end
|
134
|
+
|
135
|
+
# Makes sure the provided block runs in a transaction. If we are not currently in a transaction, a new transaction is started.
|
136
|
+
#
|
137
|
+
# It mimics the ActiveRecord's +transaction+ method's API and actually uses it under the hood.
|
138
|
+
#
|
139
|
+
# However, the main difference is that it doesn't swallow +ActiveRecord::Rollback+ exception in case when there is no transaction open.
|
140
|
+
#
|
141
|
+
# @see https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/DatabaseStatements.html#method-i-transaction
|
142
|
+
#
|
143
|
+
# @param connection [ActiveRecord::ConnectionAdapters::AbstractAdapter] Database connection to operate in. Defaults to +ActiveRecord::Base.connection+
|
144
|
+
# @param requires_new [Boolean] Forces creation of new subtransaction (savepoint) even if transaction is already opened.
|
145
|
+
# @param new_tx_options [Hash<Symbol, void>] Options to be passed to +connection.transaction+ on new transaction creation
|
146
|
+
# @return void
|
147
|
+
def in_transaction(connection = default_connection, requires_new: false, **new_tx_options)
|
148
|
+
if in_transaction?(connection) && !requires_new
|
149
|
+
yield
|
150
|
+
else
|
151
|
+
connection.transaction(requires_new: requires_new, **new_tx_options) { yield }
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
private
|
156
|
+
|
157
|
+
def default_connection
|
158
|
+
ActiveRecord::Base.connection
|
159
|
+
end
|
102
160
|
end
|
103
161
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: after_commit_everywhere
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrey Novikov
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-10-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -94,6 +94,20 @@ dependencies:
|
|
94
94
|
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: pry-byebug
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
97
111
|
- !ruby/object:Gem::Dependency
|
98
112
|
name: rails
|
99
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -184,6 +198,20 @@ dependencies:
|
|
184
198
|
- - ">="
|
185
199
|
- !ruby/object:Gem::Version
|
186
200
|
version: 1.3.6
|
201
|
+
- !ruby/object:Gem::Dependency
|
202
|
+
name: yard
|
203
|
+
requirement: !ruby/object:Gem::Requirement
|
204
|
+
requirements:
|
205
|
+
- - ">="
|
206
|
+
- !ruby/object:Gem::Version
|
207
|
+
version: '0'
|
208
|
+
type: :development
|
209
|
+
prerelease: false
|
210
|
+
version_requirements: !ruby/object:Gem::Requirement
|
211
|
+
requirements:
|
212
|
+
- - ">="
|
213
|
+
- !ruby/object:Gem::Version
|
214
|
+
version: '0'
|
187
215
|
description: Brings before_commit, after_commit, and after_rollback transactional
|
188
216
|
callbacks outside of your ActiveRecord models.
|
189
217
|
email:
|
@@ -214,6 +242,7 @@ files:
|
|
214
242
|
- gemfiles/activerecord_5_2.gemfile
|
215
243
|
- gemfiles/activerecord_6_0.gemfile
|
216
244
|
- gemfiles/activerecord_6_1.gemfile
|
245
|
+
- gemfiles/activerecord_7_0.gemfile
|
217
246
|
- gemfiles/activerecord_master.gemfile
|
218
247
|
- lib/after_commit_everywhere.rb
|
219
248
|
- lib/after_commit_everywhere/version.rb
|