chrismuc-after_commit 0.9.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/README +40 -0
- data/lib/after_commit.rb +85 -0
- data/test/after_commit_test.rb +46 -0
- metadata +55 -0
data/README
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
after_commit
|
2
|
+
============
|
3
|
+
|
4
|
+
After_commit enables you to add after_commit methods to ActiveRecord models, which are called once the transaction has been committed to the database.
|
5
|
+
Typically one needs this in asynchronous processing where you pass on IDs of new records to another thread or post them to a message queue. Using the stock after_XXX
|
6
|
+
callbacks you cannot be sure that the other thread/process is already seeing the new record (at least if the database is ACID compliant).
|
7
|
+
|
8
|
+
|
9
|
+
Usage:
|
10
|
+
======
|
11
|
+
|
12
|
+
class Blog < ActiveRecord::Base
|
13
|
+
include AfterCommit::ActiveRecord
|
14
|
+
|
15
|
+
def after_commit_on_create
|
16
|
+
# do something
|
17
|
+
end
|
18
|
+
|
19
|
+
def after_commit_on_update
|
20
|
+
# do something
|
21
|
+
end
|
22
|
+
|
23
|
+
def after_commit_on_destroy
|
24
|
+
# do something
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
You have to include AfterCommit::ActiveRecord to add the new callbacks. They aren't automatically added to ActiveRecord::Base as tracking the committed
|
30
|
+
records takes a little overhead and I thought it's better to be able to manually select which models should get that feature.
|
31
|
+
|
32
|
+
You have to explicitely define the methods like in the example above, something like after_commit_on_create :do_what_ever doesn't work.
|
33
|
+
|
34
|
+
Credits:
|
35
|
+
========
|
36
|
+
I've take existing code and basically made it threadsafe (hopefully, so it should be safe when running config.threadsafe!). Sources are:
|
37
|
+
|
38
|
+
http://github.com/GUI/after_commit/tree/master
|
39
|
+
http://github.com/freelancing-god/thinking-sphinx
|
40
|
+
|
data/lib/after_commit.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
module AfterCommit
|
2
|
+
def self.touched_records(type)
|
3
|
+
h = (Thread.current[:after_commit] ||= {})
|
4
|
+
h[type] ||= []
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.clear
|
8
|
+
Thread.current[:after_commit] = nil
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.trigger
|
12
|
+
begin
|
13
|
+
[:create, :update, :destroy].each do |type|
|
14
|
+
callback_name = "after_commit_on_#{type}"
|
15
|
+
touched_records(type).each do |record|
|
16
|
+
begin
|
17
|
+
record.send(callback_name) if record.respond_to?(callback_name)
|
18
|
+
rescue => e
|
19
|
+
puts e
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
ensure
|
24
|
+
self.clear
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
module ActiveRecord
|
29
|
+
def self.included(base)
|
30
|
+
base.class_eval do
|
31
|
+
after_create :add_touched_record_on_create
|
32
|
+
after_update :add_touched_record_on_update
|
33
|
+
after_destroy :add_touched_record_on_destroy
|
34
|
+
|
35
|
+
def add_touched_record_on_create
|
36
|
+
AfterCommit.touched_records(:create) << self
|
37
|
+
end
|
38
|
+
|
39
|
+
def add_touched_record_on_update
|
40
|
+
AfterCommit.touched_records(:update) << self
|
41
|
+
end
|
42
|
+
|
43
|
+
def add_touched_record_on_destroy
|
44
|
+
AfterCommit.touched_records(:destroy) << self
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
module ConnectionAdapters
|
51
|
+
def self.included(base)
|
52
|
+
base.class_eval do
|
53
|
+
# The commit_db_transaction method gets called when the outermost
|
54
|
+
# transaction finishes and everything inside commits. We want to
|
55
|
+
# override it so that after this happens, any records that were saved
|
56
|
+
# or destroyed within this transaction now get their after_commit
|
57
|
+
# callback fired.
|
58
|
+
def commit_db_transaction_with_callback
|
59
|
+
commit_db_transaction_without_callback
|
60
|
+
AfterCommit.trigger
|
61
|
+
end
|
62
|
+
alias_method_chain :commit_db_transaction, :callback
|
63
|
+
|
64
|
+
def rollback_db_transaction_with_callback
|
65
|
+
rollback_db_transaction_without_callback
|
66
|
+
AfterCommit.clear
|
67
|
+
end
|
68
|
+
alias_method_chain :rollback_db_transaction, :callback
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
unless defined? AFTER_COMMIT_PATCH_APPLIED
|
75
|
+
AFTER_COMMIT_PATCH_APPLIED=true # in my test setup this was executed twice resulting in an infinite recursion
|
76
|
+
|
77
|
+
Object.subclasses_of(ActiveRecord::ConnectionAdapters::AbstractAdapter).each do |klass|
|
78
|
+
puts "Applying after_commit patch to: #{klass}"
|
79
|
+
klass.send(:include, AfterCommit::ConnectionAdapters)
|
80
|
+
end
|
81
|
+
|
82
|
+
if defined?(JRUBY_VERSION) and defined?(JdbcSpec::MySQL)
|
83
|
+
JdbcSpec::MySQL.send :include, AfterCommit::ConnectionAdapters
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib')
|
2
|
+
require 'test/unit'
|
3
|
+
require 'rubygems'
|
4
|
+
require 'activerecord'
|
5
|
+
|
6
|
+
ActiveRecord::Base.establish_connection({"adapter" => defined?(JRUBY_VERSION) ? "jdbcsqlite3":"sqlite3", "database" => 'test.sqlite3'})
|
7
|
+
begin
|
8
|
+
ActiveRecord::Base.connection.execute("drop table mock_records");
|
9
|
+
rescue
|
10
|
+
end
|
11
|
+
ActiveRecord::Base.connection.execute("create table mock_records(id int)");
|
12
|
+
|
13
|
+
require File.dirname(__FILE__) + '/../init.rb'
|
14
|
+
|
15
|
+
class MockRecord < ActiveRecord::Base
|
16
|
+
include AfterCommit::ActiveRecord
|
17
|
+
attr_accessor :after_commit_on_create_called, :after_commit_on_update_called, :after_commit_on_destroy_called
|
18
|
+
|
19
|
+
def after_commit_on_create
|
20
|
+
self.after_commit_on_create_called = true
|
21
|
+
end
|
22
|
+
|
23
|
+
def after_commit_on_update
|
24
|
+
self.after_commit_on_update_called = true
|
25
|
+
end
|
26
|
+
|
27
|
+
def after_commit_on_destroy
|
28
|
+
self.after_commit_on_destroy_called = true
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class AfterCommitTest < Test::Unit::TestCase
|
33
|
+
def test_after_commit_on_create_is_called
|
34
|
+
assert_equal true, MockRecord.create!.after_commit_on_create_called
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_after_commit_on_update_is_called
|
38
|
+
record = MockRecord.create!
|
39
|
+
record.save
|
40
|
+
assert_equal true, record.after_commit_on_update_called
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_after_commit_on_destroy_is_called
|
44
|
+
assert_equal true, MockRecord.create!.destroy.after_commit_on_destroy_called
|
45
|
+
end
|
46
|
+
end
|
metadata
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: chrismuc-after_commit
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.9.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Christian Seiler
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-06-02 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Threadsafe after_commit callbacks for ActiveRecord models.
|
17
|
+
email: chr.seiler@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- README
|
26
|
+
- test/after_commit_test.rb
|
27
|
+
- lib/after_commit.rb
|
28
|
+
has_rdoc: false
|
29
|
+
homepage: http://github.com/mislav/will_paginate/wikis
|
30
|
+
post_install_message:
|
31
|
+
rdoc_options: []
|
32
|
+
|
33
|
+
require_paths:
|
34
|
+
- lib
|
35
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: "0"
|
40
|
+
version:
|
41
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: "0"
|
46
|
+
version:
|
47
|
+
requirements: []
|
48
|
+
|
49
|
+
rubyforge_project:
|
50
|
+
rubygems_version: 1.2.0
|
51
|
+
signing_key:
|
52
|
+
specification_version: 2
|
53
|
+
summary: Threadsafe after_commit callbacks for ActiveRecord models
|
54
|
+
test_files:
|
55
|
+
- test/after_commit_test.rb
|