test_after_commit 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Appraisals +4 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +2 -2
- data/Rakefile +1 -1
- data/Readme.md +8 -1
- data/gemfiles/30.gemfile +1 -1
- data/gemfiles/30.gemfile.lock +2 -2
- data/gemfiles/31.gemfile +1 -1
- data/gemfiles/31.gemfile.lock +2 -2
- data/gemfiles/32.gemfile +1 -1
- data/gemfiles/32.gemfile.lock +2 -2
- data/gemfiles/40.gemfile +12 -0
- data/gemfiles/40.gemfile.lock +80 -0
- data/lib/test_after_commit.rb +47 -46
- data/lib/test_after_commit/version.rb +1 -1
- data/spec/database.rb +35 -1
- data/spec/spec_helper.rb +16 -1
- data/spec/test_after_commit_spec.rb +51 -5
- metadata +7 -5
data/Appraisals
CHANGED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
data/Rakefile
CHANGED
@@ -6,7 +6,7 @@ task :spec do
|
|
6
6
|
end
|
7
7
|
|
8
8
|
task :default do
|
9
|
-
sh "bundle exec rake appraisal:install && bundle exec rake appraisal spec"
|
9
|
+
sh "bundle exec rake appraisal:install && bundle exec rake appraisal spec && REAL=1 bundle exec rake appraisal spec"
|
10
10
|
end
|
11
11
|
|
12
12
|
# extracted from https://github.com/grosser/project_template
|
data/Readme.md
CHANGED
@@ -8,12 +8,19 @@ Install
|
|
8
8
|
# Gemfile
|
9
9
|
gem 'test_after_commit', :group => :test
|
10
10
|
|
11
|
+
TIPS
|
12
|
+
====
|
13
|
+
- hooks do not re-raise errors (with or without this gem)
|
14
|
+
|
11
15
|
Author
|
12
16
|
======
|
13
17
|
|
14
18
|
Inspired by https://gist.github.com/1305285
|
15
19
|
|
20
|
+
### [Contributors](https://github.com/grosser/test_after_commit/contributors)
|
21
|
+
- [James Le Cuirot](https://github.com/chewi)
|
22
|
+
|
16
23
|
[Michael Grosser](http://grosser.it)<br/>
|
17
24
|
michael@grosser.it<br/>
|
18
25
|
License: MIT<br/>
|
19
|
-
[![Build Status](https://
|
26
|
+
[![Build Status](https://travis-ci.org/grosser/test_after_commit.png)](https://travis-ci.org/grosser/test_after_commit)
|
data/gemfiles/30.gemfile
CHANGED
data/gemfiles/30.gemfile.lock
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
PATH
|
2
2
|
remote: /Users/mgrosser/code/tools/test_after_commit
|
3
3
|
specs:
|
4
|
-
test_after_commit (0.0.
|
4
|
+
test_after_commit (0.0.1)
|
5
5
|
|
6
6
|
GEM
|
7
|
-
remote:
|
7
|
+
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
9
|
activemodel (3.0.16)
|
10
10
|
activesupport (= 3.0.16)
|
data/gemfiles/31.gemfile
CHANGED
data/gemfiles/31.gemfile.lock
CHANGED
data/gemfiles/32.gemfile
CHANGED
data/gemfiles/32.gemfile.lock
CHANGED
data/gemfiles/40.gemfile
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
PATH
|
2
|
+
remote: /Users/mgrosser/code/tools/test_after_commit
|
3
|
+
specs:
|
4
|
+
test_after_commit (0.0.1)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
actionpack (4.0.0.beta1)
|
10
|
+
activesupport (= 4.0.0.beta1)
|
11
|
+
builder (~> 3.1.0)
|
12
|
+
erubis (~> 2.7.0)
|
13
|
+
rack (~> 1.5.2)
|
14
|
+
rack-test (~> 0.6.2)
|
15
|
+
activemodel (4.0.0.beta1)
|
16
|
+
activesupport (= 4.0.0.beta1)
|
17
|
+
builder (~> 3.1.0)
|
18
|
+
activerecord (4.0.0.beta1)
|
19
|
+
activemodel (= 4.0.0.beta1)
|
20
|
+
activerecord-deprecated_finders (~> 0.0.3)
|
21
|
+
activesupport (= 4.0.0.beta1)
|
22
|
+
arel (~> 4.0.0.beta1)
|
23
|
+
activerecord-deprecated_finders (0.0.3)
|
24
|
+
activesupport (4.0.0.beta1)
|
25
|
+
i18n (~> 0.6.2)
|
26
|
+
minitest (~> 4.2)
|
27
|
+
multi_json (~> 1.3)
|
28
|
+
thread_safe (~> 0.1)
|
29
|
+
tzinfo (~> 0.3.33)
|
30
|
+
appraisal (0.5.1)
|
31
|
+
bundler
|
32
|
+
rake
|
33
|
+
arel (4.0.0.beta2)
|
34
|
+
atomic (1.0.1)
|
35
|
+
builder (3.1.4)
|
36
|
+
diff-lcs (1.2.1)
|
37
|
+
erubis (2.7.0)
|
38
|
+
i18n (0.6.4)
|
39
|
+
json (1.7.7)
|
40
|
+
minitest (4.6.2)
|
41
|
+
multi_json (1.6.1)
|
42
|
+
rack (1.5.2)
|
43
|
+
rack-test (0.6.2)
|
44
|
+
rack (>= 1.0)
|
45
|
+
rails-observers (0.1.1)
|
46
|
+
railties (~> 4.0.0.beta)
|
47
|
+
railties (4.0.0.beta1)
|
48
|
+
actionpack (= 4.0.0.beta1)
|
49
|
+
activesupport (= 4.0.0.beta1)
|
50
|
+
rake (>= 0.8.7)
|
51
|
+
rdoc (~> 3.4)
|
52
|
+
thor (>= 0.17.0, < 2.0)
|
53
|
+
rake (10.0.3)
|
54
|
+
rdoc (3.12.2)
|
55
|
+
json (~> 1.4)
|
56
|
+
rspec (2.13.0)
|
57
|
+
rspec-core (~> 2.13.0)
|
58
|
+
rspec-expectations (~> 2.13.0)
|
59
|
+
rspec-mocks (~> 2.13.0)
|
60
|
+
rspec-core (2.13.1)
|
61
|
+
rspec-expectations (2.13.0)
|
62
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
63
|
+
rspec-mocks (2.13.0)
|
64
|
+
sqlite3 (1.3.7)
|
65
|
+
thor (0.17.0)
|
66
|
+
thread_safe (0.1.0)
|
67
|
+
atomic
|
68
|
+
tzinfo (0.3.37)
|
69
|
+
|
70
|
+
PLATFORMS
|
71
|
+
ruby
|
72
|
+
|
73
|
+
DEPENDENCIES
|
74
|
+
activerecord (~> 4.0.0.beta1)
|
75
|
+
appraisal
|
76
|
+
rails-observers
|
77
|
+
rake
|
78
|
+
rspec (~> 2)
|
79
|
+
sqlite3
|
80
|
+
test_after_commit!
|
data/lib/test_after_commit.rb
CHANGED
@@ -3,63 +3,64 @@ require 'test_after_commit/version'
|
|
3
3
|
module TestAfterCommit
|
4
4
|
end
|
5
5
|
|
6
|
-
|
6
|
+
$PASS = 0
|
7
|
+
|
7
8
|
ActiveRecord::ConnectionAdapters::DatabaseStatements.class_eval do
|
8
|
-
#
|
9
|
-
# Run the normal transaction method; when it's done, check to see if there
|
10
|
-
# is exactly one open transaction. If so, that's the transactional
|
11
|
-
# fixtures transaction; from the model's standpoint, the completed
|
12
|
-
# transaction is the real deal. Send commit callbacks to models.
|
13
|
-
#
|
14
|
-
# If the transaction block raises a Rollback, we need to know, so we don't
|
15
|
-
# call the commit hooks. Other exceptions don't need to be explicitly
|
16
|
-
# accounted for since they will raise uncaught through this method and
|
17
|
-
# prevent the code after the hook from running.
|
18
|
-
#
|
19
9
|
def transaction_with_transactional_fixtures(*args)
|
20
|
-
|
21
|
-
rolled_back = false
|
22
|
-
|
10
|
+
@test_open_transactions ||= 0
|
23
11
|
transaction_without_transactional_fixtures(*args) do
|
24
12
|
begin
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
raise
|
13
|
+
@test_open_transactions += 1
|
14
|
+
yield
|
15
|
+
rescue ActiveRecord::Rollback => e
|
16
|
+
raise e
|
17
|
+
else
|
18
|
+
if @test_open_transactions == 2
|
19
|
+
test_commit_records
|
20
|
+
end
|
21
|
+
ensure
|
22
|
+
@test_open_transactions -= 1
|
29
23
|
end
|
30
24
|
end
|
31
|
-
|
32
|
-
commit_transaction_records(false) if not rolled_back and open_transactions == 1
|
33
|
-
|
34
|
-
return_value
|
35
25
|
end
|
36
26
|
alias_method_chain :transaction, :transactional_fixtures
|
37
27
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
# monkey-patch it to temporarily replace the array with only the records
|
46
|
-
# for the top-of-stack transaction, so the real
|
47
|
-
# `commit_transaction_records` method only sends callbacks to those.
|
48
|
-
#
|
49
|
-
def commit_transaction_records_with_transactional_fixtures(commit = true)
|
50
|
-
return commit_transaction_records_without_transactional_fixtures if commit
|
51
|
-
|
52
|
-
preserving_current_transaction_records do
|
53
|
-
@_current_transaction_records = @_current_transaction_records.pop || []
|
54
|
-
commit_transaction_records_without_transactional_fixtures
|
28
|
+
def test_commit_records
|
29
|
+
if ActiveRecord::VERSION::MAJOR == 3
|
30
|
+
commit_transaction_records(false)
|
31
|
+
else
|
32
|
+
@transaction.commit_records
|
33
|
+
@transaction.records.clear # prevent duplicate .commit!
|
34
|
+
@transaction.instance_variable_get(:@state).set_state(nil)
|
55
35
|
end
|
56
36
|
end
|
57
|
-
alias_method_chain :commit_transaction_records, :transactional_fixtures
|
58
37
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
38
|
+
if ActiveRecord::VERSION::MAJOR == 3
|
39
|
+
# The @_current_transaction_records is a stack of arrays, each one
|
40
|
+
# containing the records associated with the corresponding transaction
|
41
|
+
# in the transaction stack. This is used by the
|
42
|
+
# `rollback_transaction_records` method (to only send a rollback hook to
|
43
|
+
# models attached to the transaction being rolled back) but is usually
|
44
|
+
# ignored by the `commit_transaction_records` method. Here we
|
45
|
+
# monkey-patch it to temporarily replace the array with only the records
|
46
|
+
# for the top-of-stack transaction, so the real
|
47
|
+
# `commit_transaction_records` method only sends callbacks to those.
|
48
|
+
#
|
49
|
+
def commit_transaction_records_with_transactional_fixtures(commit = true)
|
50
|
+
return commit_transaction_records_without_transactional_fixtures if commit
|
51
|
+
|
52
|
+
preserving_current_transaction_records do
|
53
|
+
@_current_transaction_records = @_current_transaction_records.pop || []
|
54
|
+
commit_transaction_records_without_transactional_fixtures
|
55
|
+
end
|
56
|
+
end
|
57
|
+
alias_method_chain :commit_transaction_records, :transactional_fixtures
|
58
|
+
|
59
|
+
def preserving_current_transaction_records
|
60
|
+
old_current_transaction_records = @_current_transaction_records.dup
|
61
|
+
yield
|
62
|
+
ensure
|
63
|
+
@_current_transaction_records = old_current_transaction_records
|
64
|
+
end
|
64
65
|
end
|
65
66
|
end
|
data/spec/database.rb
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
# setup database
|
2
2
|
require 'active_record'
|
3
3
|
|
4
|
+
if ActiveRecord::VERSION::MAJOR > 3
|
5
|
+
require "rails/observers/activerecord/active_record"
|
6
|
+
end
|
7
|
+
|
4
8
|
ActiveRecord::Base.establish_connection(
|
5
9
|
:adapter => 'sqlite3',
|
6
10
|
:database => ':memory:'
|
@@ -10,17 +14,21 @@ ActiveRecord::Migration.verbose = false
|
|
10
14
|
|
11
15
|
ActiveRecord::Schema.define(:version => 1) do
|
12
16
|
create_table "cars", :force => true do |t|
|
17
|
+
t.integer :counter, :default => 0, :null => false
|
18
|
+
t.timestamps
|
13
19
|
end
|
14
20
|
end
|
15
21
|
|
16
22
|
class Car < ActiveRecord::Base
|
17
23
|
after_commit :simple_after_commit
|
18
24
|
after_commit :simple_after_commit_on_create, :on => :create
|
25
|
+
after_commit :save_once, :on => :create, :if => :do_after_create_save
|
19
26
|
after_commit :simple_after_commit_on_update, :on => :update
|
27
|
+
after_commit :maybe_raise_errors
|
20
28
|
|
21
29
|
after_save :trigger_rollback
|
22
30
|
|
23
|
-
attr_accessor :make_rollback
|
31
|
+
attr_accessor :make_rollback, :raise_error, :do_after_create_save
|
24
32
|
|
25
33
|
def self.called(x=nil)
|
26
34
|
@called ||= []
|
@@ -37,6 +45,18 @@ class Car < ActiveRecord::Base
|
|
37
45
|
|
38
46
|
private
|
39
47
|
|
48
|
+
def save_once
|
49
|
+
update_attributes(:counter => 3) unless counter == 3
|
50
|
+
self.class.called :save_once
|
51
|
+
end
|
52
|
+
|
53
|
+
def maybe_raise_errors
|
54
|
+
if raise_error
|
55
|
+
# puts "MAYBE RAISE" # just debugging, but it really does not work ...
|
56
|
+
raise "Expected error"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
40
60
|
def simple_after_commit
|
41
61
|
self.class.called :always
|
42
62
|
end
|
@@ -49,3 +69,17 @@ class Car < ActiveRecord::Base
|
|
49
69
|
self.class.called :update
|
50
70
|
end
|
51
71
|
end
|
72
|
+
|
73
|
+
class CarObserver < ActiveRecord::Observer
|
74
|
+
cattr_accessor :recording
|
75
|
+
|
76
|
+
[:after_commit, :after_rollback].each do |action|
|
77
|
+
define_method action do |record|
|
78
|
+
return unless recording
|
79
|
+
Car.called << :observed_after_commit
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
Car.observers = :car_observer
|
85
|
+
Car.instantiate_observers
|
data/spec/spec_helper.rb
CHANGED
@@ -1,3 +1,18 @@
|
|
1
1
|
$LOAD_PATH.unshift 'lib'
|
2
2
|
require File.expand_path '../database', __FILE__
|
3
|
-
|
3
|
+
|
4
|
+
if ENV['REAL']
|
5
|
+
puts 'using real transactions'
|
6
|
+
else
|
7
|
+
require 'test_after_commit'
|
8
|
+
end
|
9
|
+
|
10
|
+
RSpec.configure do |config|
|
11
|
+
unless ENV['REAL']
|
12
|
+
config.around do |example|
|
13
|
+
ActiveRecord::Base.transaction do # simulate transactional fixtures
|
14
|
+
example.call
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -1,11 +1,9 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe TestAfterCommit do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
example.call
|
8
|
-
end
|
4
|
+
before do
|
5
|
+
CarObserver.recording = false
|
6
|
+
Car.called.clear
|
9
7
|
end
|
10
8
|
|
11
9
|
it "has a VERSION" do
|
@@ -30,4 +28,52 @@ describe TestAfterCommit do
|
|
30
28
|
car.save.should == nil
|
31
29
|
Car.called.should == []
|
32
30
|
end
|
31
|
+
|
32
|
+
it "does not fire multiple times in nested transactions" do
|
33
|
+
Car.transaction do
|
34
|
+
Car.transaction do
|
35
|
+
Car.create!
|
36
|
+
Car.called.should == []
|
37
|
+
end
|
38
|
+
Car.called.should == []
|
39
|
+
end
|
40
|
+
Car.called.should == [:create, :always]
|
41
|
+
end
|
42
|
+
|
43
|
+
it "does not raises errors" do
|
44
|
+
car = Car.new
|
45
|
+
car.raise_error = true
|
46
|
+
car.save!
|
47
|
+
end
|
48
|
+
|
49
|
+
it "can do 1 save in after_commit" do
|
50
|
+
if !ENV['REAL']
|
51
|
+
pending "this results in infinite loop in REAL mode except on 4.0 but works in tests except for rails 3.0"
|
52
|
+
end
|
53
|
+
|
54
|
+
car = Car.new
|
55
|
+
car.do_after_create_save = true
|
56
|
+
car.save!
|
57
|
+
|
58
|
+
expected = if ActiveRecord::VERSION::MAJOR >= 4
|
59
|
+
[:update, :always, :save_once, :always] # some kind of loop prevention ... investigate we must
|
60
|
+
else
|
61
|
+
[:save_once, :create, :always, :save_once, :create, :always]
|
62
|
+
end
|
63
|
+
Car.called.should == expected
|
64
|
+
car.counter.should == 3
|
65
|
+
end
|
66
|
+
|
67
|
+
context "Observer" do
|
68
|
+
before do
|
69
|
+
CarObserver.recording = true
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should record commits" do
|
73
|
+
Car.transaction do
|
74
|
+
Car.create
|
75
|
+
end
|
76
|
+
Car.called.should == [:observed_after_commit, :create, :always]
|
77
|
+
end
|
78
|
+
end
|
33
79
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: test_after_commit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2013-03-16 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description:
|
15
15
|
email: michael@grosser.it
|
@@ -29,6 +29,8 @@ files:
|
|
29
29
|
- gemfiles/31.gemfile.lock
|
30
30
|
- gemfiles/32.gemfile
|
31
31
|
- gemfiles/32.gemfile.lock
|
32
|
+
- gemfiles/40.gemfile
|
33
|
+
- gemfiles/40.gemfile.lock
|
32
34
|
- lib/test_after_commit.rb
|
33
35
|
- lib/test_after_commit/version.rb
|
34
36
|
- spec/database.rb
|
@@ -50,7 +52,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
50
52
|
version: '0'
|
51
53
|
segments:
|
52
54
|
- 0
|
53
|
-
hash: -
|
55
|
+
hash: -3018637931883165367
|
54
56
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
57
|
none: false
|
56
58
|
requirements:
|
@@ -59,10 +61,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
59
61
|
version: '0'
|
60
62
|
segments:
|
61
63
|
- 0
|
62
|
-
hash: -
|
64
|
+
hash: -3018637931883165367
|
63
65
|
requirements: []
|
64
66
|
rubyforge_project:
|
65
|
-
rubygems_version: 1.8.
|
67
|
+
rubygems_version: 1.8.25
|
66
68
|
signing_key:
|
67
69
|
specification_version: 3
|
68
70
|
summary: makes after_commit callbacks testable in Rails 3+ with transactional_fixtures
|