larsklevan-after_commit 1.0.5
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.
- data/LICENSE +20 -0
- data/README.textile +51 -0
- data/lib/after_commit/active_record.rb +123 -0
- data/lib/after_commit/after_savepoint.rb +78 -0
- data/lib/after_commit/connection_adapters.rb +172 -0
- data/lib/after_commit.rb +75 -0
- data/test/after_commit_test.rb +126 -0
- data/test/observer_test.rb +59 -0
- data/test/test_helper.rb +24 -0
- metadata +85 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 Nick Muerdter
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.textile
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
h1. after_commit
|
2
|
+
|
3
|
+
An ActiveRecord/Rails library to add @before_commit@, @after_commit@, @before_rollback@ and @after_rollback@ callbacks. These callbacks are focused on the transactions, instead of specific model actions. This is beneficial in situations where you are doing asynchronous processing and need committed objects.
|
4
|
+
|
5
|
+
h2. Installation
|
6
|
+
|
7
|
+
<pre><code>gem install after_commit --source http://gemcutter.org</code></pre>
|
8
|
+
|
9
|
+
h2. Usage
|
10
|
+
|
11
|
+
The following callbacks are provided:
|
12
|
+
|
13
|
+
* before_commit
|
14
|
+
* before_commit_on_create
|
15
|
+
* before_commit_on_update
|
16
|
+
* before_commit_on_destroy
|
17
|
+
* after_commit
|
18
|
+
* after_commit_on_create
|
19
|
+
* after_commit_on_update
|
20
|
+
* after_commit_on_destroy
|
21
|
+
* before_rollback
|
22
|
+
* after_rollback
|
23
|
+
|
24
|
+
You can use these just like you would any other callback:
|
25
|
+
|
26
|
+
<pre><code>class Article < ActiveRecord::Base
|
27
|
+
after_commit :method_to_call_after_commit
|
28
|
+
|
29
|
+
# ...
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def method_to_call_after_commit
|
34
|
+
# Do something knowing that the transaction is committed.
|
35
|
+
end
|
36
|
+
end</code></pre>
|
37
|
+
|
38
|
+
h2. Credits
|
39
|
+
|
40
|
+
This code first appeared in a blog post "by Eli Miller":http://elimiller.blogspot.com/2007/06/proper-cache-expiry-with-aftercommit.html, and was then included in "Thinking Sphinx":http://ts.freelancing-gods.com by "Pat Allan":http://freelancing-gods.com, with modifications from Joost Hietbrink. The code was then "put on GitHub as a plugin":http://github.com/GUI/after_commit by Nick Muerdter, and many people forked and added their own contributions.
|
41
|
+
|
42
|
+
This version (maintained by Pat Allan) includes the following patches:
|
43
|
+
|
44
|
+
* Callbacks for specific types of actions (create, update, destroy) ("DeLynn Berry":http://delynnberry.com/)
|
45
|
+
* Fixes to extra callbacks ("Xavier Shay":http://rhnh.net/)
|
46
|
+
* Thread-safety ("Dmitry Galinsky":http://dima-exe.ru/)
|
47
|
+
* after_rollback callback ("Justin Balthrop":http://github.com/ninjudd)
|
48
|
+
* Test environment fix ("Pivotal Labs":http://pivotalblabs.com/)
|
49
|
+
* Scoping callbacks to specific connections ("Mat Brown":http://outofti.me/)
|
50
|
+
* before_* callbacks ("Trotter Cashion":http://trottercashion.com/)
|
51
|
+
* Gemspec and extended tests, works as a plugin, doesn't load ActiveRecord hooks twice, doesn't add Test environment hook automatically. ("David Yip":http://github.com/yipdw)
|
@@ -0,0 +1,123 @@
|
|
1
|
+
module AfterCommit
|
2
|
+
module ActiveRecord
|
3
|
+
def self.included(base)
|
4
|
+
base.class_eval do
|
5
|
+
class << self
|
6
|
+
def establish_connection_with_after_commit(spec = nil)
|
7
|
+
result = establish_connection_without_after_commit spec
|
8
|
+
include_after_commit_extensions
|
9
|
+
result
|
10
|
+
end
|
11
|
+
alias_method_chain :establish_connection, :after_commit
|
12
|
+
|
13
|
+
def include_after_commit_extensions
|
14
|
+
base = ::ActiveRecord::ConnectionAdapters::AbstractAdapter
|
15
|
+
Object.subclasses_of(base).each do |klass|
|
16
|
+
include_after_commit_extension klass
|
17
|
+
end
|
18
|
+
|
19
|
+
if defined?(JRUBY_VERSION) and defined?(JdbcSpec::MySQL)
|
20
|
+
include_after_commit_extension JdbcSpec::MySQL
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def include_after_commit_extension(adapter)
|
27
|
+
additions = AfterCommit::ConnectionAdapters
|
28
|
+
unless adapter.included_modules.include?(additions)
|
29
|
+
adapter.send :include, additions
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# The define_callbacks method was added post Rails 2.0.2 - if it
|
35
|
+
# doesn't exist, we define the callback manually
|
36
|
+
if respond_to?(:define_callbacks)
|
37
|
+
define_callbacks :after_commit,
|
38
|
+
:after_commit_on_create,
|
39
|
+
:after_commit_on_update,
|
40
|
+
:after_commit_on_destroy,
|
41
|
+
:after_rollback,
|
42
|
+
:before_commit,
|
43
|
+
:before_commit_on_create,
|
44
|
+
:before_commit_on_update,
|
45
|
+
:before_commit_on_destroy,
|
46
|
+
:before_rollback
|
47
|
+
else
|
48
|
+
class << self
|
49
|
+
# Handle after_commit callbacks - call all the registered callbacks.
|
50
|
+
def after_commit(*callbacks, &block)
|
51
|
+
callbacks << block if block_given?
|
52
|
+
write_inheritable_array(:after_commit, callbacks)
|
53
|
+
end
|
54
|
+
|
55
|
+
def after_commit_on_create(*callbacks, &block)
|
56
|
+
callbacks << block if block_given?
|
57
|
+
write_inheritable_array(:after_commit_on_create, callbacks)
|
58
|
+
end
|
59
|
+
|
60
|
+
def after_commit_on_update(*callbacks, &block)
|
61
|
+
callbacks << block if block_given?
|
62
|
+
write_inheritable_array(:after_commit_on_update, callbacks)
|
63
|
+
end
|
64
|
+
|
65
|
+
def after_commit_on_destroy(*callbacks, &block)
|
66
|
+
callbacks << block if block_given?
|
67
|
+
write_inheritable_array(:after_commit_on_destroy, callbacks)
|
68
|
+
end
|
69
|
+
|
70
|
+
def after_rollback(*callbacks, &block)
|
71
|
+
callbacks << block if block_given?
|
72
|
+
write_inheritable_array(:after_commit, callbacks)
|
73
|
+
end
|
74
|
+
|
75
|
+
def before_commit(*callbacks, &block)
|
76
|
+
callbacks << block if block_given?
|
77
|
+
write_inheritable_array(:before_commit, callbacks)
|
78
|
+
end
|
79
|
+
|
80
|
+
def before_commit_on_create(*callbacks, &block)
|
81
|
+
callbacks << block if block_given?
|
82
|
+
write_inheritable_array(:before_commit_on_create, callbacks)
|
83
|
+
end
|
84
|
+
|
85
|
+
def before_commit_on_update(*callbacks, &block)
|
86
|
+
callbacks << block if block_given?
|
87
|
+
write_inheritable_array(:before_commit_on_update, callbacks)
|
88
|
+
end
|
89
|
+
|
90
|
+
def before_commit_on_destroy(*callbacks, &block)
|
91
|
+
callbacks << block if block_given?
|
92
|
+
write_inheritable_array(:before_commit_on_destroy, callbacks)
|
93
|
+
end
|
94
|
+
|
95
|
+
def before_rollback(*callbacks, &block)
|
96
|
+
callbacks << block if block_given?
|
97
|
+
write_inheritable_array(:before_commit, callbacks)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
after_create :add_committed_record_on_create
|
103
|
+
after_update :add_committed_record_on_update
|
104
|
+
after_destroy :add_committed_record_on_destroy
|
105
|
+
|
106
|
+
def add_committed_record_on_create
|
107
|
+
AfterCommit.record(self.class.connection, self)
|
108
|
+
AfterCommit.record_created(self.class.connection, self)
|
109
|
+
end
|
110
|
+
|
111
|
+
def add_committed_record_on_update
|
112
|
+
AfterCommit.record(self.class.connection, self)
|
113
|
+
AfterCommit.record_updated(self.class.connection, self)
|
114
|
+
end
|
115
|
+
|
116
|
+
def add_committed_record_on_destroy
|
117
|
+
AfterCommit.record(self.class.connection, self)
|
118
|
+
AfterCommit.record_destroyed(self.class.connection, self)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# Fix problems caused because tests all run in a single transaction.
|
2
|
+
|
3
|
+
# The single transaction means that after_commit callback never happens in tests. Instead use savepoints.
|
4
|
+
|
5
|
+
module AfterCommit
|
6
|
+
module AfterSavepoint
|
7
|
+
def self.included(klass)
|
8
|
+
klass.class_eval do
|
9
|
+
class << self
|
10
|
+
def include_after_savepoint_extensions
|
11
|
+
base = ::ActiveRecord::ConnectionAdapters::AbstractAdapter
|
12
|
+
Object.subclasses_of(base).each do |klass|
|
13
|
+
include_after_savepoint_extension klass
|
14
|
+
end
|
15
|
+
|
16
|
+
if defined?(JRUBY_VERSION) and defined?(JdbcSpec::MySQL)
|
17
|
+
include_after_savepoint_extension JdbcSpec::MySQL
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def include_after_savepoint_extension(adapter)
|
24
|
+
additions = AfterCommit::TestConnectionAdapters
|
25
|
+
unless adapter.included_modules.include?(additions)
|
26
|
+
adapter.send :include, additions
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
module TestConnectionAdapters
|
35
|
+
def self.included(base)
|
36
|
+
base.class_eval do
|
37
|
+
def release_savepoint_with_callback
|
38
|
+
increment_transaction_pointer
|
39
|
+
committed = false
|
40
|
+
begin
|
41
|
+
trigger_before_commit_callbacks
|
42
|
+
trigger_before_commit_on_create_callbacks
|
43
|
+
trigger_before_commit_on_update_callbacks
|
44
|
+
trigger_before_commit_on_destroy_callbacks
|
45
|
+
|
46
|
+
release_savepoint_without_callback
|
47
|
+
committed = true
|
48
|
+
|
49
|
+
trigger_after_commit_callbacks
|
50
|
+
trigger_after_commit_on_create_callbacks
|
51
|
+
trigger_after_commit_on_update_callbacks
|
52
|
+
trigger_after_commit_on_destroy_callbacks
|
53
|
+
rescue
|
54
|
+
rollback_to_savepoint unless committed
|
55
|
+
ensure
|
56
|
+
AfterCommit.cleanup(self)
|
57
|
+
decrement_transaction_pointer
|
58
|
+
end
|
59
|
+
end
|
60
|
+
alias_method_chain :release_savepoint, :callback
|
61
|
+
|
62
|
+
# In the event the transaction fails and rolls back, nothing inside
|
63
|
+
# should recieve the after_commit callback, but do fire the after_rollback
|
64
|
+
# callback for each record that failed to be committed.
|
65
|
+
def rollback_to_savepoint_with_callback
|
66
|
+
begin
|
67
|
+
trigger_before_rollback_callbacks
|
68
|
+
rollback_to_savepoint_without_callback
|
69
|
+
trigger_after_rollback_callbacks
|
70
|
+
ensure
|
71
|
+
AfterCommit.cleanup(self)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
alias_method_chain :rollback_to_savepoint, :callback
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
module AfterCommit
|
2
|
+
module ConnectionAdapters
|
3
|
+
def self.included(base)
|
4
|
+
base.class_eval do
|
5
|
+
# The commit_db_transaction method gets called when the outermost
|
6
|
+
# transaction finishes and everything inside commits. We want to
|
7
|
+
# override it so that after this happens, any records that were saved
|
8
|
+
# or destroyed within this transaction now get their after_commit
|
9
|
+
# callback fired.
|
10
|
+
def commit_db_transaction_with_callback
|
11
|
+
increment_transaction_pointer
|
12
|
+
committed = false
|
13
|
+
result = nil
|
14
|
+
begin
|
15
|
+
trigger_before_commit_callbacks
|
16
|
+
trigger_before_commit_on_create_callbacks
|
17
|
+
trigger_before_commit_on_update_callbacks
|
18
|
+
trigger_before_commit_on_destroy_callbacks
|
19
|
+
|
20
|
+
result = commit_db_transaction_without_callback
|
21
|
+
committed = true
|
22
|
+
|
23
|
+
trigger_after_commit_callbacks
|
24
|
+
trigger_after_commit_on_create_callbacks
|
25
|
+
trigger_after_commit_on_update_callbacks
|
26
|
+
trigger_after_commit_on_destroy_callbacks
|
27
|
+
result
|
28
|
+
rescue
|
29
|
+
committed ? result : rollback_db_transaction
|
30
|
+
ensure
|
31
|
+
AfterCommit.cleanup(self)
|
32
|
+
decrement_transaction_pointer
|
33
|
+
end
|
34
|
+
end
|
35
|
+
alias_method_chain :commit_db_transaction, :callback
|
36
|
+
|
37
|
+
# In the event the transaction fails and rolls back, nothing inside
|
38
|
+
# should recieve the after_commit callback, but do fire the after_rollback
|
39
|
+
# callback for each record that failed to be committed.
|
40
|
+
def rollback_db_transaction_with_callback
|
41
|
+
begin
|
42
|
+
result = nil
|
43
|
+
trigger_before_rollback_callbacks
|
44
|
+
result = rollback_db_transaction_without_callback
|
45
|
+
trigger_after_rollback_callbacks
|
46
|
+
result
|
47
|
+
ensure
|
48
|
+
AfterCommit.cleanup(self)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
alias_method_chain :rollback_db_transaction, :callback
|
52
|
+
|
53
|
+
def unique_transaction_key
|
54
|
+
[object_id, transaction_pointer]
|
55
|
+
end
|
56
|
+
|
57
|
+
def old_transaction_key
|
58
|
+
[object_id, transaction_pointer - 1]
|
59
|
+
end
|
60
|
+
|
61
|
+
protected
|
62
|
+
|
63
|
+
def trigger_before_commit_callbacks
|
64
|
+
AfterCommit.records(self).each do |record|
|
65
|
+
record.send :callback, :before_commit
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def trigger_before_commit_on_create_callbacks
|
70
|
+
AfterCommit.created_records(self).each do |record|
|
71
|
+
record.send :callback, :before_commit_on_create
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def trigger_before_commit_on_update_callbacks
|
76
|
+
AfterCommit.updated_records(self).each do |record|
|
77
|
+
record.send :callback, :before_commit_on_update
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def trigger_before_commit_on_destroy_callbacks
|
82
|
+
AfterCommit.destroyed_records(self).each do |record|
|
83
|
+
record.send :callback, :before_commit_on_destroy
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def trigger_before_rollback_callbacks
|
88
|
+
AfterCommit.records(self).each do |record|
|
89
|
+
begin
|
90
|
+
record.send :callback, :before_rollback
|
91
|
+
rescue
|
92
|
+
#
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def trigger_after_commit_callbacks
|
98
|
+
# Trigger the after_commit callback for each of the committed
|
99
|
+
# records.
|
100
|
+
AfterCommit.records(self).each do |record|
|
101
|
+
begin
|
102
|
+
record.send :callback, :after_commit
|
103
|
+
rescue
|
104
|
+
#
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def trigger_after_commit_on_create_callbacks
|
110
|
+
# Trigger the after_commit_on_create callback for each of the committed
|
111
|
+
# records.
|
112
|
+
AfterCommit.created_records(self).each do |record|
|
113
|
+
begin
|
114
|
+
record.send :callback, :after_commit_on_create
|
115
|
+
rescue
|
116
|
+
#
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def trigger_after_commit_on_update_callbacks
|
122
|
+
# Trigger the after_commit_on_update callback for each of the committed
|
123
|
+
# records.
|
124
|
+
AfterCommit.updated_records(self).each do |record|
|
125
|
+
begin
|
126
|
+
record.send :callback, :after_commit_on_update
|
127
|
+
rescue
|
128
|
+
#
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def trigger_after_commit_on_destroy_callbacks
|
134
|
+
# Trigger the after_commit_on_destroy callback for each of the committed
|
135
|
+
# records.
|
136
|
+
AfterCommit.destroyed_records(self).each do |record|
|
137
|
+
begin
|
138
|
+
record.send :callback, :after_commit_on_destroy
|
139
|
+
rescue
|
140
|
+
#
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def trigger_after_rollback_callbacks
|
146
|
+
# Trigger the after_rollback callback for each of the committed
|
147
|
+
# records.
|
148
|
+
AfterCommit.records(self).each do |record|
|
149
|
+
begin
|
150
|
+
record.send :callback, :after_rollback
|
151
|
+
rescue
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def transaction_pointer
|
157
|
+
Thread.current[:after_commit_pointer] ||= 0
|
158
|
+
end
|
159
|
+
|
160
|
+
def increment_transaction_pointer
|
161
|
+
Thread.current[:after_commit_pointer] ||= 0
|
162
|
+
Thread.current[:after_commit_pointer] += 1
|
163
|
+
end
|
164
|
+
|
165
|
+
def decrement_transaction_pointer
|
166
|
+
Thread.current[:after_commit_pointer] ||= 0
|
167
|
+
Thread.current[:after_commit_pointer] -= 1
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
data/lib/after_commit.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
module AfterCommit
|
2
|
+
def self.record(connection, record)
|
3
|
+
prepare_collection :committed_records, connection
|
4
|
+
add_to_collection :committed_records, connection, record
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.record_created(connection, record)
|
8
|
+
prepare_collection :committed_records_on_create, connection
|
9
|
+
add_to_collection :committed_records_on_create, connection, record
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.record_updated(connection, record)
|
13
|
+
prepare_collection :committed_records_on_update, connection
|
14
|
+
add_to_collection :committed_records_on_update, connection, record
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.record_destroyed(connection, record)
|
18
|
+
prepare_collection :committed_records_on_destroy, connection
|
19
|
+
add_to_collection :committed_records_on_destroy, connection, record
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.records(connection)
|
23
|
+
collection :committed_records, connection
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.created_records(connection)
|
27
|
+
collection :committed_records_on_create, connection
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.updated_records(connection)
|
31
|
+
collection :committed_records_on_update, connection
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.destroyed_records(connection)
|
35
|
+
collection :committed_records_on_destroy, connection
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.cleanup(connection)
|
39
|
+
[
|
40
|
+
:committed_records,
|
41
|
+
:committed_records_on_create,
|
42
|
+
:committed_records_on_update,
|
43
|
+
:committed_records_on_destroy
|
44
|
+
].each do |collection|
|
45
|
+
Thread.current[collection] ||= {}
|
46
|
+
Thread.current[collection][connection.old_transaction_key] = []
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.prepare_collection(collection, connection)
|
51
|
+
Thread.current[collection] ||= {}
|
52
|
+
Thread.current[collection][connection.unique_transaction_key] ||= []
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.add_to_collection(collection, connection, record)
|
56
|
+
Thread.current[collection][connection.unique_transaction_key] << record
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.collection(collection, connection)
|
60
|
+
Thread.current[collection] ||= {}
|
61
|
+
Thread.current[collection][connection.old_transaction_key] ||= []
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
require 'after_commit/active_record'
|
66
|
+
require 'after_commit/connection_adapters'
|
67
|
+
require 'after_commit/after_savepoint'
|
68
|
+
|
69
|
+
ActiveRecord::Base.send(:include, AfterCommit::ActiveRecord)
|
70
|
+
ActiveRecord::Base.include_after_commit_extensions
|
71
|
+
|
72
|
+
if defined?(RAILS_ENV) && RAILS_ENV == 'test'
|
73
|
+
ActiveRecord::Base.send(:include, AfterCommit::AfterSavepoint)
|
74
|
+
ActiveRecord::Base.include_after_savepoint_extensions
|
75
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class MockRecord < ActiveRecord::Base
|
4
|
+
attr_accessor :before_commit_on_create_called
|
5
|
+
attr_accessor :before_commit_on_update_called
|
6
|
+
attr_accessor :before_commit_on_destroy_called
|
7
|
+
attr_accessor :after_commit_on_create_called
|
8
|
+
attr_accessor :after_commit_on_update_called
|
9
|
+
attr_accessor :after_commit_on_destroy_called
|
10
|
+
|
11
|
+
before_commit_on_create :do_before_create
|
12
|
+
def do_before_create
|
13
|
+
self.before_commit_on_create_called = true
|
14
|
+
end
|
15
|
+
|
16
|
+
before_commit_on_update :do_before_update
|
17
|
+
def do_before_update
|
18
|
+
self.before_commit_on_update_called = true
|
19
|
+
end
|
20
|
+
|
21
|
+
before_commit_on_create :do_before_destroy
|
22
|
+
def do_before_destroy
|
23
|
+
self.before_commit_on_destroy_called = true
|
24
|
+
end
|
25
|
+
|
26
|
+
after_commit_on_create :do_after_create
|
27
|
+
def do_after_create
|
28
|
+
self.after_commit_on_create_called = true
|
29
|
+
end
|
30
|
+
|
31
|
+
after_commit_on_update :do_after_update
|
32
|
+
def do_after_update
|
33
|
+
self.after_commit_on_update_called = true
|
34
|
+
end
|
35
|
+
|
36
|
+
after_commit_on_create :do_after_destroy
|
37
|
+
def do_after_destroy
|
38
|
+
self.after_commit_on_destroy_called = true
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class Foo < ActiveRecord::Base
|
43
|
+
attr_reader :creating
|
44
|
+
|
45
|
+
after_commit :create_bar
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def create_bar
|
50
|
+
@creating ||= 0
|
51
|
+
@creating += 1
|
52
|
+
|
53
|
+
raise Exception, 'looping' if @creating > 1
|
54
|
+
Bar.create
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class Bar < ActiveRecord::Base
|
59
|
+
#
|
60
|
+
end
|
61
|
+
|
62
|
+
class UnsavableRecord < ActiveRecord::Base
|
63
|
+
attr_accessor :after_commit_called
|
64
|
+
|
65
|
+
set_table_name 'mock_records'
|
66
|
+
|
67
|
+
protected
|
68
|
+
|
69
|
+
def after_initialize
|
70
|
+
self.after_commit_called = false
|
71
|
+
end
|
72
|
+
|
73
|
+
def after_save
|
74
|
+
raise
|
75
|
+
end
|
76
|
+
|
77
|
+
after_commit :after_commit
|
78
|
+
|
79
|
+
def after_commit
|
80
|
+
self.after_commit_called = true
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class AfterCommitTest < Test::Unit::TestCase
|
85
|
+
def test_before_commit_on_create_is_called
|
86
|
+
assert_equal true, MockRecord.create!.before_commit_on_create_called
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_before_commit_on_update_is_called
|
90
|
+
record = MockRecord.create!
|
91
|
+
record.save
|
92
|
+
assert_equal true, record.before_commit_on_update_called
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_before_commit_on_destroy_is_called
|
96
|
+
assert_equal true, MockRecord.create!.destroy.before_commit_on_destroy_called
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_after_commit_on_create_is_called
|
100
|
+
assert_equal true, MockRecord.create!.after_commit_on_create_called
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_after_commit_on_update_is_called
|
104
|
+
record = MockRecord.create!
|
105
|
+
record.save
|
106
|
+
assert_equal true, record.after_commit_on_update_called
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_after_commit_on_destroy_is_called
|
110
|
+
assert_equal true, MockRecord.create!.destroy.after_commit_on_destroy_called
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_after_commit_does_not_trigger_when_transaction_rolls_back
|
114
|
+
record = UnsavableRecord.new
|
115
|
+
begin; record.save; rescue; end
|
116
|
+
|
117
|
+
assert_equal false, record.after_commit_called
|
118
|
+
end
|
119
|
+
|
120
|
+
def test_two_transactions_are_separate
|
121
|
+
Bar.delete_all
|
122
|
+
foo = Foo.create
|
123
|
+
|
124
|
+
assert_equal 1, foo.creating
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ObservableMockRecord < ActiveRecord::Base
|
4
|
+
set_table_name 'mock_records'
|
5
|
+
|
6
|
+
attr_accessor :after_commit_called
|
7
|
+
attr_accessor :after_commit_on_create_called
|
8
|
+
attr_accessor :after_commit_on_update_called
|
9
|
+
attr_accessor :after_commit_on_destroy_called
|
10
|
+
end
|
11
|
+
|
12
|
+
class ObservableMockRecordObserver < ActiveRecord::Observer
|
13
|
+
def after_commit(model)
|
14
|
+
model.after_commit_called = true
|
15
|
+
end
|
16
|
+
|
17
|
+
def after_commit_on_create(model)
|
18
|
+
model.after_commit_on_create_called = true
|
19
|
+
end
|
20
|
+
|
21
|
+
def after_commit_on_update(model)
|
22
|
+
model.after_commit_on_update_called = true
|
23
|
+
end
|
24
|
+
|
25
|
+
def after_commit_on_destroy(model)
|
26
|
+
model.after_commit_on_destroy_called = true
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class ObserverTest < Test::Unit::TestCase
|
31
|
+
def setup
|
32
|
+
ObservableMockRecord.add_observer ObservableMockRecordObserver.instance
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_after_commit_is_called
|
36
|
+
record = ObservableMockRecord.create!
|
37
|
+
|
38
|
+
assert record.after_commit_called
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_after_commit_on_create_is_called
|
42
|
+
record = ObservableMockRecord.create!
|
43
|
+
|
44
|
+
assert record.after_commit_on_create_called
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_after_commit_on_update_is_called
|
48
|
+
record = ObservableMockRecord.create!
|
49
|
+
record.save
|
50
|
+
|
51
|
+
assert record.after_commit_on_update_called
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_after_commit_on_destroy_is_called
|
55
|
+
record = ObservableMockRecord.create!.destroy
|
56
|
+
|
57
|
+
assert record.after_commit_on_destroy_called
|
58
|
+
end
|
59
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib')
|
2
|
+
require 'test/unit'
|
3
|
+
require 'rubygems'
|
4
|
+
require 'active_record'
|
5
|
+
|
6
|
+
begin
|
7
|
+
require 'sqlite3'
|
8
|
+
rescue LoadError
|
9
|
+
gem 'sqlite3-ruby'
|
10
|
+
retry
|
11
|
+
end
|
12
|
+
|
13
|
+
ActiveRecord::Base.establish_connection({"adapter" => "sqlite3", "database" => 'test.sqlite3'})
|
14
|
+
begin
|
15
|
+
ActiveRecord::Base.connection.execute("drop table mock_records");
|
16
|
+
ActiveRecord::Base.connection.execute("drop table foos");
|
17
|
+
ActiveRecord::Base.connection.execute("drop table bars");
|
18
|
+
rescue
|
19
|
+
end
|
20
|
+
ActiveRecord::Base.connection.execute("create table mock_records(id int)");
|
21
|
+
ActiveRecord::Base.connection.execute("create table foos(id int)");
|
22
|
+
ActiveRecord::Base.connection.execute("create table bars(id int)");
|
23
|
+
|
24
|
+
require 'after_commit'
|
metadata
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: larsklevan-after_commit
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.5
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Nick Muerdter
|
8
|
+
- David Yip
|
9
|
+
- Pat Allan
|
10
|
+
- Lars Klevan
|
11
|
+
autorequire:
|
12
|
+
bindir: bin
|
13
|
+
cert_chain: []
|
14
|
+
|
15
|
+
date: 2010-01-15 00:00:00 -06:00
|
16
|
+
default_executable:
|
17
|
+
dependencies:
|
18
|
+
- !ruby/object:Gem::Dependency
|
19
|
+
name: activerecord
|
20
|
+
type: :runtime
|
21
|
+
version_requirement:
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: "0"
|
27
|
+
version:
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: shoulda
|
30
|
+
type: :development
|
31
|
+
version_requirement:
|
32
|
+
version_requirements: !ruby/object:Gem::Requirement
|
33
|
+
requirements:
|
34
|
+
- - ">="
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: "0"
|
37
|
+
version:
|
38
|
+
description: "\n A Ruby on Rails plugin to add an after_commit callback. This can be used to trigger methods only after the entire transaction is complete.\n Updated with savepoint support for unit testing.\n "
|
39
|
+
email: tastybyte@gmail.com
|
40
|
+
executables: []
|
41
|
+
|
42
|
+
extensions: []
|
43
|
+
|
44
|
+
extra_rdoc_files:
|
45
|
+
- LICENSE
|
46
|
+
- README.textile
|
47
|
+
files:
|
48
|
+
- LICENSE
|
49
|
+
- lib/after_commit.rb
|
50
|
+
- lib/after_commit/active_record.rb
|
51
|
+
- lib/after_commit/after_savepoint.rb
|
52
|
+
- lib/after_commit/connection_adapters.rb
|
53
|
+
- README.textile
|
54
|
+
has_rdoc: true
|
55
|
+
homepage: http://github.com/larsklevan/after_commit
|
56
|
+
licenses: []
|
57
|
+
|
58
|
+
post_install_message:
|
59
|
+
rdoc_options:
|
60
|
+
- --charset=UTF-8
|
61
|
+
require_paths:
|
62
|
+
- lib
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: "0"
|
68
|
+
version:
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: "0"
|
74
|
+
version:
|
75
|
+
requirements: []
|
76
|
+
|
77
|
+
rubyforge_project:
|
78
|
+
rubygems_version: 1.3.5
|
79
|
+
signing_key:
|
80
|
+
specification_version: 3
|
81
|
+
summary: after_commit callback for ActiveRecord
|
82
|
+
test_files:
|
83
|
+
- test/after_commit_test.rb
|
84
|
+
- test/observer_test.rb
|
85
|
+
- test/test_helper.rb
|