end_state 0.10.1 → 0.11.0
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/README.md +27 -20
- data/examples/example1.rb +2 -2
- data/lib/end_state/concluder.rb +0 -4
- data/lib/end_state/concluders/persistence.rb +0 -4
- data/lib/end_state/errors.rb +1 -6
- data/lib/end_state/graph.rb +4 -4
- data/lib/end_state/state_machine.rb +50 -33
- data/lib/end_state/transition.rb +0 -6
- data/lib/end_state/version.rb +1 -1
- data/spec/end_state/action_spec.rb +2 -2
- data/spec/end_state/concluders/persistence_spec.rb +2 -2
- data/spec/end_state/state_machine_spec.rb +80 -39
- data/spec/end_state/transition_spec.rb +16 -16
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fd8b390f8750c3af0948f56fa4e7471f5d4dfce8
|
|
4
|
+
data.tar.gz: ed30307b64e15e69829f25f61dcd883f3ce42fc2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d00715edd410e6dcffba526a15a5a5ad9475ce70665363423751a08b0a70912024b8c433126c8e11231b045d7278ee00821f3afb8845f1e3b578aaf56f01cad2
|
|
7
|
+
data.tar.gz: f4139a6f1fca2ca85e3883885d48106f293d0e7605060863cfd4ca6b7b350f24bfc8d10f6b65786329ebd6338c669e11bd9de2f34bc0f22c359c2eb08f7d3591
|
data/README.md
CHANGED
|
@@ -51,15 +51,17 @@ machine = Machine.new(StatefulObject.new(:a))
|
|
|
51
51
|
machine.transition :b # => true
|
|
52
52
|
machine.state # => :b
|
|
53
53
|
machine.b? # => true
|
|
54
|
-
machine.c
|
|
54
|
+
machine.transition :c # => true
|
|
55
55
|
machine.state # => :c
|
|
56
56
|
machine.can_transition? :b # => false
|
|
57
57
|
machine.can_transition? :a # => true
|
|
58
|
-
machine.b
|
|
59
|
-
machine.
|
|
58
|
+
machine.transition :b # => false
|
|
59
|
+
machine.transition! :b # => raises InvalidTransition
|
|
60
|
+
machine.transition :a # => true
|
|
60
61
|
machine.state # => :a
|
|
61
|
-
machine.go
|
|
62
|
+
machine.go # => true
|
|
62
63
|
machine.state # => :b
|
|
64
|
+
machine.go! # => raises InvalidTransition
|
|
63
65
|
```
|
|
64
66
|
|
|
65
67
|
## Initial State
|
|
@@ -86,13 +88,13 @@ class Machine < EndState::StateMachine
|
|
|
86
88
|
end
|
|
87
89
|
|
|
88
90
|
machine = Machine.new(StatefulObject.new(:a))
|
|
89
|
-
machine.d
|
|
90
|
-
machine.state
|
|
91
|
+
machine.transition :d # true
|
|
92
|
+
machine.state # :d
|
|
91
93
|
|
|
92
94
|
machine = Machine.new(StatefulObject.new(:a))
|
|
93
|
-
machine.b
|
|
94
|
-
machine.d
|
|
95
|
-
machine.state
|
|
95
|
+
machine.transition :b # true
|
|
96
|
+
machine.transition :d # true
|
|
97
|
+
machine.state # :d
|
|
96
98
|
```
|
|
97
99
|
|
|
98
100
|
## Guards
|
|
@@ -234,10 +236,11 @@ end
|
|
|
234
236
|
By using the `as` option in a transition definition you are creating an event representing that transition.
|
|
235
237
|
This can allow you to exercise the machine in a more natural "verb" style interaction. When using `as` event
|
|
236
238
|
definitions you can optionally set a `blocked` message on the transition. When the event is executed, if the
|
|
237
|
-
machine is not in
|
|
238
|
-
array on the machine.
|
|
239
|
+
machine is not in the initial state of the event, the message is added to the `failure_messages`
|
|
240
|
+
array on the machine. Events, like `transition` have both a standard and a bang (`!`) style. The bang style
|
|
241
|
+
will raise an exception if there is a problem.
|
|
239
242
|
|
|
240
|
-
```
|
|
243
|
+
```ruby
|
|
241
244
|
class Machine < EndState::StateMachine
|
|
242
245
|
transition a: :b, as: :go do |t|
|
|
243
246
|
t.blocked 'Cannot go!'
|
|
@@ -246,10 +249,11 @@ end
|
|
|
246
249
|
|
|
247
250
|
machine = Machine.new(StatefulObject.new(:a))
|
|
248
251
|
|
|
249
|
-
machine.go
|
|
252
|
+
machine.go # => true
|
|
250
253
|
machine.state # => :b
|
|
251
|
-
machine.go
|
|
254
|
+
machine.go # => false
|
|
252
255
|
machine.failure_messages # => ['Cannot go!']
|
|
256
|
+
machine.go! # => raises InvalidTransition
|
|
253
257
|
```
|
|
254
258
|
|
|
255
259
|
## Parameters
|
|
@@ -261,25 +265,28 @@ When defining a transition you can indicate what parameters you are expecting wi
|
|
|
261
265
|
If you require any params then attempting to transition without them provided will raise an error. Specifying allowed
|
|
262
266
|
params is purely for documentation purposes.
|
|
263
267
|
|
|
264
|
-
```
|
|
268
|
+
```ruby
|
|
265
269
|
class Machine < EndState::StateMachine
|
|
266
|
-
transition a: :b do |t|
|
|
270
|
+
transition a: :b, as: :go do |t|
|
|
267
271
|
t.allow_params :foo, :bar
|
|
268
272
|
end
|
|
269
273
|
end
|
|
270
274
|
```
|
|
271
275
|
|
|
272
|
-
```
|
|
276
|
+
```ruby
|
|
273
277
|
class Machine < EndState::StateMachine
|
|
274
|
-
transition a: :b do |t|
|
|
278
|
+
transition a: :b, as: :go do |t|
|
|
275
279
|
t.require_params :foo, :bar
|
|
276
280
|
end
|
|
277
281
|
end
|
|
278
282
|
|
|
279
283
|
machine = Machine.new(StatefulObject.new(:a))
|
|
284
|
+
machine.transition :b # => error raised: 'Missing params: foo, bar'
|
|
285
|
+
machine.transition :b, foo: 1, bar: 'value' # => true
|
|
280
286
|
|
|
281
|
-
machine
|
|
282
|
-
machine.
|
|
287
|
+
machine = Machine.new(StatefulObject.new(:a))
|
|
288
|
+
machine.go # => error raised: 'Missing params: foo, bar'
|
|
289
|
+
machine.go foo: 1, bar: 'value' # => true
|
|
283
290
|
```
|
|
284
291
|
|
|
285
292
|
## State storage
|
data/examples/example1.rb
CHANGED
|
@@ -25,7 +25,7 @@ end
|
|
|
25
25
|
|
|
26
26
|
class Machine < EndState::StateMachine
|
|
27
27
|
transition a: :b do |t|
|
|
28
|
-
t.guard Easy
|
|
28
|
+
t.guard Easy
|
|
29
29
|
t.persistence_on
|
|
30
30
|
end
|
|
31
31
|
|
|
@@ -35,7 +35,7 @@ class Machine < EndState::StateMachine
|
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
transition [:b, :c] => :a do |t|
|
|
38
|
-
t.concluder NoOp
|
|
38
|
+
t.concluder NoOp
|
|
39
39
|
t.persistence_on
|
|
40
40
|
end
|
|
41
41
|
end
|
data/lib/end_state/concluder.rb
CHANGED
data/lib/end_state/errors.rb
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
module EndState
|
|
2
2
|
class Error < StandardError; end
|
|
3
3
|
class UnknownState < Error; end
|
|
4
|
-
class
|
|
5
|
-
class InvalidEvent < Error; end
|
|
4
|
+
class InvalidTransition < Error; end
|
|
6
5
|
class GuardFailed < Error; end
|
|
7
6
|
class ConcluderFailed < Error; end
|
|
8
|
-
|
|
9
|
-
# Backward compatibility
|
|
10
|
-
# Finalizer is deprecated
|
|
11
|
-
FinalizerFailed = ConcluderFailed
|
|
12
7
|
end
|
data/lib/end_state/graph.rb
CHANGED
|
@@ -12,9 +12,9 @@ module EndState
|
|
|
12
12
|
def draw
|
|
13
13
|
machine.transitions.keys.each do |t|
|
|
14
14
|
left, right = t.to_a.flatten
|
|
15
|
-
nodes[left] ||=
|
|
16
|
-
nodes[right] ||=
|
|
17
|
-
edge =
|
|
15
|
+
nodes[left] ||= add_nodes(left.to_s)
|
|
16
|
+
nodes[right] ||= add_nodes(right.to_s)
|
|
17
|
+
edge = add_edges nodes[left], nodes[right]
|
|
18
18
|
if event_labels
|
|
19
19
|
event = machine.events.detect do |event, transition|
|
|
20
20
|
transition.include? t
|
|
@@ -25,4 +25,4 @@ module EndState
|
|
|
25
25
|
self
|
|
26
26
|
end
|
|
27
27
|
end
|
|
28
|
-
end
|
|
28
|
+
end
|
|
@@ -80,8 +80,8 @@ module EndState
|
|
|
80
80
|
def can_transition?(state, params = {})
|
|
81
81
|
previous_state = self.state.to_sym
|
|
82
82
|
state = state.to_sym
|
|
83
|
-
transition =
|
|
84
|
-
return
|
|
83
|
+
transition = __sm_transition_for(previous_state, state)
|
|
84
|
+
return __sm_block_transistion(transition, state, :soft) unless transition
|
|
85
85
|
transition.will_allow? state, params
|
|
86
86
|
end
|
|
87
87
|
|
|
@@ -90,11 +90,12 @@ module EndState
|
|
|
90
90
|
@success_messages = []
|
|
91
91
|
previous_state = self.state ? self.state.to_sym : self.state
|
|
92
92
|
state = state.to_sym
|
|
93
|
-
transition =
|
|
94
|
-
|
|
95
|
-
return
|
|
93
|
+
transition = __sm_transition_for(previous_state, state)
|
|
94
|
+
mode = __sm_actual_mode(mode)
|
|
95
|
+
return __sm_block_transistion(transition, state, mode) unless transition
|
|
96
|
+
return __sm_guard_failed(state, mode) unless transition.allowed?(self, params)
|
|
96
97
|
return false unless transition.action.new(self, state).call
|
|
97
|
-
return
|
|
98
|
+
return __sm_conclude_failed(state, mode) unless transition.conclude(self, previous_state, params)
|
|
98
99
|
true
|
|
99
100
|
end
|
|
100
101
|
|
|
@@ -103,62 +104,78 @@ module EndState
|
|
|
103
104
|
end
|
|
104
105
|
|
|
105
106
|
def method_missing(method, *args, &block)
|
|
106
|
-
|
|
107
|
-
return
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
if method.to_s.end_with?('!')
|
|
112
|
-
transition check_state, (args[0] || {})
|
|
113
|
-
else
|
|
114
|
-
super
|
|
115
|
-
end
|
|
107
|
+
return super unless __sm_predicate_or_event?(method)
|
|
108
|
+
return __sm_current_state?(method) if __sm_state_predicate(method)
|
|
109
|
+
new_state, mode = __sm_event(method)
|
|
110
|
+
return false if new_state == :__invalid_event__
|
|
111
|
+
transition new_state, (args[0] || {}), mode
|
|
116
112
|
end
|
|
117
113
|
|
|
118
114
|
private
|
|
119
115
|
|
|
120
|
-
def
|
|
121
|
-
|
|
116
|
+
def __sm_predicate_or_event?(method)
|
|
117
|
+
__sm_state_predicate(method) ||
|
|
118
|
+
__sm_event(method)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def __sm_state_predicate(method)
|
|
122
|
+
state = method.to_s[0..-2].to_sym
|
|
123
|
+
return unless self.class.states.include?(state) && method.to_s.end_with?('?')
|
|
124
|
+
state
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def __sm_event(method)
|
|
128
|
+
event = __sm_state_for_event(method.to_sym, __sm_actual_mode(:soft))
|
|
129
|
+
return event, __sm_actual_mode(:soft) if event
|
|
130
|
+
return unless method.to_s.end_with?('!')
|
|
131
|
+
event = __sm_state_for_event(method.to_s[0..-2].to_sym, :hard)
|
|
132
|
+
return event, :hard if event
|
|
133
|
+
nil
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def __sm_actual_mode(mode)
|
|
137
|
+
return :hard if self.class.mode == :hard
|
|
138
|
+
mode
|
|
122
139
|
end
|
|
123
140
|
|
|
124
|
-
def
|
|
125
|
-
state.to_sym ==
|
|
141
|
+
def __sm_current_state?(method)
|
|
142
|
+
state.to_sym == __sm_state_predicate(method)
|
|
126
143
|
end
|
|
127
144
|
|
|
128
|
-
def
|
|
145
|
+
def __sm_state_for_event(event, mode)
|
|
129
146
|
transitions = self.class.events[event]
|
|
130
147
|
return false unless transitions
|
|
131
148
|
start_states = transitions.map { |t| t.keys.first }
|
|
132
|
-
return
|
|
149
|
+
return __sm_invalid_event(event, mode) unless start_states.include?(state.to_sym) || start_states.include?(:any_state)
|
|
133
150
|
transitions.first.values.first
|
|
134
151
|
end
|
|
135
152
|
|
|
136
|
-
def
|
|
137
|
-
|
|
138
|
-
self.class.transitions[{ any_state: to }]
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
def invalid_event(event)
|
|
142
|
-
fail InvalidEvent, "Transition by event: #{event} is invalid." if self.class.mode == :hard
|
|
153
|
+
def __sm_invalid_event(event, mode)
|
|
154
|
+
fail InvalidTransition, "Transition by event: #{event} is invalid." if mode == :hard
|
|
143
155
|
message = self.class.transitions[self.class.events[event].first].blocked_event_message
|
|
144
156
|
@failure_messages = [message] if message
|
|
145
157
|
:__invalid_event__
|
|
146
158
|
end
|
|
147
159
|
|
|
148
|
-
def
|
|
160
|
+
def __sm_transition_for(from, to)
|
|
161
|
+
self.class.transitions[{ from => to }] ||
|
|
162
|
+
self.class.transitions[{ any_state: to }]
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def __sm_block_transistion(transition, state, mode)
|
|
149
166
|
if self.class.end_states.include? state
|
|
150
|
-
fail
|
|
167
|
+
fail InvalidTransition, "The transition: #{object.state} => #{state} is invalid." if mode == :hard
|
|
151
168
|
return false
|
|
152
169
|
end
|
|
153
170
|
fail UnknownState, "The state: #{state} is unknown."
|
|
154
171
|
end
|
|
155
172
|
|
|
156
|
-
def
|
|
173
|
+
def __sm_guard_failed(state, mode)
|
|
157
174
|
return false unless mode == :hard
|
|
158
175
|
fail GuardFailed, "The transition to #{state} was blocked: #{failure_messages.join(', ')}"
|
|
159
176
|
end
|
|
160
177
|
|
|
161
|
-
def
|
|
178
|
+
def __sm_conclude_failed(state, mode)
|
|
162
179
|
return false unless mode == :hard
|
|
163
180
|
fail ConcluderFailed, "The transition to #{state} was rolled back: #{failure_messages.join(', ')}"
|
|
164
181
|
end
|
data/lib/end_state/transition.rb
CHANGED
|
@@ -63,12 +63,6 @@ module EndState
|
|
|
63
63
|
@blocked_event_message = message
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
-
# Backward compatibility
|
|
67
|
-
# Finalizer is deprecated
|
|
68
|
-
alias_method :finalizers, :concluders
|
|
69
|
-
alias_method :finalize, :conclude
|
|
70
|
-
alias_method :finalizer, :concluder
|
|
71
|
-
|
|
72
66
|
private
|
|
73
67
|
|
|
74
68
|
def rollback(concluded, object, previous_state, params)
|
data/lib/end_state/version.rb
CHANGED
|
@@ -6,7 +6,7 @@ module EndState
|
|
|
6
6
|
let(:object) { OpenStruct.new(state: nil) }
|
|
7
7
|
let(:state) { :a }
|
|
8
8
|
|
|
9
|
-
before { object.
|
|
9
|
+
before { allow(object).to receive_message_chain(:class, store_states_as_strings: false) }
|
|
10
10
|
|
|
11
11
|
describe '#call' do
|
|
12
12
|
it 'changes the state to the new state' do
|
|
@@ -22,4 +22,4 @@ module EndState
|
|
|
22
22
|
end
|
|
23
23
|
end
|
|
24
24
|
end
|
|
25
|
-
end
|
|
25
|
+
end
|
|
@@ -18,7 +18,7 @@ module EndState
|
|
|
18
18
|
let(:object) { Object.new }
|
|
19
19
|
|
|
20
20
|
it 'returns false' do
|
|
21
|
-
expect(concluder.call).to
|
|
21
|
+
expect(concluder.call).to be false
|
|
22
22
|
end
|
|
23
23
|
end
|
|
24
24
|
end
|
|
@@ -33,7 +33,7 @@ module EndState
|
|
|
33
33
|
let(:object) { Object.new }
|
|
34
34
|
|
|
35
35
|
it 'returns true' do
|
|
36
|
-
expect(concluder.rollback).to
|
|
36
|
+
expect(concluder.rollback).to be true
|
|
37
37
|
end
|
|
38
38
|
end
|
|
39
39
|
end
|
|
@@ -23,7 +23,7 @@ module EndState
|
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
it 'does not require a block' do
|
|
26
|
-
expect
|
|
26
|
+
expect { StateMachine.transition(b: :c) }.not_to raise_error
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
it 'adds the transition to the state machine' do
|
|
@@ -89,13 +89,13 @@ module EndState
|
|
|
89
89
|
describe '.store_states_as_strings!' do
|
|
90
90
|
it 'sets the flag' do
|
|
91
91
|
StateMachine.store_states_as_strings!
|
|
92
|
-
expect(StateMachine.store_states_as_strings).to
|
|
92
|
+
expect(StateMachine.store_states_as_strings).to be true
|
|
93
93
|
end
|
|
94
94
|
end
|
|
95
95
|
|
|
96
96
|
describe '#store_states_as_strings' do
|
|
97
97
|
it 'is false by default' do
|
|
98
|
-
expect(StateMachine.store_states_as_strings).to
|
|
98
|
+
expect(StateMachine.store_states_as_strings).to be false
|
|
99
99
|
end
|
|
100
100
|
end
|
|
101
101
|
|
|
@@ -136,15 +136,15 @@ module EndState
|
|
|
136
136
|
context 'when the object has state :a' do
|
|
137
137
|
let(:object) { OpenStruct.new(state: :a) }
|
|
138
138
|
|
|
139
|
-
specify { expect(machine.a?).to
|
|
140
|
-
specify { expect(machine.b?).to
|
|
139
|
+
specify { expect(machine.a?).to be true }
|
|
140
|
+
specify { expect(machine.b?).to be false }
|
|
141
141
|
end
|
|
142
142
|
|
|
143
143
|
context 'when the object has state :b' do
|
|
144
144
|
let(:object) { OpenStruct.new(state: :b) }
|
|
145
145
|
|
|
146
|
-
specify { expect(machine.b?).to
|
|
147
|
-
specify { expect(machine.a?).to
|
|
146
|
+
specify { expect(machine.b?).to be true }
|
|
147
|
+
specify { expect(machine.a?).to be false }
|
|
148
148
|
end
|
|
149
149
|
|
|
150
150
|
context 'when the state shares a name with an event' do
|
|
@@ -153,7 +153,7 @@ module EndState
|
|
|
153
153
|
context 'and the object, in that state, cannot transition on the event' do
|
|
154
154
|
let(:object) { OpenStruct.new(state: :stop) }
|
|
155
155
|
|
|
156
|
-
specify { expect(machine.stop?).to
|
|
156
|
+
specify { expect(machine.stop?).to be true }
|
|
157
157
|
end
|
|
158
158
|
end
|
|
159
159
|
|
|
@@ -170,7 +170,7 @@ module EndState
|
|
|
170
170
|
end
|
|
171
171
|
end
|
|
172
172
|
|
|
173
|
-
describe '#{
|
|
173
|
+
describe '#{event}' do
|
|
174
174
|
let(:object) { OpenStruct.new(state: :a) }
|
|
175
175
|
before do
|
|
176
176
|
StateMachine.transition a: :b, as: :go do |t|
|
|
@@ -179,49 +179,90 @@ module EndState
|
|
|
179
179
|
end
|
|
180
180
|
|
|
181
181
|
it 'transitions the state' do
|
|
182
|
-
machine.
|
|
182
|
+
machine.go
|
|
183
183
|
expect(machine.state).to eq :b
|
|
184
184
|
end
|
|
185
185
|
|
|
186
186
|
it 'accepts params' do
|
|
187
|
-
machine.
|
|
188
|
-
machine.
|
|
189
|
-
expect(machine).to have_received(:transition).with(:b, { foo: 'bar', bar: 'foo' })
|
|
187
|
+
allow(machine).to receive(:transition)
|
|
188
|
+
machine.go foo: 'bar', bar: 'foo'
|
|
189
|
+
expect(machine).to have_received(:transition).with(:b, { foo: 'bar', bar: 'foo' }, :soft)
|
|
190
190
|
end
|
|
191
191
|
|
|
192
192
|
it 'defaults params to {}' do
|
|
193
|
-
machine.
|
|
194
|
-
machine.
|
|
195
|
-
expect(machine).to have_received(:transition).with(:b, {})
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
it 'works with an event' do
|
|
199
|
-
machine.go!
|
|
200
|
-
expect(machine.state).to eq :b
|
|
193
|
+
allow(machine).to receive(:transition)
|
|
194
|
+
machine.go
|
|
195
|
+
expect(machine).to have_received(:transition).with(:b, {}, :soft)
|
|
201
196
|
end
|
|
202
197
|
|
|
203
198
|
context 'when the intial state is :c' do
|
|
204
199
|
let(:object) { OpenStruct.new(state: :c) }
|
|
205
200
|
|
|
206
201
|
it 'blocks invalid events' do
|
|
207
|
-
machine.go
|
|
202
|
+
machine.go
|
|
208
203
|
expect(machine.state).to eq :c
|
|
209
204
|
end
|
|
210
205
|
|
|
211
206
|
it 'adds a failure message specified by blocked' do
|
|
212
|
-
machine.go
|
|
207
|
+
machine.go
|
|
213
208
|
expect(machine.failure_messages).to eq ['Invalid event!']
|
|
214
209
|
end
|
|
215
210
|
|
|
216
211
|
context 'and all transitions are forced to run in :hard mode' do
|
|
217
212
|
before { machine.class.treat_all_transitions_as_hard! }
|
|
218
213
|
|
|
219
|
-
it 'raises an
|
|
220
|
-
expect { machine.go
|
|
214
|
+
it 'raises an InvalidTransition error' do
|
|
215
|
+
expect { machine.go }.to raise_error(InvalidTransition)
|
|
221
216
|
end
|
|
222
217
|
end
|
|
223
218
|
end
|
|
224
219
|
|
|
220
|
+
context 'when using any_state with an event' do
|
|
221
|
+
before do
|
|
222
|
+
StateMachine.transition any_state: :end, as: :jump_to_end
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
it 'transitions the state to :end' do
|
|
226
|
+
machine.jump_to_end
|
|
227
|
+
expect(machine.state).to eq :end
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
describe '#{event}!' do
|
|
233
|
+
let(:object) { OpenStruct.new(state: :a) }
|
|
234
|
+
before do
|
|
235
|
+
StateMachine.transition a: :b, as: :go do |t|
|
|
236
|
+
t.blocked 'Invalid event!'
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
it 'transitions the state' do
|
|
241
|
+
machine.go!
|
|
242
|
+
expect(machine.state).to eq :b
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
it 'accepts params' do
|
|
246
|
+
allow(machine).to receive(:transition)
|
|
247
|
+
machine.go! foo: 'bar', bar: 'foo'
|
|
248
|
+
expect(machine).to have_received(:transition).with(:b, { foo: 'bar', bar: 'foo' }, :hard)
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
it 'defaults params to {}' do
|
|
252
|
+
allow(machine).to receive(:transition)
|
|
253
|
+
machine.go!
|
|
254
|
+
expect(machine).to have_received(:transition).with(:b, {}, :hard)
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
context 'when the intial state is :c' do
|
|
258
|
+
let(:object) { OpenStruct.new(state: :c) }
|
|
259
|
+
|
|
260
|
+
it 'blocks invalid events' do
|
|
261
|
+
expect { machine.go! }.to raise_error
|
|
262
|
+
expect(machine.state).to eq :c
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
|
|
225
266
|
context 'when using any_state with an event' do
|
|
226
267
|
before do
|
|
227
268
|
StateMachine.transition any_state: :end, as: :jump_to_end
|
|
@@ -242,11 +283,11 @@ module EndState
|
|
|
242
283
|
end
|
|
243
284
|
|
|
244
285
|
context 'when asking about an allowed transition' do
|
|
245
|
-
specify { expect(machine.can_transition? :b).to
|
|
286
|
+
specify { expect(machine.can_transition? :b).to be true }
|
|
246
287
|
end
|
|
247
288
|
|
|
248
289
|
context 'when asking about a disallowed transition' do
|
|
249
|
-
specify { expect(machine.can_transition? :c).to
|
|
290
|
+
specify { expect(machine.can_transition? :c).to be false }
|
|
250
291
|
end
|
|
251
292
|
|
|
252
293
|
context 'when using :any_state' do
|
|
@@ -255,13 +296,13 @@ module EndState
|
|
|
255
296
|
context 'and the initial state is :a' do
|
|
256
297
|
let(:object) { OpenStruct.new(state: :a) }
|
|
257
298
|
|
|
258
|
-
specify { expect(machine.can_transition? :d).to
|
|
299
|
+
specify { expect(machine.can_transition? :d).to be true }
|
|
259
300
|
end
|
|
260
301
|
|
|
261
302
|
context 'and the initial state is :b' do
|
|
262
303
|
let(:object) { OpenStruct.new(state: :b) }
|
|
263
304
|
|
|
264
|
-
specify { expect(machine.can_transition? :d).to
|
|
305
|
+
specify { expect(machine.can_transition? :d).to be true }
|
|
265
306
|
end
|
|
266
307
|
end
|
|
267
308
|
end
|
|
@@ -276,7 +317,7 @@ module EndState
|
|
|
276
317
|
before { StateMachine.transition a: :b }
|
|
277
318
|
|
|
278
319
|
it 'returns false' do
|
|
279
|
-
expect(machine.transition(:b)).to
|
|
320
|
+
expect(machine.transition(:b)).to be false
|
|
280
321
|
end
|
|
281
322
|
end
|
|
282
323
|
end
|
|
@@ -335,7 +376,7 @@ module EndState
|
|
|
335
376
|
|
|
336
377
|
context 'and the object satisfies the guard' do
|
|
337
378
|
before do
|
|
338
|
-
guard_instance.
|
|
379
|
+
allow(guard_instance).to receive(:allowed?).and_return(true)
|
|
339
380
|
object.state = :a
|
|
340
381
|
end
|
|
341
382
|
|
|
@@ -347,7 +388,7 @@ module EndState
|
|
|
347
388
|
|
|
348
389
|
context 'and the object does not satisfy the guard' do
|
|
349
390
|
before do
|
|
350
|
-
guard_instance.
|
|
391
|
+
allow(guard_instance).to receive(:allowed?).and_return(false)
|
|
351
392
|
object.state = :a
|
|
352
393
|
end
|
|
353
394
|
|
|
@@ -377,7 +418,7 @@ module EndState
|
|
|
377
418
|
|
|
378
419
|
context 'and the concluder is successful' do
|
|
379
420
|
before do
|
|
380
|
-
concluder_instance.
|
|
421
|
+
allow(concluder_instance).to receive(:call).and_return(true)
|
|
381
422
|
end
|
|
382
423
|
|
|
383
424
|
it 'transitions the state' do
|
|
@@ -388,7 +429,7 @@ module EndState
|
|
|
388
429
|
|
|
389
430
|
context 'and the concluder fails' do
|
|
390
431
|
before do
|
|
391
|
-
concluder_instance.
|
|
432
|
+
allow(concluder_instance).to receive(:call).and_return(false)
|
|
392
433
|
end
|
|
393
434
|
|
|
394
435
|
it 'does not transition the state' do
|
|
@@ -410,7 +451,7 @@ module EndState
|
|
|
410
451
|
before { StateMachine.transition a: :b }
|
|
411
452
|
|
|
412
453
|
it 'returns false' do
|
|
413
|
-
expect { machine.transition!(:b) }.to raise_error(
|
|
454
|
+
expect { machine.transition!(:b) }.to raise_error(InvalidTransition)
|
|
414
455
|
end
|
|
415
456
|
end
|
|
416
457
|
end
|
|
@@ -437,7 +478,7 @@ module EndState
|
|
|
437
478
|
|
|
438
479
|
context 'and the object satisfies the guard' do
|
|
439
480
|
before do
|
|
440
|
-
guard_instance.
|
|
481
|
+
allow(guard_instance).to receive(:allowed?).and_return(true)
|
|
441
482
|
object.state = :a
|
|
442
483
|
end
|
|
443
484
|
|
|
@@ -449,7 +490,7 @@ module EndState
|
|
|
449
490
|
|
|
450
491
|
context 'and the object does not satisfy the guard' do
|
|
451
492
|
before do
|
|
452
|
-
guard_instance.
|
|
493
|
+
allow(guard_instance).to receive(:allowed?).and_return(false)
|
|
453
494
|
object.state = :a
|
|
454
495
|
end
|
|
455
496
|
|
|
@@ -471,7 +512,7 @@ module EndState
|
|
|
471
512
|
|
|
472
513
|
context 'and the concluder is successful' do
|
|
473
514
|
before do
|
|
474
|
-
concluder_instance.
|
|
515
|
+
allow(concluder_instance).to receive(:call).and_return(true)
|
|
475
516
|
end
|
|
476
517
|
|
|
477
518
|
it 'transitions the state' do
|
|
@@ -482,7 +523,7 @@ module EndState
|
|
|
482
523
|
|
|
483
524
|
context 'and the concluder fails' do
|
|
484
525
|
before do
|
|
485
|
-
concluder_instance.
|
|
526
|
+
allow(concluder_instance).to receive(:call).and_return(false)
|
|
486
527
|
end
|
|
487
528
|
|
|
488
529
|
it 'does not transition the state' do
|
|
@@ -42,9 +42,9 @@ module EndState
|
|
|
42
42
|
before { transition.guards << guard }
|
|
43
43
|
|
|
44
44
|
context 'when all guards pass' do
|
|
45
|
-
before { guard_instance.
|
|
45
|
+
before { allow(guard_instance).to receive(:allowed?).and_return(true) }
|
|
46
46
|
|
|
47
|
-
specify { expect(transition.allowed? object).to
|
|
47
|
+
specify { expect(transition.allowed? object).to be true }
|
|
48
48
|
|
|
49
49
|
context 'when params are provided' do
|
|
50
50
|
it 'creates the guard with the params' do
|
|
@@ -62,16 +62,16 @@ module EndState
|
|
|
62
62
|
end
|
|
63
63
|
|
|
64
64
|
context 'and all required are provided' do
|
|
65
|
-
specify { expect(transition.allowed? object, foo: 1, bar: 2).to
|
|
65
|
+
specify { expect(transition.allowed? object, foo: 1, bar: 2).to be true }
|
|
66
66
|
end
|
|
67
67
|
end
|
|
68
68
|
end
|
|
69
69
|
end
|
|
70
70
|
|
|
71
71
|
context 'when not all guards pass' do
|
|
72
|
-
before { guard_instance.
|
|
72
|
+
before { allow(guard_instance).to receive(:allowed?).and_return(false) }
|
|
73
73
|
|
|
74
|
-
specify { expect(transition.allowed? object).to
|
|
74
|
+
specify { expect(transition.allowed? object).to be false }
|
|
75
75
|
end
|
|
76
76
|
end
|
|
77
77
|
|
|
@@ -82,9 +82,9 @@ module EndState
|
|
|
82
82
|
before { transition.guards << guard }
|
|
83
83
|
|
|
84
84
|
context 'when all guards pass' do
|
|
85
|
-
before { guard_instance.
|
|
85
|
+
before { allow(guard_instance).to receive(:will_allow?).and_return(true) }
|
|
86
86
|
|
|
87
|
-
specify { expect(transition.will_allow? object).to
|
|
87
|
+
specify { expect(transition.will_allow? object).to be true }
|
|
88
88
|
|
|
89
89
|
context 'when params are provided' do
|
|
90
90
|
it 'creates the guard with the params' do
|
|
@@ -96,20 +96,20 @@ module EndState
|
|
|
96
96
|
before { transition.require_params :foo, :bar }
|
|
97
97
|
|
|
98
98
|
context 'and not all required are provided' do
|
|
99
|
-
specify { expect(transition.will_allow? object).to
|
|
99
|
+
specify { expect(transition.will_allow? object).to be false }
|
|
100
100
|
end
|
|
101
101
|
|
|
102
102
|
context 'and all required are provided' do
|
|
103
|
-
specify { expect(transition.will_allow? object, foo: 1, bar: 2).to
|
|
103
|
+
specify { expect(transition.will_allow? object, foo: 1, bar: 2).to be true }
|
|
104
104
|
end
|
|
105
105
|
end
|
|
106
106
|
end
|
|
107
107
|
end
|
|
108
108
|
|
|
109
109
|
context 'when not all guards pass' do
|
|
110
|
-
before { guard_instance.
|
|
110
|
+
before { allow(guard_instance).to receive(:will_allow?).and_return(false) }
|
|
111
111
|
|
|
112
|
-
specify { expect(transition.will_allow? object).to
|
|
112
|
+
specify { expect(transition.will_allow? object).to be false }
|
|
113
113
|
end
|
|
114
114
|
|
|
115
115
|
context 'when params are provided' do
|
|
@@ -160,20 +160,20 @@ module EndState
|
|
|
160
160
|
let(:concluder_instance) { double :concluder_instance, call: nil, rollback: nil }
|
|
161
161
|
let(:object) { OpenStruct.new(state: :b) }
|
|
162
162
|
before do
|
|
163
|
-
object.
|
|
163
|
+
allow(object).to receive_message_chain(:class, store_states_as_strings: false)
|
|
164
164
|
transition.concluders << concluder
|
|
165
165
|
end
|
|
166
166
|
|
|
167
167
|
context 'when all concluders succeed' do
|
|
168
|
-
before { concluder_instance.
|
|
168
|
+
before { allow(concluder_instance).to receive(:call).and_return(true) }
|
|
169
169
|
|
|
170
|
-
specify { expect(transition.conclude object, :a).to
|
|
170
|
+
specify { expect(transition.conclude object, :a).to be true }
|
|
171
171
|
end
|
|
172
172
|
|
|
173
173
|
context 'when not all concluders succeed' do
|
|
174
|
-
before { concluder_instance.
|
|
174
|
+
before { allow(concluder_instance).to receive(:call).and_return(false) }
|
|
175
175
|
|
|
176
|
-
specify { expect(transition.conclude object, :a).to
|
|
176
|
+
specify { expect(transition.conclude object, :a).to be false }
|
|
177
177
|
|
|
178
178
|
it 'rolls them back' do
|
|
179
179
|
transition.conclude object, :a
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: end_state
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.11.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- alexpeachey
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2014-
|
|
11
|
+
date: 2014-08-21 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|