after_commit 1.0.6 → 1.0.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -4,7 +4,7 @@ An ActiveRecord/Rails library to add @before_commit@, @after_commit@, @before_ro
4
4
 
5
5
  h2. Installation
6
6
 
7
- <pre><code>gem install after_commit --source http://gemcutter.org</code></pre>
7
+ <pre><code>gem install after_commit</code></pre>
8
8
 
9
9
  h2. Usage
10
10
 
@@ -35,6 +35,13 @@ You can use these just like you would any other callback:
35
35
  end
36
36
  end</code></pre>
37
37
 
38
+ h2. In Tests
39
+
40
+ Keep in mind that transactions are finicky at best in tests, and so there's a helper module to make @after_commit@ play nicely in your testing context. You'll need to add these two lines to your spec/test helper:
41
+
42
+ <pre><code>ActiveRecord::Base.send(:include, AfterCommit::AfterSavepoint)
43
+ ActiveRecord::Base.include_after_savepoint_extensions</code></pre>
44
+
38
45
  h2. Credits
39
46
 
40
47
  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.
@@ -49,3 +56,7 @@ This version (maintained by Pat Allan) includes the following patches:
49
56
  * Scoping callbacks to specific connections ("Mat Brown":http://outofti.me/)
50
57
  * before_* callbacks ("Trotter Cashion":http://trottercashion.com/)
51
58
  * 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)
59
+ * Exception propagation ("George Ogata":http://github.com/oggy/)
60
+ * Keeping callbacks focused on the correct transactions ("Benjamin Stein":http://benjaminste.in/)
61
+ * Using savepoints for test helper ("Lars Klevan":http://www.linkedin.com/in/larsklevan and "Joel Chippindale":http://blog.monkeysthumb.org/)
62
+ * Only tracking objects from classes that have appropriate callbacks; Adding @after/before_commit_on_save@ to cover creates and updates but not destroys ("Jason Weathered":http://jasoncodes.com/)
@@ -14,6 +14,11 @@ module AfterCommit
14
14
  add_to_collection :committed_records_on_update, connection, record
15
15
  end
16
16
 
17
+ def self.record_saved(connection, record)
18
+ prepare_collection :committed_records_on_save, connection
19
+ add_to_collection :committed_records_on_save, connection, record
20
+ end
21
+
17
22
  def self.record_destroyed(connection, record)
18
23
  prepare_collection :committed_records_on_destroy, connection
19
24
  add_to_collection :committed_records_on_destroy, connection, record
@@ -31,6 +36,10 @@ module AfterCommit
31
36
  collection :committed_records_on_update, connection
32
37
  end
33
38
 
39
+ def self.saved_records(connection)
40
+ collection :committed_records_on_save, connection
41
+ end
42
+
34
43
  def self.destroyed_records(connection)
35
44
  collection :committed_records_on_destroy, connection
36
45
  end
@@ -40,6 +49,7 @@ module AfterCommit
40
49
  :committed_records,
41
50
  :committed_records_on_create,
42
51
  :committed_records_on_update,
52
+ :committed_records_on_save,
43
53
  :committed_records_on_destroy
44
54
  ].each do |collection|
45
55
  Thread.current[collection] ||= {}
@@ -62,9 +72,10 @@ module AfterCommit
62
72
  end
63
73
  end
64
74
 
75
+ require 'after_commit/active_support_callbacks'
65
76
  require 'after_commit/active_record'
66
77
  require 'after_commit/connection_adapters'
67
- require 'after_commit/test_bypass'
78
+ require 'after_commit/after_savepoint'
68
79
 
69
80
  ActiveRecord::Base.send(:include, AfterCommit::ActiveRecord)
70
81
  ActiveRecord::Base.include_after_commit_extensions
@@ -31,91 +31,55 @@ module AfterCommit
31
31
  end
32
32
  end
33
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
-
34
+ define_callbacks :after_commit,
35
+ :after_commit_on_create,
36
+ :after_commit_on_update,
37
+ :after_commit_on_save,
38
+ :after_commit_on_destroy,
39
+ :after_rollback,
40
+ :before_commit,
41
+ :before_commit_on_create,
42
+ :before_commit_on_update,
43
+ :before_commit_on_save,
44
+ :before_commit_on_destroy,
45
+ :before_rollback
46
+
102
47
  after_create :add_committed_record_on_create
103
48
  after_update :add_committed_record_on_update
49
+ after_save :add_committed_record_on_save
104
50
  after_destroy :add_committed_record_on_destroy
105
51
 
52
+ def add_committed_record
53
+ if have_callback? :before_commit, :after_commit, :before_rollback, :after_rollback
54
+ AfterCommit.record(self.class.connection, self)
55
+ end
56
+ end
57
+
106
58
  def add_committed_record_on_create
107
- AfterCommit.record(self.class.connection, self)
108
- AfterCommit.record_created(self.class.connection, self)
59
+ add_committed_record
60
+ if have_callback? :before_commit_on_create, :after_commit_on_create
61
+ AfterCommit.record_created(self.class.connection, self)
62
+ end
109
63
  end
110
64
 
111
65
  def add_committed_record_on_update
112
- AfterCommit.record(self.class.connection, self)
113
- AfterCommit.record_updated(self.class.connection, self)
66
+ add_committed_record
67
+ if have_callback? :before_commit_on_update, :after_commit_on_update
68
+ AfterCommit.record_updated(self.class.connection, self)
69
+ end
70
+ end
71
+
72
+ def add_committed_record_on_save
73
+ if have_callback? :before_commit_on_save, :after_commit_on_save
74
+ AfterCommit.record_saved(self.class.connection, self)
75
+ end
114
76
  end
115
77
 
116
78
  def add_committed_record_on_destroy
117
- AfterCommit.record(self.class.connection, self)
118
- AfterCommit.record_destroyed(self.class.connection, self)
79
+ add_committed_record
80
+ if have_callback? :before_commit_on_destroy, :after_commit_on_destroy
81
+ AfterCommit.record_destroyed(self.class.connection, self)
82
+ end
119
83
  end
120
84
  end
121
85
  end
@@ -0,0 +1,59 @@
1
+ if defined? ActiveSupport::Callbacks
2
+
3
+ module AfterCommit
4
+ module ActiveSupportCallbacks
5
+ def self.included(base)
6
+
7
+ base::Callback.class_eval do
8
+ def have_callback?
9
+ true
10
+ end
11
+ end
12
+
13
+ base::CallbackChain.class_eval do
14
+ def have_callback?
15
+ any? &:have_callback?
16
+ end
17
+ end
18
+
19
+ base.class_eval do
20
+ def have_callback?(*callbacks)
21
+ self.class.observers.size > 0 or
22
+ self.class.count_observers > 0 or
23
+ callbacks.any? do |callback|
24
+ self.class.send("#{callback}_callback_chain").have_callback?
25
+ end
26
+ end
27
+ end
28
+
29
+ end
30
+ end
31
+ end
32
+ ActiveSupport::Callbacks.send(:include, AfterCommit::ActiveSupportCallbacks)
33
+
34
+ else
35
+
36
+ class ActiveRecord::Base
37
+
38
+ def self.define_callbacks(*names)
39
+ names.each do |name|
40
+ instance_eval <<-RUBY
41
+ def #{name}(*callbacks, &block)
42
+ callbacks << block if block_given?
43
+ write_inheritable_array(:#{name}, callbacks)
44
+ end
45
+ RUBY
46
+ end
47
+ end
48
+
49
+ def have_callback?(*names)
50
+ self.class.observers.size > 0 or
51
+ self.class.count_observers > 0 or
52
+ names.any? do |name|
53
+ !self.class.read_inheritable_attribute(name).blank?
54
+ end
55
+ end
56
+
57
+ end
58
+
59
+ end
@@ -0,0 +1,86 @@
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_save_callbacks
44
+ trigger_before_commit_on_update_callbacks
45
+ trigger_before_commit_on_destroy_callbacks
46
+
47
+ release_savepoint_without_callback
48
+ committed = true
49
+
50
+ trigger_after_commit_callbacks
51
+ trigger_after_commit_on_create_callbacks
52
+ trigger_after_commit_on_save_callbacks
53
+ trigger_after_commit_on_update_callbacks
54
+ trigger_after_commit_on_destroy_callbacks
55
+ rescue
56
+ unless committed
57
+ decrement_transaction_pointer
58
+ rollback_to_savepoint
59
+ increment_transaction_pointer
60
+ end
61
+ ensure
62
+ AfterCommit.cleanup(self)
63
+ decrement_transaction_pointer
64
+ end
65
+ end
66
+ alias_method_chain :release_savepoint, :callback
67
+
68
+ # In the event the transaction fails and rolls back, nothing inside
69
+ # should recieve the after_commit callback, but do fire the after_rollback
70
+ # callback for each record that failed to be committed.
71
+ def rollback_to_savepoint_with_callback
72
+ increment_transaction_pointer
73
+ begin
74
+ trigger_before_rollback_callbacks
75
+ rollback_to_savepoint_without_callback
76
+ trigger_after_rollback_callbacks
77
+ ensure
78
+ AfterCommit.cleanup(self)
79
+ end
80
+ decrement_transaction_pointer
81
+ end
82
+ alias_method_chain :rollback_to_savepoint, :callback
83
+ end
84
+ end
85
+ end
86
+ end
@@ -2,6 +2,19 @@ module AfterCommit
2
2
  module ConnectionAdapters
3
3
  def self.included(base)
4
4
  base.class_eval do
5
+ def transaction_with_callback(*args, &block)
6
+ # @disable_rollback is set to false at the start of the
7
+ # outermost call to #transaction. After committing, it is
8
+ # set to true to prevent exceptions causing a spurious
9
+ # rollback.
10
+ outermost_call = @disable_rollback.nil?
11
+ @disable_rollback = false if outermost_call
12
+ transaction_without_callback(*args, &block)
13
+ ensure
14
+ @disable_rollback = nil if outermost_call
15
+ end
16
+ alias_method_chain :transaction, :callback
17
+
5
18
  # The commit_db_transaction method gets called when the outermost
6
19
  # transaction finishes and everything inside commits. We want to
7
20
  # override it so that after this happens, any records that were saved
@@ -9,27 +22,36 @@ module AfterCommit
9
22
  # callback fired.
10
23
  def commit_db_transaction_with_callback
11
24
  increment_transaction_pointer
12
- committed = false
13
25
  result = nil
14
26
  begin
15
27
  trigger_before_commit_callbacks
16
28
  trigger_before_commit_on_create_callbacks
17
29
  trigger_before_commit_on_update_callbacks
30
+ trigger_before_commit_on_save_callbacks
18
31
  trigger_before_commit_on_destroy_callbacks
19
-
32
+
20
33
  result = commit_db_transaction_without_callback
21
- committed = true
22
-
34
+ @disable_rollback = true
35
+
23
36
  trigger_after_commit_callbacks
24
37
  trigger_after_commit_on_create_callbacks
25
38
  trigger_after_commit_on_update_callbacks
39
+ trigger_after_commit_on_save_callbacks
26
40
  trigger_after_commit_on_destroy_callbacks
27
41
  result
28
42
  rescue
29
- committed ? result : rollback_db_transaction
43
+ # Need to decrement the transaction pointer before calling
44
+ # rollback... to ensure it is not incremented twice
45
+ unless @disable_rollback
46
+ decrement_transaction_pointer
47
+ @already_decremented = true
48
+ end
49
+
50
+ # We still want to raise the exception.
51
+ raise
30
52
  ensure
31
53
  AfterCommit.cleanup(self)
32
- decrement_transaction_pointer
54
+ decrement_transaction_pointer unless @already_decremented
33
55
  end
34
56
  end
35
57
  alias_method_chain :commit_db_transaction, :callback
@@ -38,6 +60,8 @@ module AfterCommit
38
60
  # should recieve the after_commit callback, but do fire the after_rollback
39
61
  # callback for each record that failed to be committed.
40
62
  def rollback_db_transaction_with_callback
63
+ return if @disable_rollback
64
+ increment_transaction_pointer
41
65
  begin
42
66
  result = nil
43
67
  trigger_before_rollback_callbacks
@@ -46,7 +70,9 @@ module AfterCommit
46
70
  result
47
71
  ensure
48
72
  AfterCommit.cleanup(self)
73
+ decrement_transaction_pointer
49
74
  end
75
+ decrement_transaction_pointer
50
76
  end
51
77
  alias_method_chain :rollback_db_transaction, :callback
52
78
 
@@ -78,6 +104,12 @@ module AfterCommit
78
104
  end
79
105
  end
80
106
 
107
+ def trigger_before_commit_on_save_callbacks
108
+ AfterCommit.saved_records(self).each do |record|
109
+ record.send :callback, :before_commit_on_save
110
+ end
111
+ end
112
+
81
113
  def trigger_before_commit_on_destroy_callbacks
82
114
  AfterCommit.destroyed_records(self).each do |record|
83
115
  record.send :callback, :before_commit_on_destroy
@@ -86,11 +118,7 @@ module AfterCommit
86
118
 
87
119
  def trigger_before_rollback_callbacks
88
120
  AfterCommit.records(self).each do |record|
89
- begin
90
- record.send :callback, :before_rollback
91
- rescue
92
- #
93
- end
121
+ record.send :callback, :before_rollback
94
122
  end
95
123
  end
96
124
 
@@ -98,23 +126,15 @@ module AfterCommit
98
126
  # Trigger the after_commit callback for each of the committed
99
127
  # records.
100
128
  AfterCommit.records(self).each do |record|
101
- begin
102
- record.send :callback, :after_commit
103
- rescue
104
- #
105
- end
129
+ record.send :callback, :after_commit
106
130
  end
107
131
  end
108
-
132
+
109
133
  def trigger_after_commit_on_create_callbacks
110
134
  # Trigger the after_commit_on_create callback for each of the committed
111
135
  # records.
112
136
  AfterCommit.created_records(self).each do |record|
113
- begin
114
- record.send :callback, :after_commit_on_create
115
- rescue
116
- #
117
- end
137
+ record.send :callback, :after_commit_on_create
118
138
  end
119
139
  end
120
140
 
@@ -122,23 +142,23 @@ module AfterCommit
122
142
  # Trigger the after_commit_on_update callback for each of the committed
123
143
  # records.
124
144
  AfterCommit.updated_records(self).each do |record|
125
- begin
126
- record.send :callback, :after_commit_on_update
127
- rescue
128
- #
129
- end
145
+ record.send :callback, :after_commit_on_update
130
146
  end
131
147
  end
132
148
 
149
+ def trigger_after_commit_on_save_callbacks
150
+ # Trigger the after_commit_on_save callback for each of the committed
151
+ # records.
152
+ AfterCommit.saved_records(self).each do |record|
153
+ record.send :callback, :after_commit_on_save
154
+ end
155
+ end
156
+
133
157
  def trigger_after_commit_on_destroy_callbacks
134
158
  # Trigger the after_commit_on_destroy callback for each of the committed
135
159
  # records.
136
160
  AfterCommit.destroyed_records(self).each do |record|
137
- begin
138
- record.send :callback, :after_commit_on_destroy
139
- rescue
140
- #
141
- end
161
+ record.send :callback, :after_commit_on_destroy
142
162
  end
143
163
  end
144
164
 
@@ -146,10 +166,7 @@ module AfterCommit
146
166
  # Trigger the after_rollback callback for each of the committed
147
167
  # records.
148
168
  AfterCommit.records(self).each do |record|
149
- begin
150
- record.send :callback, :after_rollback
151
- rescue
152
- end
169
+ record.send :callback, :after_rollback
153
170
  end
154
171
  end
155
172
 
@@ -1,41 +1,37 @@
1
1
  require 'test_helper'
2
2
 
3
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
4
+
5
+ PHASES = %w(before after)
6
+ ACTIONS = %w(create update save destroy)
7
+
8
+ PHASES.each do |phase|
9
+ ACTIONS.each do |action|
10
+
11
+ class_eval <<-RUBY
12
+
13
+ attr_accessor :#{phase}_commit_on_#{action}_called
14
+
15
+ #{phase}_commit_on_#{action} :do_#{phase}_#{action}
16
+ def do_#{phase}_#{action}
17
+ self.#{phase}_commit_on_#{action}_called = true
18
+ end
19
+
20
+ RUBY
21
+
22
+ end
24
23
  end
24
+
25
+ end
25
26
 
27
+ class CountingRecord < ActiveRecord::Base
28
+ attr_accessor :after_commit_on_create_called
29
+ cattr_accessor :counter
30
+ @@counter=0
31
+
26
32
  after_commit_on_create :do_after_create
27
33
  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
34
+ @@counter+=1
39
35
  end
40
36
  end
41
37
 
@@ -60,7 +56,7 @@ class Bar < ActiveRecord::Base
60
56
  end
61
57
 
62
58
  class UnsavableRecord < ActiveRecord::Base
63
- attr_accessor :after_commit_called
59
+ attr_accessor :after_commit_called, :after_rollback_called
64
60
 
65
61
  set_table_name 'mock_records'
66
62
 
@@ -75,10 +71,36 @@ class UnsavableRecord < ActiveRecord::Base
75
71
  end
76
72
 
77
73
  after_commit :after_commit
78
-
79
74
  def after_commit
80
75
  self.after_commit_called = true
81
76
  end
77
+
78
+ after_rollback :after_rollback
79
+ def after_rollback
80
+ self.after_rollback_called = true
81
+ end
82
+ end
83
+
84
+ class TrackCountRecord < ActiveRecord::Base
85
+ attr_accessor :total_count
86
+
87
+ set_table_name 'mock_records'
88
+
89
+ protected
90
+
91
+ after_commit :update_counts
92
+ after_rollback :update_counts
93
+
94
+ def update_counts
95
+ all_records =
96
+ AfterCommit.records(connection) +
97
+ AfterCommit.created_records(connection) +
98
+ AfterCommit.updated_records(connection) +
99
+ AfterCommit.saved_records(connection) +
100
+ AfterCommit.destroyed_records(connection)
101
+ all_records.uniq!
102
+ self.total_count = all_records.size
103
+ end
82
104
  end
83
105
 
84
106
  class AfterCommitTest < Test::Unit::TestCase
@@ -88,10 +110,26 @@ class AfterCommitTest < Test::Unit::TestCase
88
110
 
89
111
  def test_before_commit_on_update_is_called
90
112
  record = MockRecord.create!
113
+ record.before_commit_on_update_called = false
91
114
  record.save
92
115
  assert_equal true, record.before_commit_on_update_called
93
116
  end
94
117
 
118
+ def test_before_commit_on_update_is_not_called_for_create
119
+ assert_nil MockRecord.create!.before_commit_on_update_called
120
+ end
121
+
122
+ def test_before_commit_on_save_is_called_for_create
123
+ assert_equal true, MockRecord.create!.before_commit_on_save_called
124
+ end
125
+
126
+ def test_before_commit_on_save_is_called_for_update
127
+ record = MockRecord.create!
128
+ record.before_commit_on_save_called = false
129
+ record.save
130
+ assert_equal true, record.before_commit_on_save_called
131
+ end
132
+
95
133
  def test_before_commit_on_destroy_is_called
96
134
  assert_equal true, MockRecord.create!.destroy.before_commit_on_destroy_called
97
135
  end
@@ -102,10 +140,26 @@ class AfterCommitTest < Test::Unit::TestCase
102
140
 
103
141
  def test_after_commit_on_update_is_called
104
142
  record = MockRecord.create!
143
+ record.after_commit_on_update_called = false
105
144
  record.save
106
145
  assert_equal true, record.after_commit_on_update_called
107
146
  end
108
147
 
148
+ def test_after_commit_on_update_is_not_called_for_create
149
+ assert_nil MockRecord.create!.after_commit_on_update_called
150
+ end
151
+
152
+ def test_after_commit_on_save_is_called_for_create
153
+ assert_equal true, MockRecord.create!.after_commit_on_save_called
154
+ end
155
+
156
+ def test_after_commit_on_save_is_called_for_update
157
+ record = MockRecord.create!
158
+ record.after_commit_on_save_called = false
159
+ record.save
160
+ assert_equal true, record.after_commit_on_save_called
161
+ end
162
+
109
163
  def test_after_commit_on_destroy_is_called
110
164
  assert_equal true, MockRecord.create!.destroy.after_commit_on_destroy_called
111
165
  end
@@ -113,14 +167,116 @@ class AfterCommitTest < Test::Unit::TestCase
113
167
  def test_after_commit_does_not_trigger_when_transaction_rolls_back
114
168
  record = UnsavableRecord.new
115
169
  begin; record.save; rescue; end
116
-
170
+
171
+ # Ensure that commit of another transaction doesn't then trigger the
172
+ # after_commit hook on previously rolled back record
173
+ another_record = MockRecord.create!
174
+ another_record.save
175
+
117
176
  assert_equal false, record.after_commit_called
118
177
  end
178
+
179
+ def test_after_commit_does_not_trigger_when_unrelated_transaction_commits
180
+ begin
181
+ CountingRecord.transaction do
182
+ CountingRecord.create!
183
+ raise "fail"
184
+ end
185
+ rescue
186
+ end
187
+ assert_equal 0, CountingRecord.counter
188
+ CountingRecord.create!
189
+ assert_equal 1, CountingRecord.counter
190
+ end
119
191
 
192
+ def test_after_rollback_triggered_when_transaction_rolls_back
193
+ record = UnsavableRecord.new
194
+ begin; record.save; rescue; end
195
+
196
+ assert record.after_rollback_called
197
+ end
198
+
120
199
  def test_two_transactions_are_separate
121
200
  Bar.delete_all
122
201
  foo = Foo.create
123
202
 
124
203
  assert_equal 1, foo.creating
125
204
  end
205
+
206
+ def test_records_with_callbacks_are_tracked
207
+ record = nil
208
+ MockRecord.transaction do
209
+ record = TrackCountRecord.create!
210
+ 5.times.each { MockRecord.create! }
211
+ end
212
+ assert_equal 6, record.total_count
213
+ end
214
+
215
+ def test_records_without_callbacks_are_not_tracked
216
+ record = nil
217
+ MockRecord.transaction do
218
+ record = TrackCountRecord.create!
219
+ 5.times.each { Bar.create! }
220
+ end
221
+ assert_equal 1, record.total_count
222
+ end
223
+
224
+ TestError = Class.new(StandardError)
225
+
226
+ def test_exceptions_in_before_commit_on_create_are_not_swallowed
227
+ record = MockRecord.new
228
+ def record.do_before_create
229
+ raise TestError, 'catch me!'
230
+ end
231
+ assert_raises(TestError){record.save!}
232
+ end
233
+
234
+ def test_exceptions_in_after_commit_on_create_are_not_swallowed
235
+ record = MockRecord.new
236
+ def record.do_after_create
237
+ raise TestError, 'catch me!'
238
+ end
239
+ assert_raises(TestError){record.save!}
240
+ end
241
+
242
+ def test_exceptions_in_before_commit_on_update_are_not_swallowed
243
+ record = MockRecord.create
244
+ def record.do_before_update
245
+ raise TestError, 'catch me!'
246
+ end
247
+ assert_raises(TestError){record.update_attributes({})}
248
+ end
249
+
250
+ def test_exceptions_in_after_commit_on_update_are_not_swallowed
251
+ record = MockRecord.create
252
+ def record.do_after_update
253
+ raise TestError, 'catch me!'
254
+ end
255
+ assert_raises(TestError){record.update_attributes({})}
256
+ end
257
+
258
+ def test_exceptions_in_before_commit_on_destroy_are_not_swallowed
259
+ record = MockRecord.create
260
+ def record.do_before_destroy
261
+ raise TestError, 'catch me!'
262
+ end
263
+ assert_raises(TestError){record.destroy}
264
+ end
265
+
266
+ def test_exceptions_in_after_commit_on_destroy_are_not_swallowed
267
+ record = MockRecord.create
268
+ def record.do_after_destroy
269
+ raise TestError, 'catch me!'
270
+ end
271
+ assert_raises(TestError){record.destroy}
272
+ end
273
+
274
+ def test_transactions_in_hooks_do_not_cause_spurious_rollbacks
275
+ record = MockRecord.create
276
+ def record.do_after_destroy
277
+ MockRecord.transaction{}
278
+ raise TestError, 'catch me!'
279
+ end
280
+ assert_raises(TestError){record.destroy}
281
+ end
126
282
  end
@@ -2,23 +2,20 @@ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib')
2
2
  require 'test/unit'
3
3
  require 'rubygems'
4
4
  require 'active_record'
5
-
6
- begin
7
- require 'sqlite3'
8
- rescue LoadError
9
- gem 'sqlite3-ruby'
10
- retry
11
- end
5
+ require 'sqlite3'
12
6
 
13
7
  ActiveRecord::Base.establish_connection({"adapter" => "sqlite3", "database" => 'test.sqlite3'})
8
+ tables = %w( mock_records counting_records foos bars )
9
+
14
10
  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");
11
+ tables.each do |table|
12
+ ActiveRecord::Base.connection.execute("drop table #{table}");
13
+ end
18
14
  rescue
19
15
  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)");
16
+
17
+ tables.each do |table|
18
+ ActiveRecord::Base.connection.execute("create table #{table}(id int, name string)");
19
+ end
23
20
 
24
21
  require 'after_commit'
metadata CHANGED
@@ -1,7 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: after_commit
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.6
4
+ prerelease: false
5
+ segments:
6
+ - 1
7
+ - 0
8
+ - 7
9
+ version: 1.0.7
5
10
  platform: ruby
6
11
  authors:
7
12
  - Nick Muerdter
@@ -11,29 +16,33 @@ autorequire:
11
16
  bindir: bin
12
17
  cert_chain: []
13
18
 
14
- date: 2010-02-16 00:00:00 +11:00
19
+ date: 2010-05-20 00:00:00 +10:00
15
20
  default_executable:
16
21
  dependencies:
17
22
  - !ruby/object:Gem::Dependency
18
23
  name: activerecord
19
- type: :runtime
20
- version_requirement:
21
- version_requirements: !ruby/object:Gem::Requirement
24
+ prerelease: false
25
+ requirement: &id001 !ruby/object:Gem::Requirement
22
26
  requirements:
23
27
  - - ">="
24
28
  - !ruby/object:Gem::Version
29
+ segments:
30
+ - 0
25
31
  version: "0"
26
- version:
32
+ type: :runtime
33
+ version_requirements: *id001
27
34
  - !ruby/object:Gem::Dependency
28
- name: shoulda
29
- type: :development
30
- version_requirement:
31
- version_requirements: !ruby/object:Gem::Requirement
35
+ name: sqlite3-ruby
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
32
38
  requirements:
33
39
  - - ">="
34
40
  - !ruby/object:Gem::Version
41
+ segments:
42
+ - 0
35
43
  version: "0"
36
- version:
44
+ type: :development
45
+ version_requirements: *id002
37
46
  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 "
38
47
  email: pat@freelancing-gods.com
39
48
  executables: []
@@ -47,8 +56,9 @@ files:
47
56
  - LICENSE
48
57
  - lib/after_commit.rb
49
58
  - lib/after_commit/active_record.rb
59
+ - lib/after_commit/active_support_callbacks.rb
60
+ - lib/after_commit/after_savepoint.rb
50
61
  - lib/after_commit/connection_adapters.rb
51
- - lib/after_commit/test_bypass.rb
52
62
  - README.textile
53
63
  has_rdoc: true
54
64
  homepage: http://github.com/freelancing-god/after_commit
@@ -63,18 +73,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
63
73
  requirements:
64
74
  - - ">="
65
75
  - !ruby/object:Gem::Version
76
+ segments:
77
+ - 0
66
78
  version: "0"
67
- version:
68
79
  required_rubygems_version: !ruby/object:Gem::Requirement
69
80
  requirements:
70
81
  - - ">="
71
82
  - !ruby/object:Gem::Version
83
+ segments:
84
+ - 0
72
85
  version: "0"
73
- version:
74
86
  requirements: []
75
87
 
76
88
  rubyforge_project:
77
- rubygems_version: 1.3.5
89
+ rubygems_version: 1.3.6
78
90
  signing_key:
79
91
  specification_version: 3
80
92
  summary: after_commit callback for ActiveRecord
@@ -1,30 +0,0 @@
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. Each of these method definitions
4
- # overwrites the method in the after_commit plugin that stores the callback for after the commit. In each case here
5
- # we simply call the callback rather than waiting for a commit that will never come.
6
-
7
- module AfterCommit::TestBypass
8
- def self.included(klass)
9
- klass.class_eval do
10
- [:add_committed_record_on_create, :add_committed_record_on_update, :add_committed_record_on_destroy].each do |method|
11
- remove_method(method)
12
- end
13
- end
14
- end
15
-
16
- def add_committed_record_on_create
17
- callback :after_commit
18
- callback :after_commit_on_create
19
- end
20
-
21
- def add_committed_record_on_update
22
- callback :after_commit
23
- callback :after_commit_on_update
24
- end
25
-
26
- def add_committed_record_on_destroy
27
- callback :after_commit
28
- callback :after_commit_on_destroy
29
- end
30
- end