chrismuc-after_commit 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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
+
@@ -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