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 ADDED
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ tmp/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in outside_transaction.gemspec
4
+ gemspec
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,6 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rspec/core/rake_task'
4
+ RSpec::Core::RakeTask.new(:spec)
5
+ task :default => :spec
6
+
@@ -0,0 +1,3 @@
1
+ module OutsideTransaction
2
+ VERSION = "0.0.1"
3
+ end
@@ -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