after_commit 1.0.6 → 1.0.7

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.
@@ -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