outside_transaction 0.0.1
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/.gitignore +5 -0
- data/Gemfile +4 -0
- data/README.md +30 -0
- data/Rakefile +6 -0
- data/lib/outside_transaction/version.rb +3 -0
- data/lib/outside_transaction.rb +95 -0
- data/outside_transaction.gemspec +25 -0
- data/spec/outside_transaction_spec.rb +64 -0
- metadata +118 -0
data/Gemfile
ADDED
data/README.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# outside_transaction
|
|
2
|
+
|
|
3
|
+
Makes sure the given block is only executed when not inside a transaction.
|
|
4
|
+
If there is an open transaction it will be executed after the next
|
|
5
|
+
successful commit or not executed at all if there is a rollback or failure.
|
|
6
|
+
|
|
7
|
+
This is useful when you're notifying another process.
|
|
8
|
+
If the other process starts processing the message before the transaction
|
|
9
|
+
is actually committed, it will not see your uncommitted changes as
|
|
10
|
+
transactions are isolated (usually).
|
|
11
|
+
|
|
12
|
+
In the example below, if there is a lot of work after saving and
|
|
13
|
+
article, and `ArticleEmailSender` is started too soon, it will not find
|
|
14
|
+
the article you just inserted.
|
|
15
|
+
|
|
16
|
+
## Example:
|
|
17
|
+
|
|
18
|
+
```ruby
|
|
19
|
+
class Article
|
|
20
|
+
after_create :send_emails
|
|
21
|
+
|
|
22
|
+
def send_emails
|
|
23
|
+
outside_transaction do
|
|
24
|
+
Resque.enqueue(ArticleEmailSender, id) # notifies another process
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
This example is using [Resque](https://github.com/defunkt/resque).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
require "outside_transaction/version"
|
|
2
|
+
require 'active_record'
|
|
3
|
+
|
|
4
|
+
Object.class_eval do
|
|
5
|
+
def outside_transaction(&block)
|
|
6
|
+
ActiveRecord::Base.connection.outside_transaction(&block)
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
module OutsideTransaction
|
|
11
|
+
def self.enabled?
|
|
12
|
+
! (@@disabled ||= false)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.disable
|
|
16
|
+
@@disabled = true
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
module Connection
|
|
20
|
+
def self.included(base)
|
|
21
|
+
unless base.method_defined? :commit_db_transaction_without_outside_transaction
|
|
22
|
+
base.alias_method_chain :commit_db_transaction, :outside_transaction
|
|
23
|
+
end
|
|
24
|
+
unless base.method_defined? :transaction_without_outside_transaction
|
|
25
|
+
base.alias_method_chain :transaction, :outside_transaction
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def outside_transaction(&block)
|
|
30
|
+
if block_given?
|
|
31
|
+
if open_transactions == 0 || !::OutsideTransaction.enabled?
|
|
32
|
+
block.call
|
|
33
|
+
else
|
|
34
|
+
(@outside_transaction ||= []) << block
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def commit_db_transaction_with_outside_transaction
|
|
40
|
+
outside_transaction_actions = @outside_transaction
|
|
41
|
+
@outside_transaction = nil
|
|
42
|
+
commit_db_transaction_without_outside_transaction
|
|
43
|
+
if outside_transaction_actions
|
|
44
|
+
outside_transaction_actions.each do |block|
|
|
45
|
+
block.call
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def transaction_with_outside_transaction(*args, &block)
|
|
51
|
+
transaction_without_outside_transaction(*args, &block)
|
|
52
|
+
ensure
|
|
53
|
+
@outside_transaction = nil if open_transactions == 0
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
module ModelMethods
|
|
58
|
+
module ClassMethods
|
|
59
|
+
def outside_transaction(&block)
|
|
60
|
+
connection.outside_transaction(&block)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def self.included(base)
|
|
65
|
+
base.extend ClassMethods
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def outside_transaction(&block)
|
|
69
|
+
self.class.connection.outside_transaction(&block)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
ActiveRecord::Base.send(:include, OutsideTransaction::ModelMethods)
|
|
75
|
+
|
|
76
|
+
# Hack into the adapter class when the connection is made.
|
|
77
|
+
ActiveRecord::Base.instance_eval do
|
|
78
|
+
class << self
|
|
79
|
+
|
|
80
|
+
def establish_connection_with_outside_transaction(*args, &block)
|
|
81
|
+
establish_connection_without_outside_transaction(*args, &block).tap {|pool|
|
|
82
|
+
|
|
83
|
+
# ActiveRecord::ConnectionAdapters::SQLite3Adapter.class_eval do
|
|
84
|
+
pool.connection.class.class_eval do
|
|
85
|
+
include OutsideTransaction::Connection
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
}
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
alias_method_chain :establish_connection, :outside_transaction
|
|
92
|
+
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
|
3
|
+
require "outside_transaction/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |s|
|
|
6
|
+
s.name = "outside_transaction"
|
|
7
|
+
s.version = OutsideTransaction::VERSION
|
|
8
|
+
s.authors = ["Levente Bagi"]
|
|
9
|
+
s.email = ["levente@picklive.com"]
|
|
10
|
+
s.homepage = "https://github.com/Picklive/outside_transaction"
|
|
11
|
+
s.summary = %q{Run block outside transaction.}
|
|
12
|
+
s.description = %q{Run block only if not inside a transaction or when transaction is committed.}
|
|
13
|
+
|
|
14
|
+
s.rubyforge_project = "outside_transaction"
|
|
15
|
+
|
|
16
|
+
s.files = `git ls-files`.split("\n")
|
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
|
19
|
+
s.require_paths = ["lib"]
|
|
20
|
+
|
|
21
|
+
s.add_dependency "activerecord", ">= 3.0.0"
|
|
22
|
+
|
|
23
|
+
s.add_development_dependency "rspec"
|
|
24
|
+
s.add_development_dependency "sqlite3"
|
|
25
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
require 'outside_transaction'
|
|
2
|
+
|
|
3
|
+
ROOT_DIR = File.expand_path('../..')
|
|
4
|
+
FileUtils.makedirs "#{ROOT_DIR}/tmp"
|
|
5
|
+
ActiveRecord::Base.establish_connection(
|
|
6
|
+
:adapter => 'sqlite3',
|
|
7
|
+
:database => "#{ROOT_DIR}/tmp/test_db.sqlite3",
|
|
8
|
+
:pool => 5,
|
|
9
|
+
:timeout => 5000
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
describe OutsideTransaction do
|
|
13
|
+
|
|
14
|
+
let(:connection) { ActiveRecord::Base.connection }
|
|
15
|
+
delegate :transaction, :to => :connection
|
|
16
|
+
|
|
17
|
+
before(:each) { @block_ran = 0 }
|
|
18
|
+
let(:test_block) { Proc.new { @block_ran += 1 } }
|
|
19
|
+
|
|
20
|
+
def should_have_been_run
|
|
21
|
+
@block_ran.should >= 1
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def should_not_have_been_run
|
|
25
|
+
@block_ran.should == 0
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
context "when there is no transaction open" do
|
|
29
|
+
it "runs the block" do
|
|
30
|
+
outside_transaction(&test_block)
|
|
31
|
+
should_have_been_run
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
context "when a transaction is open" do
|
|
36
|
+
it "runs the block when the transaction is commited" do
|
|
37
|
+
transaction do
|
|
38
|
+
outside_transaction(&test_block)
|
|
39
|
+
should_not_have_been_run
|
|
40
|
+
end
|
|
41
|
+
should_have_been_run
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "doesn't run the block if the transaction is rolled back" do
|
|
45
|
+
transaction do
|
|
46
|
+
outside_transaction(&test_block)
|
|
47
|
+
raise ActiveRecord::Rollback
|
|
48
|
+
end
|
|
49
|
+
should_not_have_been_run
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it "doesn't run the block if the commit fails" do
|
|
53
|
+
begin
|
|
54
|
+
transaction do
|
|
55
|
+
outside_transaction(&test_block)
|
|
56
|
+
raise "Something went wrong"
|
|
57
|
+
end
|
|
58
|
+
rescue
|
|
59
|
+
end
|
|
60
|
+
should_not_have_been_run
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: outside_transaction
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
hash: 29
|
|
5
|
+
prerelease:
|
|
6
|
+
segments:
|
|
7
|
+
- 0
|
|
8
|
+
- 0
|
|
9
|
+
- 1
|
|
10
|
+
version: 0.0.1
|
|
11
|
+
platform: ruby
|
|
12
|
+
authors:
|
|
13
|
+
- Levente Bagi
|
|
14
|
+
autorequire:
|
|
15
|
+
bindir: bin
|
|
16
|
+
cert_chain: []
|
|
17
|
+
|
|
18
|
+
date: 2011-11-09 00:00:00 +00:00
|
|
19
|
+
default_executable:
|
|
20
|
+
dependencies:
|
|
21
|
+
- !ruby/object:Gem::Dependency
|
|
22
|
+
name: activerecord
|
|
23
|
+
prerelease: false
|
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
|
25
|
+
none: false
|
|
26
|
+
requirements:
|
|
27
|
+
- - ">="
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
hash: 7
|
|
30
|
+
segments:
|
|
31
|
+
- 3
|
|
32
|
+
- 0
|
|
33
|
+
- 0
|
|
34
|
+
version: 3.0.0
|
|
35
|
+
type: :runtime
|
|
36
|
+
version_requirements: *id001
|
|
37
|
+
- !ruby/object:Gem::Dependency
|
|
38
|
+
name: rspec
|
|
39
|
+
prerelease: false
|
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
|
41
|
+
none: false
|
|
42
|
+
requirements:
|
|
43
|
+
- - ">="
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
hash: 3
|
|
46
|
+
segments:
|
|
47
|
+
- 0
|
|
48
|
+
version: "0"
|
|
49
|
+
type: :development
|
|
50
|
+
version_requirements: *id002
|
|
51
|
+
- !ruby/object:Gem::Dependency
|
|
52
|
+
name: sqlite3
|
|
53
|
+
prerelease: false
|
|
54
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
|
55
|
+
none: false
|
|
56
|
+
requirements:
|
|
57
|
+
- - ">="
|
|
58
|
+
- !ruby/object:Gem::Version
|
|
59
|
+
hash: 3
|
|
60
|
+
segments:
|
|
61
|
+
- 0
|
|
62
|
+
version: "0"
|
|
63
|
+
type: :development
|
|
64
|
+
version_requirements: *id003
|
|
65
|
+
description: Run block only if not inside a transaction or when transaction is committed.
|
|
66
|
+
email:
|
|
67
|
+
- levente@picklive.com
|
|
68
|
+
executables: []
|
|
69
|
+
|
|
70
|
+
extensions: []
|
|
71
|
+
|
|
72
|
+
extra_rdoc_files: []
|
|
73
|
+
|
|
74
|
+
files:
|
|
75
|
+
- .gitignore
|
|
76
|
+
- Gemfile
|
|
77
|
+
- README.md
|
|
78
|
+
- Rakefile
|
|
79
|
+
- lib/outside_transaction.rb
|
|
80
|
+
- lib/outside_transaction/version.rb
|
|
81
|
+
- outside_transaction.gemspec
|
|
82
|
+
- spec/outside_transaction_spec.rb
|
|
83
|
+
has_rdoc: true
|
|
84
|
+
homepage: https://github.com/Picklive/outside_transaction
|
|
85
|
+
licenses: []
|
|
86
|
+
|
|
87
|
+
post_install_message:
|
|
88
|
+
rdoc_options: []
|
|
89
|
+
|
|
90
|
+
require_paths:
|
|
91
|
+
- lib
|
|
92
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
93
|
+
none: false
|
|
94
|
+
requirements:
|
|
95
|
+
- - ">="
|
|
96
|
+
- !ruby/object:Gem::Version
|
|
97
|
+
hash: 3
|
|
98
|
+
segments:
|
|
99
|
+
- 0
|
|
100
|
+
version: "0"
|
|
101
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
102
|
+
none: false
|
|
103
|
+
requirements:
|
|
104
|
+
- - ">="
|
|
105
|
+
- !ruby/object:Gem::Version
|
|
106
|
+
hash: 3
|
|
107
|
+
segments:
|
|
108
|
+
- 0
|
|
109
|
+
version: "0"
|
|
110
|
+
requirements: []
|
|
111
|
+
|
|
112
|
+
rubyforge_project: outside_transaction
|
|
113
|
+
rubygems_version: 1.6.2
|
|
114
|
+
signing_key:
|
|
115
|
+
specification_version: 3
|
|
116
|
+
summary: Run block outside transaction.
|
|
117
|
+
test_files:
|
|
118
|
+
- spec/outside_transaction_spec.rb
|