transactor 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/transactor/actor.rb +66 -0
- data/lib/transactor/errors.rb +30 -0
- data/lib/transactor/improv.rb +25 -0
- data/lib/transactor/performance.rb +85 -0
- data/lib/transactor/props.rb +6 -0
- data/lib/transactor/transaction.rb +105 -0
- data/lib/transactor/version.rb +1 -1
- data/lib/transactor.rb +21 -2
- data/spec/spec_helper.rb +1 -1
- data/spec/support/test_actors.rb +23 -0
- data/spec/transactor/actor_spec.rb +136 -0
- data/spec/transactor/improv_spec.rb +9 -0
- data/spec/transactor/performance_spec.rb +44 -0
- data/spec/transactor/transaction_spec.rb +155 -0
- data/spec/transactor_spec.rb +17 -0
- metadata +48 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2d8d6c6a970b35c85e5a2a609ac666587c91179c
|
4
|
+
data.tar.gz: 3065ef3b42767dff92f403034c3fa1b568e8cb86
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4dc2cde00f77e7742000a41d5f8b3487cde6fbfc039f554fc07abdb844a35a61d233d26b18d86c96cf5fd47703160ee10833599a5e6cf0332afb8b7d8f2c4c89
|
7
|
+
data.tar.gz: a739ebfc8a4751838aa3e31a04e5f28a66d4d92c614fe333dee4dabec830574e57283f8e992e8c618a0c0c9daca3e5de6c0e97f05f03e98a7fd954df9c183a74
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Transactor
|
2
|
+
class Actor
|
3
|
+
attr_reader :props
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def perform(*args, &block)
|
7
|
+
new(*args).perform(&block)
|
8
|
+
end
|
9
|
+
|
10
|
+
def rollback_on_failure!
|
11
|
+
@rollback_on_failure = true
|
12
|
+
end
|
13
|
+
|
14
|
+
def rollback_on_failure?
|
15
|
+
return Transactor.configuration.rollback_failed_actors if @rollback_on_failure.nil?
|
16
|
+
@rollback_on_failure
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def rollback_on_failure?
|
21
|
+
self.class.rollback_on_failure?
|
22
|
+
end
|
23
|
+
|
24
|
+
def perform(&block)
|
25
|
+
if block_given?
|
26
|
+
instance_eval &block
|
27
|
+
else
|
28
|
+
raise PerformNotImplemented, "#{self.class.name}##{__method__} has not been implemented"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def rollback(&block)
|
33
|
+
if block_given?
|
34
|
+
instance_eval &block
|
35
|
+
else
|
36
|
+
raise RollbackNotImplemented, "#{self.class.name}##{__method__} has not been implemented"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def state
|
41
|
+
props.to_h
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_s
|
45
|
+
"#{self.class.name} #{state.to_s}"
|
46
|
+
end
|
47
|
+
|
48
|
+
def method_missing(meth, *args, &block)
|
49
|
+
if props.respond_to?(meth)
|
50
|
+
props.send meth, *args, &block
|
51
|
+
else
|
52
|
+
super
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def respond_to_missing?(meth, include_private=false)
|
57
|
+
props.respond_to?(meth, include_private)
|
58
|
+
end
|
59
|
+
|
60
|
+
protected
|
61
|
+
|
62
|
+
def initialize(*args)
|
63
|
+
@props = Props.new(args.extract_options!)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Transactor
|
2
|
+
class ActorInvalid < ArgumentError; end
|
3
|
+
class PerformNotImplemented < NotImplementedError; end
|
4
|
+
class RollbackNotImplemented < NotImplementedError; end
|
5
|
+
|
6
|
+
class PerformanceBombed < StandardError
|
7
|
+
attr_reader :performance
|
8
|
+
|
9
|
+
def initialize(e, performance=nil)
|
10
|
+
super("#{e.class.name}: #{e.message} #{performance.to_s}")
|
11
|
+
set_backtrace e.backtrace
|
12
|
+
@performance = performance
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class RollbackBombed < PerformanceBombed; end
|
17
|
+
|
18
|
+
class TransactionError < StandardError
|
19
|
+
attr_reader :transaction
|
20
|
+
|
21
|
+
def initialize(e, transaction)
|
22
|
+
super("#{e.class.name}: #{e.message}")
|
23
|
+
set_backtrace e.backtrace
|
24
|
+
@transaction = transaction
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class TransactionFailed < TransactionError; end
|
29
|
+
class RollbackFailed < TransactionError; end
|
30
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Transactor
|
2
|
+
class Improv < Performance
|
3
|
+
def perform(&block)
|
4
|
+
if block_given?
|
5
|
+
super(&block)
|
6
|
+
else
|
7
|
+
super { nil }
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def rollback(&block)
|
12
|
+
if block_given?
|
13
|
+
super(&block)
|
14
|
+
else
|
15
|
+
super { nil }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
|
21
|
+
def initialize(*args)
|
22
|
+
super(Actor, *args)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module Transactor
|
2
|
+
class Performance
|
3
|
+
attr_reader :actor, :result
|
4
|
+
|
5
|
+
def self.perform(actor, *args, &block)
|
6
|
+
new(actor, *args).perform(&block)
|
7
|
+
end
|
8
|
+
|
9
|
+
def perform(&block)
|
10
|
+
@started = true
|
11
|
+
@result = actor.perform(&block)
|
12
|
+
@performed = true
|
13
|
+
self
|
14
|
+
end
|
15
|
+
|
16
|
+
def rollback(&block)
|
17
|
+
actor.rollback(&block)
|
18
|
+
@rolled_back = true
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def started?
|
23
|
+
!!@started
|
24
|
+
end
|
25
|
+
|
26
|
+
def performed?
|
27
|
+
!!@performed
|
28
|
+
end
|
29
|
+
|
30
|
+
def rolled_back?
|
31
|
+
!!@rolled_back
|
32
|
+
end
|
33
|
+
|
34
|
+
def performing?
|
35
|
+
started? && !performed?
|
36
|
+
end
|
37
|
+
|
38
|
+
def successful?
|
39
|
+
performed? && !rolled_back?
|
40
|
+
end
|
41
|
+
|
42
|
+
def failed?
|
43
|
+
started? && rolled_back?
|
44
|
+
end
|
45
|
+
|
46
|
+
def rollback_on_failure?
|
47
|
+
actor.rollback_on_failure?
|
48
|
+
end
|
49
|
+
|
50
|
+
def state
|
51
|
+
if failed?
|
52
|
+
:failed
|
53
|
+
elsif successful?
|
54
|
+
:successful
|
55
|
+
elsif performing?
|
56
|
+
:performing
|
57
|
+
elsif !started?
|
58
|
+
:not_started
|
59
|
+
else
|
60
|
+
:unknown
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def to_s
|
65
|
+
"#{actor.to_s} #{state}"
|
66
|
+
end
|
67
|
+
|
68
|
+
protected
|
69
|
+
|
70
|
+
def initialize(actor, *args)
|
71
|
+
actor = begin
|
72
|
+
if actor.is_a?(Array)
|
73
|
+
actor.map { |a| a.to_s.camelize }.join('::').constantize
|
74
|
+
elsif actor.is_a?(Symbol) || actor.is_a?(String)
|
75
|
+
actor.to_s.camelize.constantize
|
76
|
+
elsif actor.is_a?(Class) && actor <= Transactor::Actor
|
77
|
+
actor
|
78
|
+
else
|
79
|
+
raise ArgumentError, "An actor must be provided as a symbol, string, array or class (inherited from Transactor::Actor)"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
@actor = actor.new(*args)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module Transactor
|
2
|
+
class Transaction
|
3
|
+
attr_reader :performances, :result
|
4
|
+
|
5
|
+
def in_transaction(&block)
|
6
|
+
begin
|
7
|
+
@result = instance_eval &block if block_given?
|
8
|
+
rescue Exception => e # yes, we want to catch everything
|
9
|
+
begin
|
10
|
+
rollback
|
11
|
+
rescue Exception => e # yes, here too
|
12
|
+
raise RollbackFailed.new(e, self)
|
13
|
+
end
|
14
|
+
|
15
|
+
raise TransactionFailed.new(e, self)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def transaction!(&block)
|
20
|
+
if defined?(ActiveRecord::Base) && ActiveRecord::Base.respond_to?(:transaction)
|
21
|
+
ActiveRecord::Base.transaction { in_transaction &block }
|
22
|
+
else
|
23
|
+
in_transaction &block
|
24
|
+
end
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
28
|
+
def transaction(&block)
|
29
|
+
transaction!(&block)
|
30
|
+
rescue => e
|
31
|
+
false
|
32
|
+
end
|
33
|
+
|
34
|
+
def perform(actor, *args, &block)
|
35
|
+
performance = Performance.new(actor, *args)
|
36
|
+
performances << performance
|
37
|
+
performance.perform(&block)
|
38
|
+
performance.result
|
39
|
+
rescue => e
|
40
|
+
raise PerformanceBombed.new(e, performance)
|
41
|
+
end
|
42
|
+
|
43
|
+
def improvise(*args, &block)
|
44
|
+
performance = Improv.new(*args)
|
45
|
+
performances << performance
|
46
|
+
performance.perform(&block)
|
47
|
+
performance
|
48
|
+
rescue => e
|
49
|
+
raise PerformanceBombed.new(e, performance)
|
50
|
+
end
|
51
|
+
|
52
|
+
def rollback
|
53
|
+
return true if performances.empty?
|
54
|
+
|
55
|
+
if rollback_last_performance?
|
56
|
+
rollback_performances
|
57
|
+
else
|
58
|
+
bombed = performances.pop
|
59
|
+
rollback_performances
|
60
|
+
performances << bombed
|
61
|
+
end
|
62
|
+
|
63
|
+
true
|
64
|
+
end
|
65
|
+
|
66
|
+
def rollback_performances
|
67
|
+
performances.reverse.each do |performance|
|
68
|
+
begin
|
69
|
+
performance.rollback
|
70
|
+
rescue RollbackNotImplemented => e
|
71
|
+
# noop, rollback was not implemented
|
72
|
+
rescue => e
|
73
|
+
# there was an error rolling back the performance
|
74
|
+
# do we halt the transaction rollback here? return the performances and allow manual rollbacks?
|
75
|
+
raise RollbackBombed.new(e, performance)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def last_performance
|
81
|
+
performances.last
|
82
|
+
end
|
83
|
+
|
84
|
+
def rollback_last_performance?
|
85
|
+
# only rollback the last performance if it didn't bomb OR if
|
86
|
+
# it did and is configured to rollback on failure
|
87
|
+
!last_performance.failed? || (last_performance.failed? && last_performance.rollback_on_failure?)
|
88
|
+
end
|
89
|
+
|
90
|
+
def method_missing(meth, *args, &block)
|
91
|
+
perform meth, *args, &block
|
92
|
+
end
|
93
|
+
|
94
|
+
def respond_to_missing?(meth, include_private=false)
|
95
|
+
true # *shrug* we try to perform just about anything
|
96
|
+
end
|
97
|
+
|
98
|
+
protected
|
99
|
+
|
100
|
+
def initialize(*args, &block)
|
101
|
+
@performances = []
|
102
|
+
transaction! &block if block_given?
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
data/lib/transactor/version.rb
CHANGED
data/lib/transactor.rb
CHANGED
@@ -1,4 +1,23 @@
|
|
1
|
+
require 'active_support/core_ext/module/attribute_accessors'
|
2
|
+
require 'active_support/core_ext/string/inflections'
|
3
|
+
require 'active_support/core_ext/string/output_safety'
|
4
|
+
require 'canfig'
|
5
|
+
require 'transactor/version'
|
6
|
+
require 'transactor/errors'
|
7
|
+
require 'transactor/props'
|
8
|
+
require 'transactor/actor'
|
9
|
+
require 'transactor/performance'
|
10
|
+
require 'transactor/improv'
|
11
|
+
require 'transactor/transaction'
|
12
|
+
|
1
13
|
module Transactor
|
2
|
-
|
14
|
+
include Canfig::Module
|
3
15
|
|
4
|
-
|
16
|
+
configure do |config|
|
17
|
+
config.rollback_failed_actors = false
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.transaction(&block)
|
21
|
+
Transactor::Transaction.new &block
|
22
|
+
end
|
23
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -3,7 +3,7 @@ require 'rspec'
|
|
3
3
|
require 'coveralls'
|
4
4
|
Coveralls.wear!
|
5
5
|
|
6
|
-
|
6
|
+
Dir[File.join(File.dirname(__FILE__), '..', "spec/support/**/*.rb")].each { |f| require f }
|
7
7
|
|
8
8
|
RSpec.configure do |config|
|
9
9
|
#config.before(:suite) do
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class TestActor < Transactor::Actor
|
2
|
+
attr_reader :performed
|
3
|
+
|
4
|
+
def perform
|
5
|
+
@performed = true
|
6
|
+
end
|
7
|
+
|
8
|
+
def rollback
|
9
|
+
@performed = false
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class FailingActor < Transactor::Actor
|
14
|
+
attr_reader :rolled_back
|
15
|
+
|
16
|
+
def perform
|
17
|
+
raise "FAIL"
|
18
|
+
end
|
19
|
+
|
20
|
+
def rollback
|
21
|
+
@rolled_back = true
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Transactor::Actor do
|
4
|
+
subject { Transactor::Actor.new(test_one: 'one', test_two: 2) }
|
5
|
+
|
6
|
+
describe '#initialize' do
|
7
|
+
it 'converts options hash to a set of props' do
|
8
|
+
expect(subject.props).to respond_to(:test_one)
|
9
|
+
expect(subject.props.test_one).to eql('one')
|
10
|
+
expect(subject.props).to respond_to(:test_two)
|
11
|
+
expect(subject.props.test_two).to eql(2)
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'provides attribute readers for options hash keys' do
|
15
|
+
expect(subject).to respond_to(:test_one)
|
16
|
+
expect(subject).to respond_to(:test_two)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#perform' do
|
21
|
+
context 'when passed a block' do
|
22
|
+
it 'executes the block and returns the value' do
|
23
|
+
expect(subject.perform { 5+5 }).to eql(10)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'when not passed a block' do
|
28
|
+
it 'raises a PerformNotImplemented error' do
|
29
|
+
expect { subject.perform }.to raise_exception(Transactor::PerformNotImplemented)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe '#rollback' do
|
35
|
+
context 'when passed a block' do
|
36
|
+
it 'executes the block and returns the value' do
|
37
|
+
expect(subject.rollback { 5+5 }).to eql(10)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'when not passed a block' do
|
42
|
+
it 'raises a PerformNotImplemented error' do
|
43
|
+
expect { subject.rollback }.to raise_exception(Transactor::RollbackNotImplemented)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '#state' do
|
49
|
+
it 'converts props into a hash' do
|
50
|
+
expect(subject.state).to eql({test_one: 'one', test_two: 2})
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe '#to_s' do
|
55
|
+
it 'stringifies the class name and state' do
|
56
|
+
expect(subject.to_s).to eql("#{subject.class.name} #{subject.state.to_s}")
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe '.rollback_on_failure!' do
|
61
|
+
before(:each) { Transactor::Actor.remove_instance_variable :@rollback_on_failure rescue nil }
|
62
|
+
|
63
|
+
it 'sets the class instance variable to true' do
|
64
|
+
Transactor::Actor.rollback_on_failure!
|
65
|
+
expect(Transactor::Actor.instance_variable_get(:@rollback_on_failure)).to be_true
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe '.rollback_on_failure?' do
|
70
|
+
before(:each) { Transactor::Actor.remove_instance_variable :@rollback_on_failure rescue nil }
|
71
|
+
|
72
|
+
context 'when the actor is configured to rollback on failure' do
|
73
|
+
it 'returns true'do
|
74
|
+
Transactor::Actor.rollback_on_failure!
|
75
|
+
expect(Transactor::Actor.rollback_on_failure?).to be_true
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context 'when the actor is configured not to rollback on failure' do
|
80
|
+
it 'returns false' do
|
81
|
+
Transactor::Actor.rollback_on_failure!
|
82
|
+
expect(Transactor::Actor.rollback_on_failure?).to be_true
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'when the actor is not configured' do
|
87
|
+
context 'when Transactor is configured to rollback failed actors' do
|
88
|
+
it 'returns true' do
|
89
|
+
Transactor.configure { |config| config.rollback_failed_actors = true }
|
90
|
+
expect(Transactor::Actor.rollback_on_failure?).to be_true
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context 'when Transactor is configured not to rollback failed actors' do
|
95
|
+
it 'returns false' do
|
96
|
+
Transactor.configure { |config| config.rollback_failed_actors = false }
|
97
|
+
expect(Transactor::Actor.rollback_on_failure?).to be_false
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe '#rollback_on_failure?' do
|
104
|
+
before(:each) { Transactor::Actor.remove_instance_variable :@rollback_on_failure rescue nil }
|
105
|
+
|
106
|
+
context 'when the actor is configured to rollback on failure' do
|
107
|
+
it 'returns true'do
|
108
|
+
Transactor::Actor.rollback_on_failure!
|
109
|
+
expect(Transactor::Actor.new.rollback_on_failure?).to be_true
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
context 'when the actor is configured not to rollback on failure' do
|
114
|
+
it 'returns false' do
|
115
|
+
Transactor::Actor.rollback_on_failure!
|
116
|
+
expect(Transactor::Actor.new.rollback_on_failure?).to be_true
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context 'when the actor is not configured' do
|
121
|
+
context 'when Transactor is configured to rollback failed actors' do
|
122
|
+
it 'returns true' do
|
123
|
+
Transactor.configure { |config| config.rollback_failed_actors = true }
|
124
|
+
expect(Transactor::Actor.new.rollback_on_failure?).to be_true
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
context 'when Transactor is configured not to rollback failed actors' do
|
129
|
+
it 'returns false' do
|
130
|
+
Transactor.configure { |config| config.rollback_failed_actors = false }
|
131
|
+
expect(Transactor::Actor.new.rollback_on_failure?).to be_false
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Transactor::Performance do
|
4
|
+
subject { Transactor::Performance.new(TestActor) }
|
5
|
+
|
6
|
+
describe '#initialize' do
|
7
|
+
it 'accepts a class constant as an actor' do
|
8
|
+
expect(Transactor::Performance.new(TestActor).actor).to be_an_instance_of(TestActor)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'accepts a symbol as an actor' do
|
12
|
+
expect(Transactor::Performance.new(:test_actor).actor).to be_an_instance_of(TestActor)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'accepts a string as an actor' do
|
16
|
+
expect(Transactor::Performance.new('test_actor').actor).to be_an_instance_of(TestActor)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'accepts an array as an actor' do
|
20
|
+
expect(Transactor::Performance.new([:transactor, :actor]).actor).to be_an_instance_of(Transactor::Actor)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#perform' do
|
25
|
+
it "calls the actor's perform method" do
|
26
|
+
subject.perform
|
27
|
+
expect(subject.actor.performed).to be_true
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#rollback' do
|
32
|
+
it "calls the actor's rollback method" do
|
33
|
+
subject.perform
|
34
|
+
subject.rollback
|
35
|
+
expect(subject.actor.performed).to be_false
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe '#to_s' do
|
40
|
+
it 'stringifies the actor and state' do
|
41
|
+
expect(subject.to_s).to eql("#{subject.actor.to_s} #{subject.state.to_s}")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Transactor::Transaction do
|
4
|
+
describe '#transaction!' do
|
5
|
+
it 'returns self' do
|
6
|
+
expect(subject.transaction!).to eql(subject)
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'accepts a block' do
|
10
|
+
expect(subject.transaction! { 5+5 }).to eql(subject)
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'bubbles any raised exceptions' do
|
14
|
+
expect do
|
15
|
+
subject.transaction! do
|
16
|
+
raise "Transaction Error"
|
17
|
+
end
|
18
|
+
end.to raise_exception(Transactor::TransactionFailed)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#transaction' do
|
23
|
+
it 'returns self' do
|
24
|
+
expect(subject.transaction).to eql(subject)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'accepts a block' do
|
28
|
+
expect(subject.transaction { 5+5 }).to eql(subject)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'rescues any raised exceptions' do
|
32
|
+
expect do
|
33
|
+
subject.transaction do
|
34
|
+
raise "Transaction Error"
|
35
|
+
end
|
36
|
+
end.to_not raise_exception
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe '#result' do
|
41
|
+
it 'returns the result of the block' do
|
42
|
+
subject.transaction! { 5+5 }
|
43
|
+
expect(subject.result).to eql(10)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '#perform' do
|
48
|
+
it 'adds a performance to the performances array' do
|
49
|
+
subject.perform(TestActor)
|
50
|
+
expect(subject.performances.length).to eql(1)
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'casts the provided actor in the performance' do
|
54
|
+
subject.perform(TestActor)
|
55
|
+
expect(subject.performances.last.actor).to be_an_instance_of(TestActor)
|
56
|
+
end
|
57
|
+
|
58
|
+
it "triggers the actor's performance" do
|
59
|
+
subject.perform(TestActor)
|
60
|
+
expect(subject.performances.last.actor.performed).to be_true
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'bubbles exceptions as PerformanceBombed errors' do
|
64
|
+
expect { subject.perform(FailingActor) }.to raise_exception(Transactor::PerformanceBombed)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe '#improvise' do
|
69
|
+
it 'returns an improv performance' do
|
70
|
+
expect(subject.improvise).to be_an_instance_of(Transactor::Improv)
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'adds a performance to the performances array' do
|
74
|
+
subject.improvise
|
75
|
+
expect(subject.performances.length).to eql(1)
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'casts the default actor in the performance' do
|
79
|
+
subject.improvise
|
80
|
+
expect(subject.performances.last.actor).to be_an_instance_of(Transactor::Actor)
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'executes the improv block' do
|
84
|
+
subject.improvise { 5+5 }
|
85
|
+
expect(subject.performances.last.result).to eql(10)
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'bubbles exceptions as PerformanceBombed errors' do
|
89
|
+
expect do
|
90
|
+
subject.improvise do
|
91
|
+
raise "Bombing"
|
92
|
+
end
|
93
|
+
end.to raise_exception(Transactor::PerformanceBombed)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe '#rollback' do
|
98
|
+
context 'when there are no failed performances' do
|
99
|
+
it 'rolls back all performances' do
|
100
|
+
3.times { subject.perform(TestActor) }
|
101
|
+
subject.performances.each do |performance|
|
102
|
+
expect(performance.actor.performed).to be_true
|
103
|
+
end
|
104
|
+
subject.rollback
|
105
|
+
subject.performances.each do |performance|
|
106
|
+
expect(performance.actor.performed).to be_false
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
context 'when the last performance failed' do
|
112
|
+
context 'and is configured to rollback on failure' do
|
113
|
+
it 'rolls back all performances' do
|
114
|
+
3.times { subject.perform(TestActor) }
|
115
|
+
subject.performances.each do |performance|
|
116
|
+
expect(performance.actor.performed).to be_true
|
117
|
+
end
|
118
|
+
|
119
|
+
TestActor.instance_variable_set :@rollback_on_failure, true
|
120
|
+
subject.performances.last.instance_variable_set :@failed, true
|
121
|
+
subject.rollback
|
122
|
+
TestActor.instance_variable_set :@rollback_on_failure, false
|
123
|
+
|
124
|
+
subject.performances.each do |performance|
|
125
|
+
expect(performance.actor.performed).to be_false
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
context 'and is configured not to rollback on failure' do
|
131
|
+
it 'rolls back all performances except the one that failed' do
|
132
|
+
TestActor.instance_variable_set :@rollback_on_failure, false
|
133
|
+
3.times { subject.perform(TestActor) }
|
134
|
+
subject.performances.each do |performance|
|
135
|
+
expect(performance.actor.performed).to be_true
|
136
|
+
end
|
137
|
+
subject.performances.last.instance_variable_set :@failed, true
|
138
|
+
subject.rollback
|
139
|
+
subject.performances.each do |performance|
|
140
|
+
expect(performance.actor.performed).to be_false
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
describe '#method_missing' do
|
148
|
+
it 'translates method calls to actor performances' do
|
149
|
+
subject.transaction! do
|
150
|
+
test_actor
|
151
|
+
end
|
152
|
+
expect(subject.performances.last.actor).to be_an_instance_of(TestActor)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Transactor do
|
4
|
+
describe '#transaction' do
|
5
|
+
it 'returns a Transactor::Transaction' do
|
6
|
+
expect(Transactor.transaction).to be_an_instance_of(Transactor::Transaction)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
context 'configuration' do
|
11
|
+
describe 'rollback_failed_actors' do
|
12
|
+
it 'defaults to false' do
|
13
|
+
expect(Transactor.configuration.rollback_failed_actors).to be_false
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
metadata
CHANGED
@@ -1,15 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: transactor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mark Rebec
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-05-
|
11
|
+
date: 2015-05-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: canfig
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
13
41
|
- !ruby/object:Gem::Dependency
|
14
42
|
name: rake
|
15
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -46,8 +74,20 @@ extensions: []
|
|
46
74
|
extra_rdoc_files: []
|
47
75
|
files:
|
48
76
|
- lib/transactor.rb
|
77
|
+
- lib/transactor/actor.rb
|
78
|
+
- lib/transactor/errors.rb
|
79
|
+
- lib/transactor/improv.rb
|
80
|
+
- lib/transactor/performance.rb
|
81
|
+
- lib/transactor/props.rb
|
82
|
+
- lib/transactor/transaction.rb
|
49
83
|
- lib/transactor/version.rb
|
50
84
|
- spec/spec_helper.rb
|
85
|
+
- spec/support/test_actors.rb
|
86
|
+
- spec/transactor/actor_spec.rb
|
87
|
+
- spec/transactor/improv_spec.rb
|
88
|
+
- spec/transactor/performance_spec.rb
|
89
|
+
- spec/transactor/transaction_spec.rb
|
90
|
+
- spec/transactor_spec.rb
|
51
91
|
homepage: http://github.com/markrebec/transactor
|
52
92
|
licenses: []
|
53
93
|
metadata: {}
|
@@ -73,3 +113,9 @@ specification_version: 4
|
|
73
113
|
summary: Transactional actors for easy rollbacks
|
74
114
|
test_files:
|
75
115
|
- spec/spec_helper.rb
|
116
|
+
- spec/support/test_actors.rb
|
117
|
+
- spec/transactor/actor_spec.rb
|
118
|
+
- spec/transactor/improv_spec.rb
|
119
|
+
- spec/transactor/performance_spec.rb
|
120
|
+
- spec/transactor/transaction_spec.rb
|
121
|
+
- spec/transactor_spec.rb
|