statesman 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +7 -1
- data/.travis.yml +19 -4
- data/CHANGELOG.md +30 -0
- data/README.md +11 -35
- data/lib/generators/statesman/add_constraints_to_most_recent_generator.rb +28 -0
- data/lib/generators/statesman/add_most_recent_generator.rb +25 -0
- data/lib/generators/statesman/generator_helpers.rb +6 -2
- data/lib/generators/statesman/templates/add_constraints_to_most_recent_migration.rb.erb +13 -0
- data/lib/generators/statesman/templates/add_most_recent_migration.rb.erb +9 -0
- data/lib/generators/statesman/templates/create_migration.rb.erb +4 -3
- data/lib/generators/statesman/templates/update_migration.rb.erb +5 -4
- data/lib/statesman.rb +1 -0
- data/lib/statesman/adapters/active_record.rb +23 -8
- data/lib/statesman/adapters/active_record_queries.rb +62 -15
- data/lib/statesman/railtie.rb +9 -0
- data/lib/statesman/version.rb +1 -1
- data/lib/tasks/statesman.rake +49 -0
- data/spec/fixtures/add_constraints_to_most_recent_for_bacon_transitions.rb +13 -0
- data/spec/fixtures/add_most_recent_to_bacon_transitions.rb +9 -0
- data/spec/generators/statesman/active_record_transition_generator_spec.rb +0 -2
- data/spec/generators/statesman/add_constraints_to_most_recent_generator_spec.rb +38 -0
- data/spec/generators/statesman/add_most_recent_generator_spec.rb +35 -0
- data/spec/generators/statesman/migration_generator_spec.rb +10 -1
- data/spec/generators/statesman/mongoid_transition_generator_spec.rb +0 -2
- data/spec/spec_helper.rb +22 -7
- data/spec/statesman/adapters/active_record_queries_spec.rb +110 -28
- data/spec/statesman/adapters/active_record_spec.rb +61 -31
- data/spec/statesman/adapters/mongoid_spec.rb +8 -17
- data/spec/statesman/adapters/shared_examples.rb +10 -17
- data/spec/statesman/callback_spec.rb +2 -6
- data/spec/statesman/config_spec.rb +2 -5
- data/spec/statesman/guard_spec.rb +3 -9
- data/spec/statesman/machine_spec.rb +91 -129
- data/spec/support/active_record.rb +35 -4
- data/spec/support/generators_shared_examples.rb +1 -4
- data/statesman.gemspec +5 -3
- metadata +52 -10
@@ -2,18 +2,14 @@ require "spec_helper"
|
|
2
2
|
require "statesman/adapters/shared_examples"
|
3
3
|
require "statesman/exceptions"
|
4
4
|
|
5
|
-
describe Statesman::Adapters::ActiveRecord do
|
5
|
+
describe Statesman::Adapters::ActiveRecord, active_record: true do
|
6
6
|
before do
|
7
7
|
prepare_model_table
|
8
8
|
prepare_transitions_table
|
9
9
|
end
|
10
10
|
|
11
11
|
before { MyActiveRecordModelTransition.serialize(:metadata, JSON) }
|
12
|
-
let(:observer)
|
13
|
-
result = double(Statesman::Machine)
|
14
|
-
allow(result).to receive(:execute)
|
15
|
-
result
|
16
|
-
end
|
12
|
+
let(:observer) { double(Statesman::Machine, execute: nil) }
|
17
13
|
let(:model) { MyActiveRecordModel.create(current_state: :pending) }
|
18
14
|
it_behaves_like "an adapter", described_class, MyActiveRecordModelTransition
|
19
15
|
|
@@ -24,11 +20,12 @@ describe Statesman::Adapters::ActiveRecord do
|
|
24
20
|
allow(metadata_column).to receive_messages(sql_type: '')
|
25
21
|
allow(MyActiveRecordModelTransition).to receive_messages(columns_hash:
|
26
22
|
{ 'metadata' => metadata_column })
|
27
|
-
if ::ActiveRecord.gem_version
|
23
|
+
if ::ActiveRecord.respond_to?(:gem_version) &&
|
24
|
+
::ActiveRecord.gem_version >= Gem::Version.new('4.2.0.a')
|
28
25
|
allow(metadata_column).to receive_messages(cast_type: '')
|
29
26
|
else
|
30
|
-
allow(MyActiveRecordModelTransition)
|
31
|
-
|
27
|
+
allow(MyActiveRecordModelTransition).
|
28
|
+
to receive_messages(serialized_attributes: {})
|
32
29
|
end
|
33
30
|
end
|
34
31
|
|
@@ -46,16 +43,17 @@ describe Statesman::Adapters::ActiveRecord do
|
|
46
43
|
allow(metadata_column).to receive_messages(sql_type: 'json')
|
47
44
|
allow(MyActiveRecordModelTransition).to receive_messages(columns_hash:
|
48
45
|
{ 'metadata' => metadata_column })
|
49
|
-
if ::ActiveRecord.gem_version
|
46
|
+
if ::ActiveRecord.respond_to?(:gem_version) &&
|
47
|
+
::ActiveRecord.gem_version >= Gem::Version.new('4.2.0.a')
|
50
48
|
serialized_type = ::ActiveRecord::Type::Serialized.new(
|
51
49
|
'', ::ActiveRecord::Coders::JSON
|
52
50
|
)
|
53
|
-
expect(metadata_column)
|
54
|
-
|
55
|
-
|
51
|
+
expect(metadata_column).
|
52
|
+
to receive(:cast_type).
|
53
|
+
and_return(serialized_type)
|
56
54
|
else
|
57
|
-
expect(MyActiveRecordModelTransition)
|
58
|
-
|
55
|
+
expect(MyActiveRecordModelTransition).
|
56
|
+
to receive_messages(serialized_attributes: { 'metadata' => '' })
|
59
57
|
end
|
60
58
|
end
|
61
59
|
|
@@ -83,15 +81,15 @@ describe Statesman::Adapters::ActiveRecord do
|
|
83
81
|
adapter2.create(:x, :y)
|
84
82
|
adapter.last
|
85
83
|
adapter2.create(:y, :z)
|
86
|
-
expect { adapter.create(:y, :z) }
|
87
|
-
|
84
|
+
expect { adapter.create(:y, :z) }.
|
85
|
+
to raise_exception(Statesman::TransitionConflictError)
|
88
86
|
end
|
89
87
|
end
|
90
88
|
|
91
89
|
context "when other exceptions occur" do
|
92
90
|
before do
|
93
|
-
allow_any_instance_of(MyActiveRecordModelTransition)
|
94
|
-
|
91
|
+
allow_any_instance_of(MyActiveRecordModelTransition).
|
92
|
+
to receive(:save!).and_raise(error)
|
95
93
|
end
|
96
94
|
|
97
95
|
context "ActiveRecord::RecordNotUnique unrelated to this transition" do
|
@@ -104,6 +102,44 @@ describe Statesman::Adapters::ActiveRecord do
|
|
104
102
|
it { is_expected.to raise_exception(StandardError) }
|
105
103
|
end
|
106
104
|
end
|
105
|
+
|
106
|
+
context "when the transition_class has a most_recent column" do
|
107
|
+
subject { create }
|
108
|
+
|
109
|
+
context "with no previous transition" do
|
110
|
+
its(:most_recent) { is_expected.to eq(true) }
|
111
|
+
end
|
112
|
+
|
113
|
+
context "with a previous transition" do
|
114
|
+
let!(:previous_transition) { adapter.create(from, to) }
|
115
|
+
its(:most_recent) { is_expected.to eq(true) }
|
116
|
+
|
117
|
+
it "updates the previous transition's most_recent flag" do
|
118
|
+
expect { create }.
|
119
|
+
to change { previous_transition.reload.most_recent }.
|
120
|
+
from(true).to(false)
|
121
|
+
end
|
122
|
+
|
123
|
+
context "and the parent model is updated in a callback" do
|
124
|
+
before do
|
125
|
+
allow(observer).to receive(:execute) do |phase|
|
126
|
+
if phase == :before
|
127
|
+
model.update_attributes!(current_state: :ready)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
it "doesn't save the transition too early" do
|
133
|
+
expect { create }.to_not raise_exception
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
context "when the transition_class doesn't have a most_recent column" do
|
140
|
+
before { drop_most_recent_column }
|
141
|
+
it { is_expected.to_not raise_exception }
|
142
|
+
end
|
107
143
|
end
|
108
144
|
|
109
145
|
describe "#last" do
|
@@ -111,18 +147,14 @@ describe Statesman::Adapters::ActiveRecord do
|
|
111
147
|
described_class.new(MyActiveRecordModelTransition, model, observer)
|
112
148
|
end
|
113
149
|
|
114
|
-
before
|
115
|
-
adapter.create(:x, :y)
|
116
|
-
end
|
150
|
+
before { adapter.create(:x, :y) }
|
117
151
|
|
118
152
|
context "with a previously looked up transition" do
|
119
|
-
before
|
120
|
-
adapter.last
|
121
|
-
end
|
153
|
+
before { adapter.last }
|
122
154
|
|
123
155
|
it "caches the transition" do
|
124
|
-
expect_any_instance_of(MyActiveRecordModel)
|
125
|
-
|
156
|
+
expect_any_instance_of(MyActiveRecordModel).
|
157
|
+
to receive(:my_active_record_model_transitions).never
|
126
158
|
adapter.last
|
127
159
|
end
|
128
160
|
|
@@ -135,10 +167,8 @@ describe Statesman::Adapters::ActiveRecord do
|
|
135
167
|
end
|
136
168
|
|
137
169
|
context "with a pre-fetched transition history" do
|
138
|
-
before
|
139
|
-
|
140
|
-
model.my_active_record_model_transitions.load_target
|
141
|
-
end
|
170
|
+
before { adapter.create(:x, :y) }
|
171
|
+
before { model.my_active_record_model_transitions.load_target }
|
142
172
|
|
143
173
|
it "doesn't query the database" do
|
144
174
|
expect(MyActiveRecordModelTransition).not_to receive(:connection)
|
@@ -5,23 +5,16 @@ require "support/mongoid"
|
|
5
5
|
require "mongoid"
|
6
6
|
|
7
7
|
describe Statesman::Adapters::Mongoid, mongo: true do
|
8
|
-
|
9
|
-
|
10
|
-
Mongoid.purge!
|
11
|
-
end
|
12
|
-
let(:observer) do
|
13
|
-
result = double(Statesman::Machine)
|
14
|
-
allow(result).to receive(:execute)
|
15
|
-
result
|
16
|
-
end
|
8
|
+
after { Mongoid.purge! }
|
9
|
+
let(:observer) { double(Statesman::Machine, execute: nil) }
|
17
10
|
let(:model) { MyMongoidModel.create(current_state: :pending) }
|
18
11
|
it_behaves_like "an adapter", described_class, MyMongoidModelTransition
|
19
12
|
|
20
13
|
describe "#initialize" do
|
21
14
|
context "with unserialized metadata" do
|
22
15
|
before do
|
23
|
-
allow_any_instance_of(described_class)
|
24
|
-
|
16
|
+
allow_any_instance_of(described_class).
|
17
|
+
to receive_messages(transition_class_hash_fields: [])
|
25
18
|
end
|
26
19
|
|
27
20
|
it "raises an exception if metadata is not serialized" do
|
@@ -39,14 +32,12 @@ describe Statesman::Adapters::Mongoid, mongo: true do
|
|
39
32
|
end
|
40
33
|
|
41
34
|
context "with a previously looked up transition" do
|
42
|
-
before
|
43
|
-
|
44
|
-
adapter.last
|
45
|
-
end
|
35
|
+
before { adapter.create(:x, :y) }
|
36
|
+
before { adapter.last }
|
46
37
|
|
47
38
|
it "caches the transition" do
|
48
|
-
expect_any_instance_of(MyMongoidModel)
|
49
|
-
|
39
|
+
expect_any_instance_of(MyMongoidModel).
|
40
|
+
to receive(:my_mongoid_model_transitions).never
|
50
41
|
adapter.last
|
51
42
|
end
|
52
43
|
|
@@ -12,11 +12,7 @@ require "spec_helper"
|
|
12
12
|
# last: Returns the latest transition history item
|
13
13
|
#
|
14
14
|
shared_examples_for "an adapter" do |adapter_class, transition_class|
|
15
|
-
let(:observer)
|
16
|
-
result = double(Statesman::Machine)
|
17
|
-
allow(result).to receive(:execute)
|
18
|
-
result
|
19
|
-
end
|
15
|
+
let(:observer) { double(Statesman::Machine, execute: nil) }
|
20
16
|
let(:adapter) { adapter_class.new(transition_class, model, observer) }
|
21
17
|
|
22
18
|
describe "#initialize" do
|
@@ -55,8 +51,8 @@ shared_examples_for "an adapter" do |adapter_class, transition_class|
|
|
55
51
|
|
56
52
|
context "with before callbacks" do
|
57
53
|
it "is called before the state transition" do
|
58
|
-
expect(observer).to receive(:execute)
|
59
|
-
|
54
|
+
expect(observer).to receive(:execute).
|
55
|
+
with(:before, anything, anything, anything) {
|
60
56
|
expect(adapter.history.length).to eq(0)
|
61
57
|
}.once
|
62
58
|
adapter.create(from, to)
|
@@ -66,9 +62,8 @@ shared_examples_for "an adapter" do |adapter_class, transition_class|
|
|
66
62
|
|
67
63
|
context "with after callbacks" do
|
68
64
|
it "is called after the state transition" do
|
69
|
-
expect(observer).to receive(:execute)
|
70
|
-
|
71
|
-
|_phase, _from_state, _to_state, transition|
|
65
|
+
expect(observer).to receive(:execute).
|
66
|
+
with(:after, anything, anything, anything) { |_, _, _, transition|
|
72
67
|
expect(adapter.last).to eq(transition)
|
73
68
|
}.once
|
74
69
|
adapter.create(from, to)
|
@@ -77,9 +72,8 @@ shared_examples_for "an adapter" do |adapter_class, transition_class|
|
|
77
72
|
it "exposes the new transition for subsequent transitions" do
|
78
73
|
adapter.create(from, to)
|
79
74
|
|
80
|
-
expect(observer).to receive(:execute)
|
81
|
-
|
82
|
-
|_phase, _from_state, _to_state, transition|
|
75
|
+
expect(observer).to receive(:execute).
|
76
|
+
with(:after, anything, anything, anything) { |_, _, _, transition|
|
83
77
|
expect(adapter.last).to eq(transition)
|
84
78
|
}.once
|
85
79
|
adapter.create(to, there)
|
@@ -104,16 +98,15 @@ shared_examples_for "an adapter" do |adapter_class, transition_class|
|
|
104
98
|
context "sorting" do
|
105
99
|
let!(:transition2) { adapter.create(:x, :y) }
|
106
100
|
subject { adapter.history }
|
101
|
+
|
107
102
|
it { is_expected.to eq(adapter.history.sort_by(&:sort_key)) }
|
108
103
|
end
|
109
104
|
end
|
110
105
|
end
|
111
106
|
|
112
107
|
describe "#last" do
|
113
|
-
before
|
114
|
-
|
115
|
-
adapter.create(:y, :z)
|
116
|
-
end
|
108
|
+
before { adapter.create(:x, :y) }
|
109
|
+
before { adapter.create(:y, :z) }
|
117
110
|
subject { adapter.last }
|
118
111
|
|
119
112
|
it { is_expected.to be_a(transition_class) }
|
@@ -61,9 +61,7 @@ describe Statesman::Callback do
|
|
61
61
|
end
|
62
62
|
|
63
63
|
context "with any to and any from value on the callback" do
|
64
|
-
let(:callback)
|
65
|
-
Statesman::Callback.new(callback: cb_lambda)
|
66
|
-
end
|
64
|
+
let(:callback) { Statesman::Callback.new(callback: cb_lambda) }
|
67
65
|
let(:from) { :x }
|
68
66
|
let(:to) { :y }
|
69
67
|
|
@@ -93,9 +91,7 @@ describe Statesman::Callback do
|
|
93
91
|
end
|
94
92
|
|
95
93
|
context "with any to value on the callback" do
|
96
|
-
let(:callback)
|
97
|
-
Statesman::Callback.new(from: :x, callback: cb_lambda)
|
98
|
-
end
|
94
|
+
let(:callback) { Statesman::Callback.new(from: :x, callback: cb_lambda) }
|
99
95
|
let(:to) { :y }
|
100
96
|
|
101
97
|
context "and an allowed to value" do
|
@@ -2,16 +2,13 @@ require "spec_helper"
|
|
2
2
|
|
3
3
|
describe Statesman::Config do
|
4
4
|
let(:instance) { Statesman::Config.new }
|
5
|
-
|
6
|
-
after do
|
7
|
-
# Don't leak global config changes into other specs
|
8
|
-
Statesman.configure { storage_adapter(Statesman::Adapters::Memory) }
|
9
|
-
end
|
5
|
+
after { Statesman.configure { storage_adapter(Statesman::Adapters::Memory) } }
|
10
6
|
|
11
7
|
describe "#storage_adapter" do
|
12
8
|
let(:adapter) { Class.new }
|
13
9
|
before { instance.storage_adapter(adapter) }
|
14
10
|
subject { instance.adapter_class }
|
11
|
+
|
15
12
|
it { is_expected.to be(adapter) }
|
16
13
|
|
17
14
|
it "is DSL configurable" do
|
@@ -7,22 +7,16 @@ describe Statesman::Guard do
|
|
7
7
|
specify { expect(guard).to be_a(Statesman::Callback) }
|
8
8
|
|
9
9
|
describe "#call" do
|
10
|
-
subject { guard.call }
|
10
|
+
subject(:call) { guard.call }
|
11
11
|
|
12
12
|
context "success" do
|
13
13
|
let(:callback) { -> { true } }
|
14
|
-
|
15
|
-
it "does not raise an error" do
|
16
|
-
expect { guard.call }.to_not raise_error
|
17
|
-
end
|
14
|
+
specify { expect { call }.to_not raise_error }
|
18
15
|
end
|
19
16
|
|
20
17
|
context "error" do
|
21
18
|
let(:callback) { -> { false } }
|
22
|
-
|
23
|
-
it "raises an error" do
|
24
|
-
expect { guard.call }.to raise_error(Statesman::GuardFailedError)
|
25
|
-
end
|
19
|
+
specify { expect { call }.to raise_error(Statesman::GuardFailedError) }
|
26
20
|
end
|
27
21
|
end
|
28
22
|
end
|
@@ -15,9 +15,8 @@ describe Statesman::Machine do
|
|
15
15
|
|
16
16
|
context "when an initial state is already defined" do
|
17
17
|
it "raises an error" do
|
18
|
-
expect
|
19
|
-
|
20
|
-
end.to raise_error(Statesman::InvalidStateError)
|
18
|
+
expect { machine.state(:y, initial: true) }.
|
19
|
+
to raise_error(Statesman::InvalidStateError)
|
21
20
|
end
|
22
21
|
end
|
23
22
|
end
|
@@ -51,15 +50,15 @@ describe Statesman::Machine do
|
|
51
50
|
|
52
51
|
context "when an irrelevant exception occurs" do
|
53
52
|
it "runs the transition once" do
|
54
|
-
expect(instance)
|
55
|
-
|
56
|
-
|
53
|
+
expect(instance).
|
54
|
+
to receive(:transition_to).once.
|
55
|
+
and_raise(StandardError)
|
57
56
|
transition_state rescue nil # rubocop:disable RescueModifier
|
58
57
|
end
|
59
58
|
|
60
59
|
it "re-raises the exception" do
|
61
|
-
allow(instance).to receive(:transition_to).once
|
62
|
-
|
60
|
+
allow(instance).to receive(:transition_to).once.
|
61
|
+
and_raise(StandardError)
|
63
62
|
expect { transition_state }.to raise_error(StandardError)
|
64
63
|
end
|
65
64
|
end
|
@@ -67,31 +66,31 @@ describe Statesman::Machine do
|
|
67
66
|
context "when a TransitionConflictError occurs" do
|
68
67
|
context "and is resolved on the second attempt" do
|
69
68
|
it "runs the transition twice" do
|
70
|
-
expect(instance)
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
expect(instance)
|
75
|
-
|
69
|
+
expect(instance).
|
70
|
+
to receive(:transition_to).once.
|
71
|
+
and_raise(Statesman::TransitionConflictError).
|
72
|
+
ordered
|
73
|
+
expect(instance).
|
74
|
+
to receive(:transition_to).once.ordered.and_call_original
|
76
75
|
transition_state
|
77
76
|
end
|
78
77
|
end
|
79
78
|
|
80
79
|
context "and keeps occurring" do
|
81
80
|
it "runs the transition `retry_attempts + 1` times" do
|
82
|
-
expect(instance)
|
83
|
-
|
84
|
-
|
85
|
-
|
81
|
+
expect(instance).
|
82
|
+
to receive(:transition_to).
|
83
|
+
exactly(retry_attempts + 1).times.
|
84
|
+
and_raise(Statesman::TransitionConflictError)
|
86
85
|
transition_state rescue nil # rubocop:disable RescueModifier
|
87
86
|
end
|
88
87
|
|
89
88
|
it "re-raises the conflict" do
|
90
|
-
allow(instance)
|
91
|
-
|
92
|
-
|
93
|
-
expect { transition_state }
|
94
|
-
|
89
|
+
allow(instance).
|
90
|
+
to receive(:transition_to).
|
91
|
+
and_raise(Statesman::TransitionConflictError)
|
92
|
+
expect { transition_state }.
|
93
|
+
to raise_error(Statesman::TransitionConflictError)
|
95
94
|
end
|
96
95
|
end
|
97
96
|
end
|
@@ -108,49 +107,43 @@ describe Statesman::Machine do
|
|
108
107
|
|
109
108
|
context "given neither a 'from' nor a 'to' state" do
|
110
109
|
it "raises an error" do
|
111
|
-
expect
|
112
|
-
|
113
|
-
end.to raise_error(Statesman::InvalidStateError)
|
110
|
+
expect { machine.transition }.
|
111
|
+
to raise_error(Statesman::InvalidStateError)
|
114
112
|
end
|
115
113
|
end
|
116
114
|
|
117
115
|
context "given no 'from' state and a valid 'to' state" do
|
118
116
|
it "raises an error" do
|
119
|
-
expect
|
120
|
-
|
121
|
-
end.to raise_error(Statesman::InvalidStateError)
|
117
|
+
expect { machine.transition from: nil, to: :x }.
|
118
|
+
to raise_error(Statesman::InvalidStateError)
|
122
119
|
end
|
123
120
|
end
|
124
121
|
|
125
122
|
context "given a valid 'from' state and a no 'to' state" do
|
126
123
|
it "raises an error" do
|
127
|
-
expect
|
128
|
-
|
129
|
-
end.to raise_error(Statesman::InvalidStateError)
|
124
|
+
expect { machine.transition from: :x, to: nil }.
|
125
|
+
to raise_error(Statesman::InvalidStateError)
|
130
126
|
end
|
131
127
|
end
|
132
128
|
|
133
129
|
context "given a valid 'from' state and an empty 'to' state array" do
|
134
130
|
it "raises an error" do
|
135
|
-
expect
|
136
|
-
|
137
|
-
end.to raise_error(Statesman::InvalidStateError)
|
131
|
+
expect { machine.transition from: :x, to: [] }.
|
132
|
+
to raise_error(Statesman::InvalidStateError)
|
138
133
|
end
|
139
134
|
end
|
140
135
|
|
141
136
|
context "given an invalid 'from' state" do
|
142
137
|
it "raises an error" do
|
143
|
-
expect
|
144
|
-
|
145
|
-
end.to raise_error(Statesman::InvalidStateError)
|
138
|
+
expect { machine.transition(from: :a, to: :x) }.
|
139
|
+
to raise_error(Statesman::InvalidStateError)
|
146
140
|
end
|
147
141
|
end
|
148
142
|
|
149
143
|
context "given an invalid 'to' state" do
|
150
144
|
it "raises an error" do
|
151
|
-
expect
|
152
|
-
|
153
|
-
end.to raise_error(Statesman::InvalidStateError)
|
145
|
+
expect { machine.transition(from: :x, to: :a) }.
|
146
|
+
to raise_error(Statesman::InvalidStateError)
|
154
147
|
end
|
155
148
|
end
|
156
149
|
|
@@ -176,25 +169,22 @@ describe Statesman::Machine do
|
|
176
169
|
|
177
170
|
context "with a terminal 'from' state" do
|
178
171
|
it "raises an exception" do
|
179
|
-
expect
|
180
|
-
|
181
|
-
end.to raise_error(Statesman::InvalidTransitionError)
|
172
|
+
expect { machine.validate_callback_condition(from: :z, to: :y) }.
|
173
|
+
to raise_error(Statesman::InvalidTransitionError)
|
182
174
|
end
|
183
175
|
end
|
184
176
|
|
185
177
|
context "with an initial 'to' state" do
|
186
178
|
it "raises an exception" do
|
187
|
-
expect
|
188
|
-
|
189
|
-
end.to raise_error(Statesman::InvalidTransitionError)
|
179
|
+
expect { machine.validate_callback_condition(from: :y, to: :x) }.
|
180
|
+
to raise_error(Statesman::InvalidTransitionError)
|
190
181
|
end
|
191
182
|
end
|
192
183
|
|
193
184
|
context "with an invalid transition" do
|
194
185
|
it "raises an exception" do
|
195
|
-
expect
|
196
|
-
|
197
|
-
end.to raise_error(Statesman::InvalidTransitionError)
|
186
|
+
expect { machine.validate_callback_condition(from: :x, to: :z) }.
|
187
|
+
to raise_error(Statesman::InvalidTransitionError)
|
198
188
|
end
|
199
189
|
end
|
200
190
|
|
@@ -206,9 +196,8 @@ describe Statesman::Machine do
|
|
206
196
|
|
207
197
|
context "with a valid transition" do
|
208
198
|
it "does not raise an exception" do
|
209
|
-
expect
|
210
|
-
|
211
|
-
end.to_not raise_error
|
199
|
+
expect { machine.validate_callback_condition(from: :x, to: :y) }.
|
200
|
+
to_not raise_error
|
212
201
|
end
|
213
202
|
end
|
214
203
|
end
|
@@ -227,9 +216,7 @@ describe Statesman::Machine do
|
|
227
216
|
let(:set_callback) { machine.send(assignment_method, options) {} }
|
228
217
|
|
229
218
|
shared_examples "fails" do |error_type|
|
230
|
-
|
231
|
-
expect { set_callback }.to raise_error(error_type)
|
232
|
-
end
|
219
|
+
specify { expect { set_callback }.to raise_error(error_type) }
|
233
220
|
|
234
221
|
it "does not add a callback" do
|
235
222
|
expect do
|
@@ -243,13 +230,11 @@ describe Statesman::Machine do
|
|
243
230
|
end
|
244
231
|
|
245
232
|
shared_examples "adds callback" do
|
246
|
-
|
247
|
-
expect { set_callback }.to_not raise_error
|
248
|
-
end
|
233
|
+
specify { expect { set_callback }.to_not raise_error }
|
249
234
|
|
250
235
|
it "stores callbacks" do
|
251
|
-
expect { set_callback }.
|
252
|
-
machine.callbacks[callback_store], :count).by(1)
|
236
|
+
expect { set_callback }.
|
237
|
+
to change(machine.callbacks[callback_store], :count).by(1)
|
253
238
|
end
|
254
239
|
|
255
240
|
it "stores callback instances" do
|
@@ -330,22 +315,22 @@ describe Statesman::Machine do
|
|
330
315
|
|
331
316
|
context "transition class" do
|
332
317
|
it "sets a default" do
|
333
|
-
expect(Statesman.storage_adapter).to receive(:new).once
|
334
|
-
|
318
|
+
expect(Statesman.storage_adapter).to receive(:new).once.
|
319
|
+
with(Statesman::Adapters::MemoryTransition, my_model, anything)
|
335
320
|
machine.new(my_model)
|
336
321
|
end
|
337
322
|
|
338
323
|
it "sets the passed class" do
|
339
324
|
my_transition_class = Class.new
|
340
|
-
expect(Statesman.storage_adapter).to receive(:new).once
|
341
|
-
|
325
|
+
expect(Statesman.storage_adapter).to receive(:new).once.
|
326
|
+
with(my_transition_class, my_model, anything)
|
342
327
|
machine.new(my_model, transition_class: my_transition_class)
|
343
328
|
end
|
344
329
|
|
345
330
|
it "falls back to Memory without transaction_class" do
|
346
331
|
allow(Statesman).to receive(:storage_adapter).and_return(Class.new)
|
347
|
-
expect(Statesman::Adapters::Memory).to receive(:new).once
|
348
|
-
|
332
|
+
expect(Statesman::Adapters::Memory).to receive(:new).once.
|
333
|
+
with(Statesman::Adapters::MemoryTransition, my_model, anything)
|
349
334
|
machine.new(my_model)
|
350
335
|
end
|
351
336
|
end
|
@@ -380,10 +365,8 @@ describe Statesman::Machine do
|
|
380
365
|
end
|
381
366
|
|
382
367
|
context "with multiple transitions" do
|
383
|
-
before
|
384
|
-
|
385
|
-
instance.transition_to!(:z)
|
386
|
-
end
|
368
|
+
before { instance.transition_to!(:y) }
|
369
|
+
before { instance.transition_to!(:z) }
|
387
370
|
|
388
371
|
it { is_expected.to eq("z") }
|
389
372
|
end
|
@@ -408,18 +391,12 @@ describe Statesman::Machine do
|
|
408
391
|
end
|
409
392
|
|
410
393
|
context "with one possible state" do
|
411
|
-
before
|
412
|
-
instance.transition_to!(:y)
|
413
|
-
end
|
414
|
-
|
394
|
+
before { instance.transition_to!(:y) }
|
415
395
|
it { is_expected.to eq(['z']) }
|
416
396
|
end
|
417
397
|
|
418
398
|
context "with no possible transitions" do
|
419
|
-
before
|
420
|
-
instance.transition_to!(:z)
|
421
|
-
end
|
422
|
-
|
399
|
+
before { instance.transition_to!(:z) }
|
423
400
|
it { is_expected.to eq([]) }
|
424
401
|
end
|
425
402
|
end
|
@@ -429,8 +406,8 @@ describe Statesman::Machine do
|
|
429
406
|
let(:last_action) { "Whatever" }
|
430
407
|
|
431
408
|
it "delegates to the storage adapter" do
|
432
|
-
expect_any_instance_of(Statesman.storage_adapter).to receive(:last).once
|
433
|
-
|
409
|
+
expect_any_instance_of(Statesman.storage_adapter).to receive(:last).once.
|
410
|
+
and_return(last_action)
|
434
411
|
expect(instance.last_transition).to be(last_action)
|
435
412
|
end
|
436
413
|
end
|
@@ -499,9 +476,8 @@ describe Statesman::Machine do
|
|
499
476
|
|
500
477
|
context "when the state cannot be transitioned to" do
|
501
478
|
it "raises an error" do
|
502
|
-
expect
|
503
|
-
|
504
|
-
end.to raise_error(Statesman::TransitionFailedError)
|
479
|
+
expect { instance.transition_to!(:z) }.
|
480
|
+
to raise_error(Statesman::TransitionFailedError)
|
505
481
|
end
|
506
482
|
end
|
507
483
|
|
@@ -512,12 +488,11 @@ describe Statesman::Machine do
|
|
512
488
|
end
|
513
489
|
|
514
490
|
it "creates a new transition object" do
|
515
|
-
expect
|
516
|
-
instance.
|
517
|
-
end.to change(instance.history, :count).by(1)
|
491
|
+
expect { instance.transition_to!(:y) }.
|
492
|
+
to change(instance.history, :count).by(1)
|
518
493
|
|
519
|
-
expect(instance.history.first)
|
520
|
-
|
494
|
+
expect(instance.history.first).
|
495
|
+
to be_a(Statesman::Adapters::MemoryTransition)
|
521
496
|
expect(instance.history.first.to_state).to eq("y")
|
522
497
|
end
|
523
498
|
|
@@ -532,9 +507,7 @@ describe Statesman::Machine do
|
|
532
507
|
expect(instance.history.first.metadata).to eq({})
|
533
508
|
end
|
534
509
|
|
535
|
-
|
536
|
-
expect(instance.transition_to!(:y)).to be_truthy
|
537
|
-
end
|
510
|
+
specify { expect(instance.transition_to!(:y)).to be_truthy }
|
538
511
|
|
539
512
|
context "with a guard" do
|
540
513
|
let(:result) { true }
|
@@ -545,8 +518,8 @@ describe Statesman::Machine do
|
|
545
518
|
let(:instance) { machine.new(my_model) }
|
546
519
|
|
547
520
|
it "passes the object to the guard" do
|
548
|
-
expect(guard_cb).to receive(:call).once
|
549
|
-
|
521
|
+
expect(guard_cb).to receive(:call).once.
|
522
|
+
with(my_model, instance.last_transition, {}).and_return(true)
|
550
523
|
instance.transition_to!(:y)
|
551
524
|
end
|
552
525
|
end
|
@@ -562,9 +535,8 @@ describe Statesman::Machine do
|
|
562
535
|
let(:result) { false }
|
563
536
|
|
564
537
|
it "raises an exception" do
|
565
|
-
expect
|
566
|
-
|
567
|
-
end.to raise_error(Statesman::GuardFailedError)
|
538
|
+
expect { instance.transition_to!(:y) }.
|
539
|
+
to raise_error(Statesman::GuardFailedError)
|
568
540
|
end
|
569
541
|
end
|
570
542
|
end
|
@@ -578,32 +550,29 @@ describe Statesman::Machine do
|
|
578
550
|
|
579
551
|
context "when it is succesful" do
|
580
552
|
before do
|
581
|
-
expect(instance).to receive(:transition_to!).once
|
582
|
-
|
553
|
+
expect(instance).to receive(:transition_to!).once.
|
554
|
+
with(:some_state, metadata).and_return(:some_state)
|
583
555
|
end
|
584
556
|
it { is_expected.to be(:some_state) }
|
585
557
|
end
|
586
558
|
|
587
559
|
context "when it is unsuccesful" do
|
588
560
|
before do
|
589
|
-
allow(instance).to receive(:transition_to!)
|
590
|
-
|
561
|
+
allow(instance).to receive(:transition_to!).
|
562
|
+
and_raise(Statesman::GuardFailedError)
|
591
563
|
end
|
592
564
|
it { is_expected.to be_falsey }
|
593
565
|
end
|
594
566
|
|
595
567
|
context "when a non statesman exception is raised" do
|
596
568
|
before do
|
597
|
-
allow(instance).to receive(:transition_to!)
|
598
|
-
|
569
|
+
allow(instance).to receive(:transition_to!).
|
570
|
+
and_raise(RuntimeError, 'user defined exception')
|
599
571
|
end
|
600
572
|
|
601
573
|
it "should not rescue the exception" do
|
602
|
-
|
603
|
-
|
604
|
-
end
|
605
|
-
|
606
|
-
expectation.to raise_error(RuntimeError, 'user defined exception')
|
574
|
+
expect { instance.transition_to(:some_state, metadata) }.
|
575
|
+
to raise_error(RuntimeError, 'user defined exception')
|
607
576
|
end
|
608
577
|
end
|
609
578
|
end
|
@@ -650,13 +619,11 @@ describe Statesman::Machine do
|
|
650
619
|
end
|
651
620
|
|
652
621
|
describe "#before_callbacks_for" do
|
653
|
-
it_behaves_like "a callback filter", :before_transition,
|
654
|
-
:before
|
622
|
+
it_behaves_like "a callback filter", :before_transition, :before
|
655
623
|
end
|
656
624
|
|
657
625
|
describe "#after_callbacks_for" do
|
658
|
-
it_behaves_like "a callback filter", :after_transition,
|
659
|
-
:after
|
626
|
+
it_behaves_like "a callback filter", :after_transition, :after
|
660
627
|
end
|
661
628
|
|
662
629
|
describe "#event" do
|
@@ -680,9 +647,8 @@ describe Statesman::Machine do
|
|
680
647
|
|
681
648
|
context "when the state cannot be transitioned to" do
|
682
649
|
it "raises an error" do
|
683
|
-
expect
|
684
|
-
|
685
|
-
end.to raise_error(Statesman::TransitionFailedError)
|
650
|
+
expect { instance.trigger!(:event_2) }.
|
651
|
+
to raise_error(Statesman::TransitionFailedError)
|
686
652
|
end
|
687
653
|
end
|
688
654
|
|
@@ -693,12 +659,11 @@ describe Statesman::Machine do
|
|
693
659
|
end
|
694
660
|
|
695
661
|
it "creates a new transition object" do
|
696
|
-
expect
|
697
|
-
instance.
|
698
|
-
end.to change(instance.history, :count).by(1)
|
662
|
+
expect { instance.trigger!(:event_1) }.
|
663
|
+
to change(instance.history, :count).by(1)
|
699
664
|
|
700
|
-
expect(instance.history.first)
|
701
|
-
|
665
|
+
expect(instance.history.first).
|
666
|
+
to be_a(Statesman::Adapters::MemoryTransition)
|
702
667
|
expect(instance.history.first.to_state).to eq("y")
|
703
668
|
end
|
704
669
|
|
@@ -728,16 +693,16 @@ describe Statesman::Machine do
|
|
728
693
|
let(:instance) { machine.new(my_model) }
|
729
694
|
|
730
695
|
it "passes the object to the guard" do
|
731
|
-
expect(guard_cb).to receive(:call).once
|
732
|
-
|
696
|
+
expect(guard_cb).to receive(:call).once.
|
697
|
+
with(my_model, instance.last_transition, {}).and_return(true)
|
733
698
|
instance.trigger!(:event_1)
|
734
699
|
end
|
735
700
|
end
|
736
701
|
|
737
702
|
context "which passes" do
|
738
703
|
it "changes state" do
|
739
|
-
instance.trigger!(:event_1)
|
740
|
-
|
704
|
+
expect { instance.trigger!(:event_1) }.
|
705
|
+
to change { instance.current_state }.to("y")
|
741
706
|
end
|
742
707
|
end
|
743
708
|
|
@@ -745,14 +710,12 @@ describe Statesman::Machine do
|
|
745
710
|
let(:result) { false }
|
746
711
|
|
747
712
|
it "raises an exception" do
|
748
|
-
expect
|
749
|
-
|
750
|
-
end.to raise_error(Statesman::GuardFailedError)
|
713
|
+
expect { instance.trigger!(:event_1) }.
|
714
|
+
to raise_error(Statesman::GuardFailedError)
|
751
715
|
end
|
752
716
|
end
|
753
717
|
end
|
754
718
|
end
|
755
|
-
|
756
719
|
end
|
757
720
|
|
758
721
|
describe "#available_events" do
|
@@ -784,5 +747,4 @@ describe Statesman::Machine do
|
|
784
747
|
expect(instance.available_events).to eq([:event_2, :event_3])
|
785
748
|
end
|
786
749
|
end
|
787
|
-
|
788
750
|
end
|