after_commit 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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.
@@ -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
+ * Gemspec and extended tests ("David Yip":http://github.com/yipdw)
47
+ * Thread-safety ("Dmitry Galinsky":http://dima-exe.ru/)
48
+ * after_rollback callback ("Justin Balthrop":http://github.com/ninjudd)
49
+ * Test environment fix ("Pivotal Labs":http://pivotalblabs.com/)
50
+ * Scoping callbacks to specific connections ("Mat Brown":http://outofti.me/)
51
+ * before_* callbacks ("Trotter Cashion":http://trottercashion.com/)
@@ -0,0 +1,65 @@
1
+ module AfterCommit
2
+ def self.record(connection, record)
3
+ Thread.current[:committed_records] ||= {}
4
+ Thread.current[:committed_records][connection.object_id] ||= []
5
+ Thread.current[:committed_records][connection.object_id] << record
6
+ end
7
+
8
+ def self.record_created(connection, record)
9
+ Thread.current[:committed_records_on_create] ||= {}
10
+ Thread.current[:committed_records_on_create][connection.object_id] ||= []
11
+ Thread.current[:committed_records_on_create][connection.object_id] << record
12
+ end
13
+
14
+ def self.record_updated(connection, record)
15
+ Thread.current[:committed_records_on_update] ||= {}
16
+ Thread.current[:committed_records_on_update][connection.object_id] ||= []
17
+ Thread.current[:committed_records_on_update][connection.object_id] << record
18
+ end
19
+
20
+ def self.record_destroyed(connection, record)
21
+ Thread.current[:committed_records_on_destroy] ||= {}
22
+ Thread.current[:committed_records_on_destroy][connection.object_id] ||= []
23
+ Thread.current[:committed_records_on_destroy][connection.object_id] << record
24
+ end
25
+
26
+ def self.created_records(connection)
27
+ Thread.current[:committed_records_on_create] ||= {}
28
+ Thread.current[:committed_records_on_create][connection.object_id] ||= []
29
+ end
30
+
31
+ def self.updated_records(connection)
32
+ Thread.current[:committed_records_on_update] ||= {}
33
+ Thread.current[:committed_records_on_update][connection.object_id] ||= []
34
+ end
35
+
36
+ def self.destroyed_records(connection)
37
+ Thread.current[:committed_records_on_destroy] ||= {}
38
+ Thread.current[:committed_records_on_destroy][connection.object_id] ||= []
39
+ end
40
+
41
+ def self.records(connection)
42
+ Thread.current[:committed_records] ||= {}
43
+ Thread.current[:committed_records][connection.object_id] ||= []
44
+ end
45
+
46
+ def self.cleanup(connection)
47
+ Thread.current[:committed_records] = {}
48
+ Thread.current[:committed_records_on_create] = {}
49
+ Thread.current[:committed_records_on_update] = {}
50
+ Thread.current[:committed_records_on_destroy] = {}
51
+ end
52
+ end
53
+
54
+ require 'after_commit/active_record'
55
+ require 'after_commit/connection_adapters'
56
+
57
+ ActiveRecord::Base.send(:include, AfterCommit::ActiveRecord)
58
+
59
+ Object.subclasses_of(ActiveRecord::ConnectionAdapters::AbstractAdapter).each do |klass|
60
+ klass.send(:include, AfterCommit::ConnectionAdapters)
61
+ end
62
+
63
+ if defined?(JRUBY_VERSION) and defined?(JdbcSpec::MySQL)
64
+ JdbcSpec::MySQL.send :include, AfterCommit::ConnectionAdapters
65
+ end
@@ -0,0 +1,94 @@
1
+ module AfterCommit
2
+ module ActiveRecord
3
+ def self.included(base)
4
+ base.class_eval do
5
+ # The define_callbacks method was added post Rails 2.0.2 - if it
6
+ # doesn't exist, we define the callback manually
7
+ if respond_to?(:define_callbacks)
8
+ define_callbacks :after_commit,
9
+ :after_commit_on_create,
10
+ :after_commit_on_update,
11
+ :after_commit_on_destroy,
12
+ :after_rollback,
13
+ :before_commit,
14
+ :before_commit_on_create,
15
+ :before_commit_on_update,
16
+ :before_commit_on_destroy,
17
+ :before_rollback
18
+ else
19
+ class << self
20
+ # Handle after_commit callbacks - call all the registered callbacks.
21
+ def after_commit(*callbacks, &block)
22
+ callbacks << block if block_given?
23
+ write_inheritable_array(:after_commit, callbacks)
24
+ end
25
+
26
+ def after_commit_on_create(*callbacks, &block)
27
+ callbacks << block if block_given?
28
+ write_inheritable_array(:after_commit_on_create, callbacks)
29
+ end
30
+
31
+ def after_commit_on_update(*callbacks, &block)
32
+ callbacks << block if block_given?
33
+ write_inheritable_array(:after_commit_on_update, callbacks)
34
+ end
35
+
36
+ def after_commit_on_destroy(*callbacks, &block)
37
+ callbacks << block if block_given?
38
+ write_inheritable_array(:after_commit_on_destroy, callbacks)
39
+ end
40
+
41
+ def after_rollback(*callbacks, &block)
42
+ callbacks << block if block_given?
43
+ write_inheritable_array(:after_commit, callbacks)
44
+ end
45
+
46
+ def before_commit(*callbacks, &block)
47
+ callbacks << block if block_given?
48
+ write_inheritable_array(:before_commit, callbacks)
49
+ end
50
+
51
+ def before_commit_on_create(*callbacks, &block)
52
+ callbacks << block if block_given?
53
+ write_inheritable_array(:before_commit_on_create, callbacks)
54
+ end
55
+
56
+ def before_commit_on_update(*callbacks, &block)
57
+ callbacks << block if block_given?
58
+ write_inheritable_array(:before_commit_on_update, callbacks)
59
+ end
60
+
61
+ def before_commit_on_destroy(*callbacks, &block)
62
+ callbacks << block if block_given?
63
+ write_inheritable_array(:before_commit_on_destroy, callbacks)
64
+ end
65
+
66
+ def before_rollback(*callbacks, &block)
67
+ callbacks << block if block_given?
68
+ write_inheritable_array(:before_commit, callbacks)
69
+ end
70
+ end
71
+ end
72
+
73
+ after_create :add_committed_record_on_create
74
+ after_update :add_committed_record_on_update
75
+ after_destroy :add_committed_record_on_destroy
76
+
77
+ def add_committed_record_on_create
78
+ AfterCommit.record(self.class.connection, self)
79
+ AfterCommit.record_created(self.class.connection, self)
80
+ end
81
+
82
+ def add_committed_record_on_update
83
+ AfterCommit.record(self.class.connection, self)
84
+ AfterCommit.record_updated(self.class.connection, self)
85
+ end
86
+
87
+ def add_committed_record_on_destroy
88
+ AfterCommit.record(self.class.connection, self)
89
+ AfterCommit.record_destroyed(self.class.connection, self)
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,140 @@
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
+ begin
12
+ trigger_before_commit_callbacks
13
+ trigger_before_commit_on_create_callbacks
14
+ trigger_before_commit_on_update_callbacks
15
+ trigger_before_commit_on_destroy_callbacks
16
+
17
+ commit_db_transaction_without_callback
18
+
19
+ trigger_after_commit_callbacks
20
+ trigger_after_commit_on_create_callbacks
21
+ trigger_after_commit_on_update_callbacks
22
+ trigger_after_commit_on_destroy_callbacks
23
+ ensure
24
+ AfterCommit.cleanup(self)
25
+ end
26
+ end
27
+ alias_method_chain :commit_db_transaction, :callback
28
+
29
+ # In the event the transaction fails and rolls back, nothing inside
30
+ # should recieve the after_commit callback, but do fire the after_rollback
31
+ # callback for each record that failed to be committed.
32
+ def rollback_db_transaction_with_callback
33
+ begin
34
+ trigger_before_rollback_callbacks
35
+ rollback_db_transaction_without_callback
36
+ trigger_after_rollback_callbacks
37
+ ensure
38
+ AfterCommit.cleanup(self)
39
+ end
40
+ end
41
+ alias_method_chain :rollback_db_transaction, :callback
42
+
43
+ protected
44
+
45
+ def trigger_before_commit_callbacks
46
+ AfterCommit.records(self).each do |record|
47
+ record.send :callback, :before_commit
48
+ end
49
+ end
50
+
51
+ def trigger_before_commit_on_create_callbacks
52
+ AfterCommit.created_records(self).each do |record|
53
+ record.send :callback, :before_commit_on_create
54
+ end
55
+ end
56
+
57
+ def trigger_before_commit_on_update_callbacks
58
+ AfterCommit.updated_records(self).each do |record|
59
+ record.send :callback, :before_commit_on_update
60
+ end
61
+ end
62
+
63
+ def trigger_before_commit_on_destroy_callbacks
64
+ AfterCommit.destroyed_records(self).each do |record|
65
+ record.send :callback, :before_commit_on_destroy
66
+ end
67
+ end
68
+
69
+ def trigger_before_rollback_callbacks
70
+ AfterCommit.records(self).each do |record|
71
+ begin
72
+ record.send :callback, :before_rollback
73
+ rescue
74
+ #
75
+ end
76
+ end
77
+ end
78
+
79
+ def trigger_after_commit_callbacks
80
+ # Trigger the after_commit callback for each of the committed
81
+ # records.
82
+ AfterCommit.records(self).each do |record|
83
+ begin
84
+ record.send :callback, :after_commit
85
+ rescue
86
+ #
87
+ end
88
+ end
89
+ end
90
+
91
+ def trigger_after_commit_on_create_callbacks
92
+ # Trigger the after_commit_on_create callback for each of the committed
93
+ # records.
94
+ AfterCommit.created_records(self).each do |record|
95
+ begin
96
+ record.send :callback, :after_commit_on_create
97
+ rescue
98
+ #
99
+ end
100
+ end
101
+ end
102
+
103
+ def trigger_after_commit_on_update_callbacks
104
+ # Trigger the after_commit_on_update callback for each of the committed
105
+ # records.
106
+ AfterCommit.updated_records(self).each do |record|
107
+ begin
108
+ record.send :callback, :after_commit_on_update
109
+ rescue
110
+ #
111
+ end
112
+ end
113
+ end
114
+
115
+ def trigger_after_commit_on_destroy_callbacks
116
+ # Trigger the after_commit_on_destroy callback for each of the committed
117
+ # records.
118
+ AfterCommit.destroyed_records(self).each do |record|
119
+ begin
120
+ record.send :callback, :after_commit_on_destroy
121
+ rescue
122
+ #
123
+ end
124
+ end
125
+ end
126
+
127
+ def trigger_after_rollback_callbacks
128
+ # Trigger the after_rollback callback for each of the committed
129
+ # records.
130
+ AfterCommit.records(self).each do |record|
131
+ begin
132
+ record.send :callback, :after_rollback
133
+ rescue
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,31 @@
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, :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
17
+ after_commit_callback
18
+ end
19
+
20
+ def add_committed_record_on_create
21
+ after_commit_on_create_callback
22
+ end
23
+
24
+ def add_committed_record_on_update
25
+ after_commit_on_update_callback
26
+ end
27
+
28
+ def add_committed_record_on_destroy
29
+ after_commit_on_destroy_callback
30
+ end
31
+ end
@@ -0,0 +1,6 @@
1
+ require 'after_commit'
2
+
3
+ ActiveRecord::Base.class_eval do
4
+ include AfterCommit::ActiveRecord
5
+ include AfterCommit::TestBypass if RAILS_ENV == 'test'
6
+ end
@@ -0,0 +1,99 @@
1
+ require File.dirname(__FILE__) + '/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 UnsavableRecord < ActiveRecord::Base
43
+ attr_accessor :after_commit_called
44
+
45
+ set_table_name 'mock_records'
46
+
47
+ protected
48
+
49
+ def after_initialize
50
+ self.after_commit_called = false
51
+ end
52
+
53
+ def after_save
54
+ raise
55
+ end
56
+
57
+ after_commit :after_commit
58
+
59
+ def after_commit
60
+ self.after_commit_called = true
61
+ end
62
+ end
63
+
64
+ class AfterCommitTest < Test::Unit::TestCase
65
+ def test_before_commit_on_create_is_called
66
+ assert_equal true, MockRecord.create!.before_commit_on_create_called
67
+ end
68
+
69
+ def test_before_commit_on_update_is_called
70
+ record = MockRecord.create!
71
+ record.save
72
+ assert_equal true, record.before_commit_on_update_called
73
+ end
74
+
75
+ def test_before_commit_on_destroy_is_called
76
+ assert_equal true, MockRecord.create!.destroy.before_commit_on_destroy_called
77
+ end
78
+
79
+ def test_after_commit_on_create_is_called
80
+ assert_equal true, MockRecord.create!.after_commit_on_create_called
81
+ end
82
+
83
+ def test_after_commit_on_update_is_called
84
+ record = MockRecord.create!
85
+ record.save
86
+ assert_equal true, record.after_commit_on_update_called
87
+ end
88
+
89
+ def test_after_commit_on_destroy_is_called
90
+ assert_equal true, MockRecord.create!.destroy.after_commit_on_destroy_called
91
+ end
92
+
93
+ def test_after_commit_does_not_trigger_when_transaction_rolls_back
94
+ record = UnsavableRecord.new
95
+ begin; record.save; rescue; end
96
+
97
+ assert_equal false, record.after_commit_called
98
+ end
99
+ end
@@ -0,0 +1,59 @@
1
+ require File.dirname(__FILE__) + '/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
@@ -0,0 +1,20 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib')
2
+ require 'test/unit'
3
+ require 'rubygems'
4
+ require 'activerecord'
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
+ rescue
17
+ end
18
+ ActiveRecord::Base.connection.execute("create table mock_records(id int)");
19
+
20
+ require 'after_commit'
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: after_commit
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Nick Muerdter
8
+ - David Yip
9
+ - Pat Allan
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+
14
+ date: 2009-11-10 00:00:00 +11:00
15
+ default_executable:
16
+ dependencies:
17
+ - !ruby/object:Gem::Dependency
18
+ name: activerecord
19
+ type: :runtime
20
+ version_requirement:
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: "0"
26
+ version:
27
+ - !ruby/object:Gem::Dependency
28
+ name: shoulda
29
+ type: :development
30
+ version_requirement:
31
+ version_requirements: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: "0"
36
+ version:
37
+ 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
+ email: pat@freelancing-gods.com
39
+ executables: []
40
+
41
+ extensions: []
42
+
43
+ extra_rdoc_files:
44
+ - LICENSE
45
+ - README.textile
46
+ files:
47
+ - LICENSE
48
+ - lib/after_commit.rb
49
+ - lib/after_commit/active_record.rb
50
+ - lib/after_commit/connection_adapters.rb
51
+ - lib/after_commit/test_bypass.rb
52
+ - rails/init.rb
53
+ - README.textile
54
+ has_rdoc: true
55
+ homepage: http://github.com/freelancing-god/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