transactor 0.0.1 → 0.0.2
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.
- 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
|