fish_transactions 1.0.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 +7 -0
- data/MIT-LICENSE.txt +21 -0
- data/README.md +90 -0
- data/Rakefile +12 -0
- data/lib/fish_transactions/active_record_mods/base.rb +67 -0
- data/lib/fish_transactions/active_record_mods/connection_abstract_adapter.rb +20 -0
- data/lib/fish_transactions/active_record_mods.rb +6 -0
- data/lib/fish_transactions/callbacks.rb +102 -0
- data/lib/fish_transactions/callbacks_storage.rb +58 -0
- data/lib/fish_transactions/version.rb +6 -0
- data/lib/fish_transactions.rb +4 -0
- data/spec/callbacks_spec.rb +332 -0
- data/spec/callbacks_storage_spec.rb +119 -0
- data/spec/spec_helper.rb +25 -0
- metadata +159 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a822130f177c9753a9d30a248208ae38d46e29c7
|
4
|
+
data.tar.gz: 051ec846fc08b2a295d8f88601c4da608c73f7f0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5c9a9eb96340b83db54f2f20679616af083b25bd02892c6aceb5438de74c2a5318c3741ba26ea81679d8c5e5d02dd5cc453949d6f1779073382e6f20145d1ec6
|
7
|
+
data.tar.gz: d7a17433321b20d4ccea1320b5c956efe518e514c5e0c814867fbcd8040ce110697692901f66f7433830b698581235c78fcada6815876fc80bbc63b1eb09d4fc
|
data/MIT-LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 John Owen
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
# FishTransactions
|
2
|
+
|
3
|
+
Fish Transactions allows to add transaction callbacks anywhere.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'fish_transactions'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install fish_transactions
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
First you need to include `FishTransactions#Callbacks` module into the class
|
24
|
+
you want to use it.
|
25
|
+
Once included, you can directly call `after_commit` or `after_rollback` with
|
26
|
+
the code you wanna run on such events.
|
27
|
+
|
28
|
+
|
29
|
+
#### Example
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
class SomeClass
|
33
|
+
|
34
|
+
include FishTransaction::Callbacks
|
35
|
+
|
36
|
+
# ...
|
37
|
+
|
38
|
+
def some_method
|
39
|
+
# ...
|
40
|
+
|
41
|
+
ActiveRecord::Base.transaction do
|
42
|
+
# executes some code
|
43
|
+
puts "runs within transaction"
|
44
|
+
|
45
|
+
after_commit do
|
46
|
+
|
47
|
+
# things to do after transaction
|
48
|
+
puts "runs after transaction"
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
# executes more code
|
53
|
+
puts "again runs within transaction"
|
54
|
+
end
|
55
|
+
# ...
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
# ...
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
```
|
64
|
+
will output
|
65
|
+
```
|
66
|
+
runs within transaction
|
67
|
+
again runs within transaction
|
68
|
+
runs after transaction
|
69
|
+
```
|
70
|
+
|
71
|
+
Please see [FishTransactions::Callbacks](FishTransactions/Callbacks.html) for more details.
|
72
|
+
|
73
|
+
## Contributing
|
74
|
+
|
75
|
+
Bug reports and pull requests are welcome on GitHub at
|
76
|
+
[beetrack/fish_transactions](https://github.com/beetrack/fish_transactions). This project is intended to be
|
77
|
+
a safe, welcoming space for collaboration, and contributors are expected to
|
78
|
+
adhere to the [Contributor Covenant](http://contributor-covenant.org) code
|
79
|
+
of conduct.
|
80
|
+
|
81
|
+
|
82
|
+
## Development
|
83
|
+
|
84
|
+
This gem is inspired on [AR After Transaction gem](https://github.com/grosser/ar_after_transaction).
|
85
|
+
|
86
|
+
|
87
|
+
## License
|
88
|
+
|
89
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
90
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require 'rdoc/task'
|
3
|
+
task :default => :spec
|
4
|
+
|
5
|
+
desc 'Generate documentation for FishTransactions.'
|
6
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
7
|
+
rdoc.rdoc_dir = 'doc'
|
8
|
+
rdoc.title = 'Fish Transactions'
|
9
|
+
rdoc.rdoc_files.include('README.md')
|
10
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
11
|
+
rdoc.main = 'README.md'
|
12
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'fish_transactions/callbacks_storage'
|
2
|
+
|
3
|
+
module FishTransactions
|
4
|
+
|
5
|
+
##
|
6
|
+
# Module that modifies ActiveRecord transactions to capture events
|
7
|
+
module ActiveRecordMods
|
8
|
+
##
|
9
|
+
# Modifications for ActiveRecord::Base class
|
10
|
+
# All methods here will be class level methods
|
11
|
+
module Base
|
12
|
+
|
13
|
+
##
|
14
|
+
# When extended, this module will:
|
15
|
+
#
|
16
|
+
# * alias the +:transaction+ method and them override it
|
17
|
+
def self.extended( base )
|
18
|
+
|
19
|
+
base.class_eval do
|
20
|
+
class << self
|
21
|
+
# create an alias for the original +transaction+ method
|
22
|
+
alias_method :original_ar_transaction, :transaction
|
23
|
+
# and we 'rename' the new method as transaction
|
24
|
+
alias_method :transaction, :transaction_with_callbacks
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
|
32
|
+
##
|
33
|
+
# Wrapper of original transaction.
|
34
|
+
# Captures and then raises Rollback exception
|
35
|
+
# to know there were a rollback
|
36
|
+
def transaction_with_callbacks(*args)
|
37
|
+
|
38
|
+
committed = true
|
39
|
+
|
40
|
+
original_ar_transaction(*args) do
|
41
|
+
begin
|
42
|
+
yield
|
43
|
+
rescue ActiveRecord::Rollback
|
44
|
+
committed = false
|
45
|
+
raise
|
46
|
+
end
|
47
|
+
end
|
48
|
+
rescue Exception
|
49
|
+
committed = false
|
50
|
+
raise
|
51
|
+
ensure
|
52
|
+
if committed
|
53
|
+
callbacks.committed!
|
54
|
+
else
|
55
|
+
callbacks.rolledback!
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
##
|
60
|
+
# read-access to callbacks storage
|
61
|
+
def callbacks
|
62
|
+
connection.fish_callbacks_storage ||= FishTransactions::CallbacksStorage.new
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module FishTransactions
|
2
|
+
|
3
|
+
# :nodoc:
|
4
|
+
module ActiveRecordMods
|
5
|
+
|
6
|
+
##
|
7
|
+
# Modifications for ActiveRecord::ConnectionAdapters::AbstractAdapter class
|
8
|
+
module ConnectionAbstractAdapter
|
9
|
+
|
10
|
+
##
|
11
|
+
# When included this module, will add:
|
12
|
+
#
|
13
|
+
# * <tt>attr_accessor fish_callbacks_storage</tt>
|
14
|
+
def self.included(base)
|
15
|
+
base.send(:attr_accessor,:fish_callbacks_storage)
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
require 'fish_transactions/active_record_mods/connection_abstract_adapter'
|
2
|
+
require 'fish_transactions/active_record_mods/base'
|
3
|
+
|
4
|
+
# :nodoc: all
|
5
|
+
ActiveRecord::Base.extend FishTransactions::ActiveRecordMods::Base
|
6
|
+
ActiveRecord::ConnectionAdapters::AbstractAdapter.send(:include, FishTransactions::ActiveRecordMods::ConnectionAbstractAdapter)
|
@@ -0,0 +1,102 @@
|
|
1
|
+
module FishTransactions
|
2
|
+
|
3
|
+
##
|
4
|
+
# = Fish Transactions Callbacks
|
5
|
+
#
|
6
|
+
# This module allows to add transaction callbacks anywhere.
|
7
|
+
# You just need to include this module and you will able to
|
8
|
+
# use it.
|
9
|
+
#
|
10
|
+
module Callbacks
|
11
|
+
|
12
|
+
##
|
13
|
+
# Allows to execute any block of code after transaction completes.
|
14
|
+
# If no transaction is actually open, the code runs immediately.
|
15
|
+
#
|
16
|
+
# Accepts the following options that modifies this behavior:
|
17
|
+
#
|
18
|
+
# * <tt>:only</tt> - Execute this code only on commit or only on
|
19
|
+
# rollback. Accepts one of the following symbols: <tt>:commit</tt>,
|
20
|
+
# <tt>:rollback</tt>.
|
21
|
+
# * <tt>:if_no_transaction</tt> - Specifies what to do if there is no
|
22
|
+
# active transaction. Accepts one of the following symbols:
|
23
|
+
# <tt>:run</tt> (default), <tt>:skip</tt> (do not run).
|
24
|
+
#
|
25
|
+
# Example of use:
|
26
|
+
#
|
27
|
+
# ActiveRecord::Base.transaction do
|
28
|
+
# # executes some code
|
29
|
+
# puts "runs within transaction"
|
30
|
+
# after_transaction do
|
31
|
+
# # things to do after transaction
|
32
|
+
# puts "runs after transaction"
|
33
|
+
# end
|
34
|
+
# # executes more code
|
35
|
+
# puts "again runs within transaction"
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# will output
|
39
|
+
#
|
40
|
+
# runs within transaction
|
41
|
+
# again runs within transaction
|
42
|
+
# runs after transaction
|
43
|
+
#
|
44
|
+
#
|
45
|
+
#
|
46
|
+
def after_transaction(opts = {}, &block)
|
47
|
+
|
48
|
+
# default options
|
49
|
+
default_options = { only: nil, if_no_transaction: :run}
|
50
|
+
opts = default_options.merge(opts)
|
51
|
+
|
52
|
+
# normalize opts to string keys
|
53
|
+
normalized = opts.dup
|
54
|
+
opts.each{ |k,v| normalized[k.to_s] = v }
|
55
|
+
opts = normalized
|
56
|
+
|
57
|
+
|
58
|
+
if ActiveRecord::Base.connection.open_transactions > 0
|
59
|
+
callbacks = ActiveRecord::Base.callbacks
|
60
|
+
|
61
|
+
case opts['only']
|
62
|
+
when :commit
|
63
|
+
callbacks.store(:commit,&block)
|
64
|
+
when :rollback
|
65
|
+
callbacks.store(:rollback,&block)
|
66
|
+
else
|
67
|
+
# both cases
|
68
|
+
callbacks.store(:commit,&block)
|
69
|
+
callbacks.store(:rollback,&block)
|
70
|
+
end
|
71
|
+
else
|
72
|
+
if opts['if_no_transaction'] == :run
|
73
|
+
block.call
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
alias after_tx after_transaction
|
78
|
+
|
79
|
+
##
|
80
|
+
# Executes some code only after current transactions does commit.
|
81
|
+
# If no transaction is actually open, the code runs immediately.
|
82
|
+
#
|
83
|
+
# Shortcut for <tt>after_transaction(only: :commit, if_no_transaction: :run) do ... end </tt>
|
84
|
+
#
|
85
|
+
# Please use #after_transaction for more options
|
86
|
+
def after_commit(&block)
|
87
|
+
after_transaction(only: :commit, &block)
|
88
|
+
end
|
89
|
+
|
90
|
+
##
|
91
|
+
# Executes some code only after current transaction does rollback.
|
92
|
+
# If no transaction is actually open, the code does not runs.
|
93
|
+
#
|
94
|
+
# Shortcut for <tt>after_transaction(only: :rollback, if_no_transaction: :skip) do ... end </tt>
|
95
|
+
#
|
96
|
+
# Please use #after_transaction for more options
|
97
|
+
def after_rollback(&block)
|
98
|
+
after_transaction(only: :rollback, if_no_transaction: :skip, &block)
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module FishTransactions
|
2
|
+
##
|
3
|
+
# Stores callbacks to be run on certain events
|
4
|
+
# (currently, commit-only or rollback-only)
|
5
|
+
class CallbacksStorage
|
6
|
+
|
7
|
+
##
|
8
|
+
# Creates the storage empty
|
9
|
+
def initialize
|
10
|
+
@on_commit = []
|
11
|
+
@on_rollback = []
|
12
|
+
end
|
13
|
+
|
14
|
+
##
|
15
|
+
# Stores a callback block to run on a event
|
16
|
+
#
|
17
|
+
# An event must be one of these symbols:
|
18
|
+
#
|
19
|
+
# * +:commit+ - run callback on commit only
|
20
|
+
# * +:rollback+ - run callback on rollback only
|
21
|
+
# * otherwise will raise an exception
|
22
|
+
def store(event,&callback_block)
|
23
|
+
case event
|
24
|
+
when :commit then @on_commit << callback_block
|
25
|
+
when :rollback then @on_rollback << callback_block
|
26
|
+
else raise "unknown event #{event.inspect}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
##
|
31
|
+
# Runs all callbacks asociated with commit event
|
32
|
+
def committed!
|
33
|
+
@on_commit.each do |callback|
|
34
|
+
callback.call
|
35
|
+
end
|
36
|
+
clear
|
37
|
+
end
|
38
|
+
|
39
|
+
##
|
40
|
+
# Runs all callbacks asociated with rollback event
|
41
|
+
def rolledback!
|
42
|
+
@on_rollback.each do |callback|
|
43
|
+
callback.call
|
44
|
+
end
|
45
|
+
clear
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
##
|
51
|
+
# Clear callbacks
|
52
|
+
def clear
|
53
|
+
@on_commit.clear
|
54
|
+
@on_rollback.clear
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,332 @@
|
|
1
|
+
require 'fish_transactions'
|
2
|
+
|
3
|
+
module FishTransactions
|
4
|
+
|
5
|
+
class TestCallbacksClass
|
6
|
+
include Callbacks
|
7
|
+
|
8
|
+
def run_with_after_commit
|
9
|
+
before_the_block
|
10
|
+
|
11
|
+
after_commit do
|
12
|
+
the_after_commit_block
|
13
|
+
|
14
|
+
end
|
15
|
+
after_the_block_just_before_commit
|
16
|
+
end
|
17
|
+
|
18
|
+
def run_with_after_rollback
|
19
|
+
before_the_block
|
20
|
+
|
21
|
+
after_rollback do
|
22
|
+
the_after_rollback_block
|
23
|
+
|
24
|
+
end
|
25
|
+
after_the_block_just_before_commit
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
def run_with_after_transaction_no_args
|
30
|
+
before_the_block
|
31
|
+
|
32
|
+
after_transaction do
|
33
|
+
the_after_transaction_block
|
34
|
+
|
35
|
+
end
|
36
|
+
after_the_block_just_before_commit
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
def run_with_after_transaction(opts)
|
41
|
+
before_the_block
|
42
|
+
|
43
|
+
after_transaction(opts) do
|
44
|
+
the_after_transaction_block
|
45
|
+
|
46
|
+
end
|
47
|
+
after_the_block_just_before_commit
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
def before_the_block; end
|
52
|
+
def after_the_block_just_before_commit; end
|
53
|
+
|
54
|
+
def the_after_commit_block; end
|
55
|
+
def the_after_rollback_block; end
|
56
|
+
def the_after_transaction_block; end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
describe Callbacks do
|
61
|
+
|
62
|
+
let(:execution_double) do
|
63
|
+
double = TestCallbacksClass.new
|
64
|
+
allow(double).to receive(:run).and_call_original
|
65
|
+
double
|
66
|
+
end
|
67
|
+
|
68
|
+
describe '#after_commit' do
|
69
|
+
|
70
|
+
context 'without transaction' do
|
71
|
+
|
72
|
+
it 'should run in place' do
|
73
|
+
|
74
|
+
expect(execution_double).to receive_in_order(
|
75
|
+
:before_the_block,
|
76
|
+
:the_after_commit_block,
|
77
|
+
:after_the_block_just_before_commit
|
78
|
+
)
|
79
|
+
|
80
|
+
execution_double.run_with_after_commit
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context 'within a transaction' do
|
86
|
+
|
87
|
+
it 'should wait transaction to commit' do
|
88
|
+
|
89
|
+
expect(execution_double).to receive_in_order(
|
90
|
+
:before_the_block,
|
91
|
+
:after_the_block_just_before_commit,
|
92
|
+
:the_after_commit_block
|
93
|
+
)
|
94
|
+
|
95
|
+
ActiveRecord::Base.transaction do
|
96
|
+
execution_double.run_with_after_commit
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'should not run on rollback' do
|
102
|
+
expect(execution_double).to receive_in_order(
|
103
|
+
:before_the_block,
|
104
|
+
:after_the_block_just_before_commit
|
105
|
+
).but_not(
|
106
|
+
:the_after_commit_block
|
107
|
+
)
|
108
|
+
|
109
|
+
ActiveRecord::Base.transaction do
|
110
|
+
execution_double.run_with_after_commit
|
111
|
+
raise ActiveRecord::Rollback
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
describe '#after_rollback' do
|
120
|
+
|
121
|
+
context 'without transaction' do
|
122
|
+
|
123
|
+
it 'should not run at all' do
|
124
|
+
|
125
|
+
expect(execution_double).to receive_in_order(
|
126
|
+
:before_the_block,
|
127
|
+
:after_the_block_just_before_commit
|
128
|
+
).but_not(
|
129
|
+
:the_after_rollback_block
|
130
|
+
)
|
131
|
+
|
132
|
+
execution_double.run_with_after_rollback
|
133
|
+
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
context 'within a transaction' do
|
138
|
+
|
139
|
+
it 'should not run on commit' do
|
140
|
+
|
141
|
+
expect(execution_double).to receive_in_order(
|
142
|
+
:before_the_block,
|
143
|
+
:after_the_block_just_before_commit
|
144
|
+
).but_not(
|
145
|
+
:the_after_rollback_block
|
146
|
+
)
|
147
|
+
|
148
|
+
ActiveRecord::Base.transaction do
|
149
|
+
execution_double.run_with_after_rollback
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'should run on rollback' do
|
155
|
+
expect(execution_double).to receive_in_order(
|
156
|
+
:before_the_block,
|
157
|
+
:after_the_block_just_before_commit,
|
158
|
+
:the_after_rollback_block
|
159
|
+
)
|
160
|
+
|
161
|
+
ActiveRecord::Base.transaction do
|
162
|
+
execution_double.run_with_after_rollback
|
163
|
+
raise ActiveRecord::Rollback
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
170
|
+
|
171
|
+
describe '#after_transaction' do
|
172
|
+
|
173
|
+
context 'without a transaction' do
|
174
|
+
|
175
|
+
context 'by default' do
|
176
|
+
|
177
|
+
it 'should executes immediately' do
|
178
|
+
expect(execution_double).to receive_in_order(
|
179
|
+
:before_the_block,
|
180
|
+
:the_after_transaction_block,
|
181
|
+
:after_the_block_just_before_commit
|
182
|
+
)
|
183
|
+
execution_double.run_with_after_transaction_no_args
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
context 'and with a {:if_no_transaction => :run} param' do
|
188
|
+
|
189
|
+
it 'should executes inmediately' do
|
190
|
+
expect(execution_double).to receive_in_order(
|
191
|
+
:before_the_block,
|
192
|
+
:the_after_transaction_block,
|
193
|
+
:after_the_block_just_before_commit
|
194
|
+
)
|
195
|
+
execution_double.run_with_after_transaction(if_no_transaction: :run)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
context 'and with a {:if_no_transaction => :skip} param' do
|
200
|
+
it 'should not execute' do
|
201
|
+
expect(execution_double).to receive_in_order(
|
202
|
+
:before_the_block,
|
203
|
+
:after_the_block_just_before_commit
|
204
|
+
).but_not(
|
205
|
+
:the_after_transaction_block
|
206
|
+
)
|
207
|
+
execution_double.run_with_after_transaction(if_no_transaction: :skip)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
context 'and with a {:if_no_transaction => :skip} param given as string' do
|
212
|
+
it 'should not execute' do
|
213
|
+
expect(execution_double).to receive_in_order(
|
214
|
+
:before_the_block,
|
215
|
+
:after_the_block_just_before_commit
|
216
|
+
).but_not(
|
217
|
+
:the_after_transaction_block
|
218
|
+
)
|
219
|
+
execution_double.run_with_after_transaction('if_no_transaction' => 'skip')
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
end
|
224
|
+
|
225
|
+
context 'within a transaction' do
|
226
|
+
|
227
|
+
context 'by default' do
|
228
|
+
it 'should executes after commit' do
|
229
|
+
|
230
|
+
expect(execution_double).to receive_in_order(
|
231
|
+
:before_the_block,
|
232
|
+
:after_the_block_just_before_commit,
|
233
|
+
:the_after_transaction_block
|
234
|
+
)
|
235
|
+
ActiveRecord::Base.transaction do
|
236
|
+
execution_double.run_with_after_transaction_no_args
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
it 'should executes after rollback' do
|
241
|
+
|
242
|
+
expect(execution_double).to receive_in_order(
|
243
|
+
:before_the_block,
|
244
|
+
:after_the_block_just_before_commit,
|
245
|
+
:the_after_transaction_block
|
246
|
+
)
|
247
|
+
ActiveRecord::Base.transaction do
|
248
|
+
execution_double.run_with_after_transaction_no_args
|
249
|
+
raise ActiveRecord::Rollback
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
it 'should executes after failure' do
|
254
|
+
|
255
|
+
expect(execution_double).to receive_in_order(
|
256
|
+
:before_the_block,
|
257
|
+
:after_the_block_just_before_commit,
|
258
|
+
:the_after_transaction_block
|
259
|
+
)
|
260
|
+
begin
|
261
|
+
ActiveRecord::Base.transaction do
|
262
|
+
execution_double.run_with_after_transaction_no_args
|
263
|
+
raise "Some other exception"
|
264
|
+
end
|
265
|
+
rescue # doesn't matter
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
context 'and with a {only: :commit} param' do
|
271
|
+
it 'should executes after commit' do
|
272
|
+
|
273
|
+
expect(execution_double).to receive_in_order(
|
274
|
+
:before_the_block,
|
275
|
+
:after_the_block_just_before_commit,
|
276
|
+
:the_after_transaction_block
|
277
|
+
)
|
278
|
+
ActiveRecord::Base.transaction do
|
279
|
+
execution_double.run_with_after_transaction(only: :commit)
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
it 'should not executes after rollback' do
|
284
|
+
|
285
|
+
expect(execution_double).to receive_in_order(
|
286
|
+
:before_the_block,
|
287
|
+
:after_the_block_just_before_commit
|
288
|
+
).but_not(
|
289
|
+
:the_after_transaction_block
|
290
|
+
)
|
291
|
+
ActiveRecord::Base.transaction do
|
292
|
+
execution_double.run_with_after_transaction(only: :commit)
|
293
|
+
raise ActiveRecord::Rollback
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
|
299
|
+
context 'and with a {only: :rollback} param' do
|
300
|
+
it 'should not executes after commit' do
|
301
|
+
|
302
|
+
expect(execution_double).to receive_in_order(
|
303
|
+
:before_the_block,
|
304
|
+
:after_the_block_just_before_commit
|
305
|
+
).but_not(
|
306
|
+
:the_after_transaction_block
|
307
|
+
)
|
308
|
+
ActiveRecord::Base.transaction do
|
309
|
+
execution_double.run_with_after_transaction(only: :rollback)
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
it 'should executes after rollback' do
|
314
|
+
|
315
|
+
expect(execution_double).to receive_in_order(
|
316
|
+
:before_the_block,
|
317
|
+
:after_the_block_just_before_commit,
|
318
|
+
:the_after_transaction_block
|
319
|
+
)
|
320
|
+
ActiveRecord::Base.transaction do
|
321
|
+
execution_double.run_with_after_transaction(only: :rollback)
|
322
|
+
raise ActiveRecord::Rollback
|
323
|
+
end
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
end
|
328
|
+
|
329
|
+
end
|
330
|
+
|
331
|
+
end
|
332
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'fish_transactions/callbacks_storage'
|
2
|
+
|
3
|
+
module FishTransactions
|
4
|
+
|
5
|
+
describe CallbacksStorage do
|
6
|
+
|
7
|
+
|
8
|
+
let(:null_proc){ Proc.new{} }
|
9
|
+
subject(:storage){ CallbacksStorage.new }
|
10
|
+
|
11
|
+
# just a warm test: to raise or not to raise error
|
12
|
+
describe '#store' do
|
13
|
+
it 'accepts commit event' do
|
14
|
+
# just run and hope no exception raised
|
15
|
+
storage.store(:commit,&null_proc)
|
16
|
+
end
|
17
|
+
it 'accepts commit event' do
|
18
|
+
storage.store(:rollback,&null_proc)
|
19
|
+
end
|
20
|
+
it 'does not accepts other events' do
|
21
|
+
# I prefer to repeat this code lot of times to be more clear than DRYer
|
22
|
+
|
23
|
+
# test tx AR events
|
24
|
+
expect { storage.store(:after_commit,&null_proc) }.to raise_error(RuntimeError, /^unknown event.+$/)
|
25
|
+
expect { storage.store(:after_rollback,&null_proc) }.to raise_error(RuntimeError, /^unknown event.+$/)
|
26
|
+
|
27
|
+
# and some common AR events
|
28
|
+
expect { storage.store(:after_validation,&null_proc) }.to raise_error(RuntimeError, /^unknown event.+$/)
|
29
|
+
expect { storage.store(:after_create,&null_proc) }.to raise_error(RuntimeError, /^unknown event.+$/)
|
30
|
+
expect { storage.store(:after_update,&null_proc) }.to raise_error(RuntimeError, /^unknown event.+$/)
|
31
|
+
expect { storage.store(:after_save,&null_proc) }.to raise_error(RuntimeError, /^unknown event.+$/)
|
32
|
+
|
33
|
+
expect { storage.store(:before_validation,&null_proc) }.to raise_error(RuntimeError, /^unknown event.+$/)
|
34
|
+
expect { storage.store(:before_save,&null_proc) }.to raise_error(RuntimeError, /^unknown event.+$/)
|
35
|
+
expect { storage.store(:before_create,&null_proc) }.to raise_error(RuntimeError, /^unknown event.+$/)
|
36
|
+
expect { storage.store(:before_update,&null_proc) }.to raise_error(RuntimeError, /^unknown event.+$/)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe '#committed!' do
|
41
|
+
|
42
|
+
it 'runs a stored block' do
|
43
|
+
expect do |test_block|
|
44
|
+
storage.store :commit, &test_block
|
45
|
+
storage.committed!
|
46
|
+
end.to yield_control
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'runs any stored block' do
|
50
|
+
expect do |test_block|
|
51
|
+
# i will store test block in the middle, to be sure
|
52
|
+
# that first or last are not the only called
|
53
|
+
storage.store :commit, &null_proc
|
54
|
+
storage.store :commit, &test_block
|
55
|
+
storage.store :commit, &null_proc
|
56
|
+
storage.committed!
|
57
|
+
end.to yield_control
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'does not a rollback block' do
|
61
|
+
expect do |test_block|
|
62
|
+
storage.store :rollback, &test_block
|
63
|
+
storage.committed!
|
64
|
+
end.not_to yield_control
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'clears callbacks' do
|
68
|
+
expect do |test_block|
|
69
|
+
storage.store :commit, &test_block
|
70
|
+
storage.committed!
|
71
|
+
|
72
|
+
storage.committed!
|
73
|
+
end.to yield_control.once
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
describe '#rollbacked!' do
|
79
|
+
|
80
|
+
it 'runs a stored block' do
|
81
|
+
expect do |test_block|
|
82
|
+
storage.store :rollback, &test_block
|
83
|
+
storage.rolledback!
|
84
|
+
end.to yield_control
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'runs any stored block' do
|
88
|
+
expect do |test_block|
|
89
|
+
# i will store test block in the middle, to be sure
|
90
|
+
# that first or last are not the only called
|
91
|
+
storage.store :rollback, &null_proc
|
92
|
+
storage.store :rollback, &test_block
|
93
|
+
storage.store :rollback, &null_proc
|
94
|
+
storage.rolledback!
|
95
|
+
end.to yield_control
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'does not a rollback block' do
|
99
|
+
expect do |test_block|
|
100
|
+
storage.store :commit, &test_block
|
101
|
+
storage.rolledback!
|
102
|
+
end.not_to yield_control
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'clears callbacks' do
|
106
|
+
expect do |test_block|
|
107
|
+
storage.store :rollback, &test_block
|
108
|
+
storage.rolledback!
|
109
|
+
|
110
|
+
storage.rolledback!
|
111
|
+
end.to yield_control.once
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
SimpleCov.start
|
3
|
+
|
4
|
+
require 'rspec'
|
5
|
+
require 'active_record'
|
6
|
+
|
7
|
+
ActiveRecord::Base.establish_connection :adapter => :nulldb
|
8
|
+
|
9
|
+
require 'rspec/expectations'
|
10
|
+
|
11
|
+
RSpec::Matchers.define :receive_in_order do |*expected_call_order|
|
12
|
+
match do |test_double|
|
13
|
+
Array.wrap(expected_call_order).each do |a_call|
|
14
|
+
expect(test_double).to receive(a_call).ordered
|
15
|
+
end
|
16
|
+
Array.wrap(@not_to_be_called).each do |avoid_call|
|
17
|
+
expect(test_double).not_to receive(avoid_call)
|
18
|
+
end
|
19
|
+
true
|
20
|
+
end
|
21
|
+
|
22
|
+
chain :but_not do |*expected_not_be_called|
|
23
|
+
@not_to_be_called = expected_not_be_called
|
24
|
+
end
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fish_transactions
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- John Owen
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-06-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.12'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.12'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.4'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.4'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rdoc
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.12'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.12'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: simplecov
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.11'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.11'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: activerecord-nulldb-adapter
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0.3'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0.3'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: activerecord
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '4.2'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '4.2'
|
111
|
+
description: Allows to pospose the execution of a code waiting for Active Record transactions,
|
112
|
+
on commit, on rollback or both.
|
113
|
+
email:
|
114
|
+
- john.owen@beetrack.com
|
115
|
+
executables: []
|
116
|
+
extensions: []
|
117
|
+
extra_rdoc_files: []
|
118
|
+
files:
|
119
|
+
- MIT-LICENSE.txt
|
120
|
+
- README.md
|
121
|
+
- Rakefile
|
122
|
+
- lib/fish_transactions.rb
|
123
|
+
- lib/fish_transactions/active_record_mods.rb
|
124
|
+
- lib/fish_transactions/active_record_mods/base.rb
|
125
|
+
- lib/fish_transactions/active_record_mods/connection_abstract_adapter.rb
|
126
|
+
- lib/fish_transactions/callbacks.rb
|
127
|
+
- lib/fish_transactions/callbacks_storage.rb
|
128
|
+
- lib/fish_transactions/version.rb
|
129
|
+
- spec/callbacks_spec.rb
|
130
|
+
- spec/callbacks_storage_spec.rb
|
131
|
+
- spec/spec_helper.rb
|
132
|
+
homepage: https://github.com/Beetrack/fish_transactions
|
133
|
+
licenses:
|
134
|
+
- MIT
|
135
|
+
metadata: {}
|
136
|
+
post_install_message:
|
137
|
+
rdoc_options: []
|
138
|
+
require_paths:
|
139
|
+
- lib
|
140
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - ">="
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: 2.1.0
|
145
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
146
|
+
requirements:
|
147
|
+
- - ">="
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
requirements: []
|
151
|
+
rubyforge_project:
|
152
|
+
rubygems_version: 2.4.5.1
|
153
|
+
signing_key:
|
154
|
+
specification_version: 4
|
155
|
+
summary: Callbacks for Active Record transactions that can be used anywhere.
|
156
|
+
test_files:
|
157
|
+
- spec/callbacks_spec.rb
|
158
|
+
- spec/callbacks_storage_spec.rb
|
159
|
+
- spec/spec_helper.rb
|