finite_machine 0.10.1 → 0.10.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/CHANGELOG.md +9 -0
- data/lib/finite_machine.rb +1 -1
- data/lib/finite_machine/catchable.rb +3 -0
- data/lib/finite_machine/event.rb +23 -8
- data/lib/finite_machine/{event_builder.rb → event_definition.rb} +14 -9
- data/lib/finite_machine/events_chain.rb +1 -1
- data/lib/finite_machine/hooks.rb +2 -4
- data/lib/finite_machine/state_machine.rb +19 -14
- data/lib/finite_machine/state_parser.rb +32 -12
- data/lib/finite_machine/transition.rb +31 -20
- data/lib/finite_machine/transition_builder.rb +8 -2
- data/lib/finite_machine/version.rb +1 -1
- data/spec/unit/callbacks_spec.rb +28 -0
- data/spec/unit/choice_spec.rb +42 -2
- data/spec/unit/event/add_spec.rb +2 -4
- data/spec/unit/events_spec.rb +4 -1
- data/spec/unit/state_parser/{parse_states_spec.rb → parse_spec.rb} +8 -8
- data/spec/unit/transition/parse_states_spec.rb +1 -1
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: edb4b2a96673bef5c94c26d477821d2e59a5f756
|
4
|
+
data.tar.gz: 518346cb8ac30326a5a9550f6b0b18bea9de5918
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d179ed858abc96e2776de1f45278b261ce50f8adccf1841420c334a4f2427d0999ee3cc46e40c263bff62cadbf68284a619d491f8ea8016cfa0d3c576d1dd40c
|
7
|
+
data.tar.gz: 2036811dac682bb9decb39a5c39e5cc7c026109db57e97946dd3ca26b8106d109eaecffd200c6c8481cf5c5fb3a5fff6eaae5ed188b9e4bd1ab2d3ea1276aa6a
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
0.10.2 (July 5, 2015)
|
2
|
+
|
3
|
+
* Fix to run 'on_after' callbacks even when event cancalled by @craiglittle
|
4
|
+
* Fix to cancel transition when no matching choice is found by @craiglittle
|
5
|
+
* Change StateParser #parse method
|
6
|
+
* Change EventBuilder to EventDefinition and invert dependencies
|
7
|
+
* Change Event#call to #trigger
|
8
|
+
* Change Transition#call to #execute
|
9
|
+
|
1
10
|
0.10.1 (May 24, 2015)
|
2
11
|
|
3
12
|
* Add ability to inherit state machine definitions
|
data/lib/finite_machine.rb
CHANGED
@@ -17,7 +17,7 @@ require "finite_machine/async_call"
|
|
17
17
|
require "finite_machine/hook_event"
|
18
18
|
require "finite_machine/env"
|
19
19
|
require "finite_machine/event"
|
20
|
-
require "finite_machine/
|
20
|
+
require "finite_machine/event_definition"
|
21
21
|
require "finite_machine/event_queue"
|
22
22
|
require "finite_machine/events_chain"
|
23
23
|
require "finite_machine/hooks"
|
data/lib/finite_machine/event.rb
CHANGED
@@ -2,20 +2,32 @@
|
|
2
2
|
|
3
3
|
module FiniteMachine
|
4
4
|
# A class representing event with transitions
|
5
|
+
#
|
6
|
+
# Used by {EventDefinition} to create events.
|
7
|
+
#
|
8
|
+
# @api private
|
5
9
|
class Event
|
6
10
|
include Comparable
|
7
11
|
include Threadable
|
8
12
|
|
9
13
|
# The name of this event
|
14
|
+
#
|
15
|
+
# @return [Symbol]
|
10
16
|
attr_threadsafe :name
|
11
17
|
|
12
18
|
# The state transitions for this event
|
19
|
+
#
|
20
|
+
# @return [Array[Transition]]
|
13
21
|
attr_threadsafe :state_transitions
|
14
22
|
|
15
23
|
# The reference to the state machine for this event
|
24
|
+
#
|
25
|
+
# @return [StateMachine]
|
16
26
|
attr_threadsafe :machine
|
17
27
|
|
18
28
|
# The silent option for this transition
|
29
|
+
#
|
30
|
+
# @return [Boolean]
|
19
31
|
attr_threadsafe :silent
|
20
32
|
|
21
33
|
# Initialize an Event
|
@@ -39,19 +51,21 @@ module FiniteMachine
|
|
39
51
|
# @example
|
40
52
|
# event << FiniteMachine::Transition.new machine, :a => :b
|
41
53
|
#
|
42
|
-
# @return [
|
54
|
+
# @return [Event]
|
43
55
|
#
|
44
56
|
# @api public
|
45
57
|
def <<(transition)
|
46
58
|
sync_exclusive do
|
47
59
|
Array(transition).flatten.each { |trans| state_transitions << trans }
|
48
60
|
end
|
61
|
+
self
|
49
62
|
end
|
50
63
|
alias_method :add, :<<
|
51
64
|
|
52
65
|
# Find next transition
|
53
66
|
#
|
54
|
-
# @return [
|
67
|
+
# @return [Transition]
|
68
|
+
# the next available transition
|
55
69
|
#
|
56
70
|
# @api private
|
57
71
|
def next_transition
|
@@ -65,7 +79,7 @@ module FiniteMachine
|
|
65
79
|
#
|
66
80
|
# @param [Array[Object]] args
|
67
81
|
#
|
68
|
-
# return
|
82
|
+
# return [Transition]
|
69
83
|
#
|
70
84
|
# @api private
|
71
85
|
def find_transition(*args)
|
@@ -81,17 +95,18 @@ module FiniteMachine
|
|
81
95
|
# If silent option is passed the event will not fire any callbacks
|
82
96
|
#
|
83
97
|
# @example
|
84
|
-
# transition =
|
85
|
-
# transition.
|
98
|
+
# transition = Event.new(machine, {})
|
99
|
+
# transition.trigger
|
86
100
|
#
|
87
|
-
# @return [
|
101
|
+
# @return [Boolean]
|
102
|
+
# true is transition succeeded, false otherwise
|
88
103
|
#
|
89
104
|
# @api public
|
90
|
-
def
|
105
|
+
def trigger(*args, &block)
|
91
106
|
sync_exclusive do
|
92
107
|
event_transition = next_transition
|
93
108
|
if silent
|
94
|
-
event_transition.
|
109
|
+
event_transition.execute(*args, &block)
|
95
110
|
else
|
96
111
|
machine.send(:transition, event_transition, *args, &block)
|
97
112
|
end
|
@@ -1,8 +1,13 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
module FiniteMachine
|
4
|
-
# A class responsible for
|
5
|
-
|
4
|
+
# A class responsible for defining event methods on state machine
|
5
|
+
#
|
6
|
+
# Used to add event definitions from {TransitionBuilder} to
|
7
|
+
# the {StateMachine} to obtain convenience helpers.
|
8
|
+
#
|
9
|
+
# @api private
|
10
|
+
class EventDefinition
|
6
11
|
include Threadable
|
7
12
|
include Safety
|
8
13
|
|
@@ -18,15 +23,15 @@ module FiniteMachine
|
|
18
23
|
@machine = machine
|
19
24
|
end
|
20
25
|
|
21
|
-
#
|
26
|
+
# Define transition event names as state machine events
|
22
27
|
#
|
23
|
-
# @param [
|
24
|
-
# the transition for which event is
|
28
|
+
# @param [Transition] transition
|
29
|
+
# the transition for which event definition is created
|
25
30
|
#
|
26
|
-
# @return [
|
31
|
+
# @return [Transition]
|
27
32
|
#
|
28
33
|
# @api private
|
29
|
-
def
|
34
|
+
def apply(transition)
|
30
35
|
name = transition.name
|
31
36
|
detect_event_conflict!(name)
|
32
37
|
if machine.singleton_class.send(:method_defined?, name)
|
@@ -53,12 +58,12 @@ module FiniteMachine
|
|
53
58
|
# @api private
|
54
59
|
def define_event_transition(name, transition)
|
55
60
|
silent = transition.silent
|
56
|
-
_event =
|
61
|
+
_event = Event.new(machine, name: name, silent: silent)
|
57
62
|
_event << transition
|
58
63
|
machine.events_chain.add(name, _event)
|
59
64
|
|
60
65
|
machine.send(:define_singleton_method, name) do |*args, &block|
|
61
|
-
_event.
|
66
|
+
_event.trigger(*args, &block)
|
62
67
|
end
|
63
68
|
end
|
64
69
|
|
@@ -99,7 +99,7 @@ module FiniteMachine
|
|
99
99
|
# @api public
|
100
100
|
def check_choice_conditions(name, *args, &block)
|
101
101
|
chain[name].state_transitions.any? do |trans|
|
102
|
-
trans.check_conditions(*args, &block)
|
102
|
+
trans.current? && trans.check_conditions(*args, &block)
|
103
103
|
end
|
104
104
|
end
|
105
105
|
|
data/lib/finite_machine/hooks.rb
CHANGED
@@ -66,10 +66,8 @@ module FiniteMachine
|
|
66
66
|
# @return [Hash]
|
67
67
|
#
|
68
68
|
# @api public
|
69
|
-
def call(event_type, event_state)
|
70
|
-
collection[event_type][event_state].each
|
71
|
-
yield hook
|
72
|
-
end
|
69
|
+
def call(event_type, event_state, &block)
|
70
|
+
collection[event_type][event_state].each(&block)
|
73
71
|
end
|
74
72
|
|
75
73
|
# Check if collection has any elements
|
@@ -257,8 +257,9 @@ module FiniteMachine
|
|
257
257
|
exception = InvalidStateError
|
258
258
|
catch_error(exception) ||
|
259
259
|
fail(exception, "inappropriate current state '#{state}'")
|
260
|
-
|
260
|
+
return false
|
261
261
|
end
|
262
|
+
return true
|
262
263
|
end
|
263
264
|
|
264
265
|
# Performs transition
|
@@ -274,24 +275,28 @@ module FiniteMachine
|
|
274
275
|
sync_exclusive do
|
275
276
|
notify HookEvent::Before, event_transition, *args
|
276
277
|
|
277
|
-
|
278
|
-
|
278
|
+
if valid_state?(event_transition) && event_transition.valid?(*args, &block)
|
279
|
+
notify HookEvent::Exit, event_transition, *args
|
279
280
|
|
280
|
-
|
281
|
+
begin
|
282
|
+
event_transition.execute(*args)
|
283
|
+
Logger.report_transition(event_transition, *args) if log_transitions
|
281
284
|
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
+
notify HookEvent::Transition, event_transition, *args
|
286
|
+
rescue Exception => e
|
287
|
+
catch_error(e) || raise_transition_error(e)
|
288
|
+
end
|
285
289
|
|
286
|
-
notify HookEvent::
|
287
|
-
|
288
|
-
|
289
|
-
end
|
290
|
+
notify HookEvent::Enter, event_transition, *args
|
291
|
+
|
292
|
+
notify HookEvent::After, event_transition, *args
|
290
293
|
|
291
|
-
|
292
|
-
|
294
|
+
event_transition.same?(state) ? NOTRANSITION : SUCCEEDED
|
295
|
+
else
|
296
|
+
notify HookEvent::After, event_transition, *args
|
293
297
|
|
294
|
-
|
298
|
+
CANCELLED
|
299
|
+
end
|
295
300
|
end
|
296
301
|
end
|
297
302
|
|
@@ -2,6 +2,10 @@
|
|
2
2
|
|
3
3
|
module FiniteMachine
|
4
4
|
# A class responsible for converting transition arguments to states
|
5
|
+
#
|
6
|
+
# Used by {TransitionBuilder} to parse user input state transitions.
|
7
|
+
#
|
8
|
+
# @api private
|
5
9
|
class StateParser
|
6
10
|
include Threadable
|
7
11
|
|
@@ -19,6 +23,7 @@ module FiniteMachine
|
|
19
23
|
# @api public
|
20
24
|
def initialize(attrs)
|
21
25
|
@attrs = ensure_only_states!(attrs)
|
26
|
+
freeze
|
22
27
|
end
|
23
28
|
|
24
29
|
# Extract states from attributes
|
@@ -28,16 +33,14 @@ module FiniteMachine
|
|
28
33
|
# @example
|
29
34
|
# StateParpser.new(attr).parase_states
|
30
35
|
#
|
31
|
-
# @
|
36
|
+
# @yield [Hash[Symbol]] the resolved states
|
37
|
+
#
|
38
|
+
# @return [Hash[Symbol]] the resolved states
|
32
39
|
#
|
33
40
|
# @api public
|
34
|
-
def
|
35
|
-
|
36
|
-
|
37
|
-
else
|
38
|
-
convert_attributes_to_states_hash
|
39
|
-
end
|
40
|
-
block ? transitions.each(&block) : transitions
|
41
|
+
def parse(&block)
|
42
|
+
states = extract_states
|
43
|
+
block ? states.each(&block) : states
|
41
44
|
end
|
42
45
|
|
43
46
|
# Check if attributes contain :from or :to key
|
@@ -84,10 +87,10 @@ module FiniteMachine
|
|
84
87
|
#
|
85
88
|
# @api private
|
86
89
|
def ensure_only_states!(attrs)
|
87
|
-
|
88
|
-
BLACKLIST.each { |key|
|
89
|
-
raise_not_enough_transitions unless
|
90
|
-
|
90
|
+
attributes = attrs.dup
|
91
|
+
BLACKLIST.each { |key| attributes.delete(key) }
|
92
|
+
raise_not_enough_transitions unless attributes.any?
|
93
|
+
attributes
|
91
94
|
end
|
92
95
|
|
93
96
|
# Convert attrbiutes with :from, :to keys to states hash
|
@@ -104,6 +107,10 @@ module FiniteMachine
|
|
104
107
|
|
105
108
|
# Convert collapsed attributes to states hash
|
106
109
|
#
|
110
|
+
# @example
|
111
|
+
# parser = StateParser.new([:green, :red] => :yellow)
|
112
|
+
# parser.parse # => {green: :yellow, red: :yellow}
|
113
|
+
#
|
107
114
|
# @return [Hash[Symbol]]
|
108
115
|
#
|
109
116
|
# @api private
|
@@ -118,6 +125,19 @@ module FiniteMachine
|
|
118
125
|
end
|
119
126
|
end
|
120
127
|
|
128
|
+
# Perform extraction of states from user supplied definitions
|
129
|
+
#
|
130
|
+
# @return [Hash[Symbol]] the resolved states
|
131
|
+
#
|
132
|
+
# @api private
|
133
|
+
def extract_states
|
134
|
+
if contains_from_to_keys?
|
135
|
+
convert_from_to_attributes_to_states_hash
|
136
|
+
else
|
137
|
+
convert_attributes_to_states_hash
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
121
141
|
# Raise error when not enough transitions are provided
|
122
142
|
#
|
123
143
|
# @raise [NotEnoughTransitionsError]
|
@@ -26,7 +26,7 @@ module FiniteMachine
|
|
26
26
|
attr_threadsafe :cancelled
|
27
27
|
|
28
28
|
# All states for this transition event
|
29
|
-
attr_threadsafe :
|
29
|
+
attr_threadsafe :states
|
30
30
|
|
31
31
|
# Silence callbacks
|
32
32
|
attr_threadsafe :silent
|
@@ -40,10 +40,10 @@ module FiniteMachine
|
|
40
40
|
def initialize(machine, attrs = {})
|
41
41
|
@machine = machine
|
42
42
|
@name = attrs.fetch(:name, DEFAULT_STATE)
|
43
|
-
@
|
43
|
+
@states = attrs.fetch(:parsed_states, {})
|
44
44
|
@silent = attrs.fetch(:silent, false)
|
45
|
-
@from_states = @
|
46
|
-
@to_states = @
|
45
|
+
@from_states = @states.keys
|
46
|
+
@to_states = @states.values
|
47
47
|
@from_state = @from_states.first
|
48
48
|
@if = Array(attrs.fetch(:if, []))
|
49
49
|
@unless = Array(attrs.fetch(:unless, []))
|
@@ -54,21 +54,21 @@ module FiniteMachine
|
|
54
54
|
# Create transition with associated helper methods
|
55
55
|
#
|
56
56
|
# @param [FiniteMachine::StateMachine] machine
|
57
|
+
#
|
57
58
|
# @param [Hash] attrs
|
58
59
|
#
|
59
60
|
# @example
|
60
|
-
#
|
61
|
+
# attributes = {parsed_states: {green: :yellow}, silent: true}
|
62
|
+
# Transition.create(machine, attrbiutes)
|
61
63
|
#
|
62
|
-
# @return [
|
64
|
+
# @return [Transition]
|
63
65
|
#
|
64
66
|
# @api public
|
65
67
|
def self.create(machine, attrs = {})
|
66
68
|
transition = new(machine, attrs)
|
67
69
|
transition.update_transitions
|
68
|
-
transition.
|
69
|
-
|
70
|
-
builder = EventBuilder.new(machine)
|
71
|
-
builder.call(transition)
|
70
|
+
transition.define_state_query_methods
|
71
|
+
transition
|
72
72
|
end
|
73
73
|
|
74
74
|
# Decide :to state from available transitions for this event
|
@@ -79,7 +79,12 @@ module FiniteMachine
|
|
79
79
|
def to_state(*args)
|
80
80
|
if transition_choice?
|
81
81
|
found_trans = machine.select_choice_transition(name, from_state, *args)
|
82
|
-
|
82
|
+
|
83
|
+
if found_trans.nil? # no choice found
|
84
|
+
from_state
|
85
|
+
else
|
86
|
+
found_trans.states[from_state] || found_trans.states[ANY_STATE]
|
87
|
+
end
|
83
88
|
else
|
84
89
|
available_trans = machine.transitions[name]
|
85
90
|
available_trans[from_state] || available_trans[ANY_STATE]
|
@@ -114,7 +119,7 @@ module FiniteMachine
|
|
114
119
|
#
|
115
120
|
# @api public
|
116
121
|
def same?(state)
|
117
|
-
|
122
|
+
states[state] == state || (states[ANY_STATE] == state && from_state == state)
|
118
123
|
end
|
119
124
|
|
120
125
|
# Check if from matches current state
|
@@ -161,24 +166,30 @@ module FiniteMachine
|
|
161
166
|
|
162
167
|
# Add transition to the machine
|
163
168
|
#
|
164
|
-
# @return [
|
169
|
+
# @return [Transition]
|
165
170
|
#
|
166
171
|
# @api private
|
167
172
|
def update_transitions
|
168
173
|
from_states.each do |from|
|
169
174
|
if (value = machine.transitions[name][from])
|
170
|
-
machine.transitions[name][from] = [value,
|
175
|
+
machine.transitions[name][from] = [value, states[from]].flatten
|
171
176
|
else
|
172
|
-
machine.transitions[name][from] =
|
177
|
+
machine.transitions[name][from] = states[from] || ANY_STATE
|
173
178
|
end
|
174
179
|
end
|
180
|
+
self
|
175
181
|
end
|
176
182
|
|
177
183
|
# Define helper state mehods for the transition states
|
178
184
|
#
|
185
|
+
# @return [Transition]
|
186
|
+
#
|
179
187
|
# @api private
|
180
|
-
def
|
181
|
-
from_states.concat(to_states).each
|
188
|
+
def define_state_query_methods
|
189
|
+
from_states.concat(to_states).each do |state|
|
190
|
+
define_state_query_method(state)
|
191
|
+
end
|
192
|
+
self
|
182
193
|
end
|
183
194
|
|
184
195
|
# Define state helper method
|
@@ -186,7 +197,7 @@ module FiniteMachine
|
|
186
197
|
# @param [Symbol] state
|
187
198
|
#
|
188
199
|
# @api private
|
189
|
-
def
|
200
|
+
def define_state_query_method(state)
|
190
201
|
return if machine.respond_to?("#{state}?")
|
191
202
|
machine.send(:define_singleton_method, "#{state}?") do
|
192
203
|
machine.is?(state.to_sym)
|
@@ -225,7 +236,7 @@ module FiniteMachine
|
|
225
236
|
# @return [nil]
|
226
237
|
#
|
227
238
|
# @api private
|
228
|
-
def
|
239
|
+
def execute(*args)
|
229
240
|
sync_exclusive do
|
230
241
|
return if cancelled
|
231
242
|
self.from_state = machine.state
|
@@ -250,7 +261,7 @@ module FiniteMachine
|
|
250
261
|
#
|
251
262
|
# @api public
|
252
263
|
def inspect
|
253
|
-
transitions = @
|
264
|
+
transitions = @states.map { |from, to| "#{from} -> #{to}" }.join(', ')
|
254
265
|
"<##{self.class} @name=#{@name}, @transitions=#{transitions}, @when=#{@conditions}>"
|
255
266
|
end
|
256
267
|
end # Transition
|
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
module FiniteMachine
|
4
4
|
# A class reponsible for building transition out of parsed states
|
5
|
+
#
|
6
|
+
# @api private
|
5
7
|
class TransitionBuilder
|
6
8
|
include Threadable
|
7
9
|
|
@@ -10,6 +12,8 @@ module FiniteMachine
|
|
10
12
|
|
11
13
|
attr_threadsafe :attributes
|
12
14
|
|
15
|
+
attr_threadsafe :event_definition
|
16
|
+
|
13
17
|
# Initialize a TransitionBuilder
|
14
18
|
#
|
15
19
|
# @example
|
@@ -19,6 +23,7 @@ module FiniteMachine
|
|
19
23
|
def initialize(machine, attributes = {})
|
20
24
|
@machine = machine
|
21
25
|
@attributes = attributes
|
26
|
+
@event_definition = EventDefinition.new(machine)
|
22
27
|
end
|
23
28
|
|
24
29
|
# Creates transitions for the states
|
@@ -33,9 +38,10 @@ module FiniteMachine
|
|
33
38
|
#
|
34
39
|
# @api public
|
35
40
|
def call(states)
|
36
|
-
FiniteMachine::StateParser.new(states).
|
41
|
+
FiniteMachine::StateParser.new(states).parse do |from, to|
|
37
42
|
attributes.merge!(parsed_states: { from => to })
|
38
|
-
Transition.create(machine, attributes)
|
43
|
+
transition = Transition.create(machine, attributes)
|
44
|
+
event_definition.apply(transition)
|
39
45
|
end
|
40
46
|
end
|
41
47
|
end # TransitionBuilder
|
data/spec/unit/callbacks_spec.rb
CHANGED
@@ -902,4 +902,32 @@ RSpec.describe FiniteMachine, 'callbacks' do
|
|
902
902
|
fsm.stop
|
903
903
|
expect(called).to eq(['on_enter_red'])
|
904
904
|
end
|
905
|
+
|
906
|
+
it "executes event-based callbacks even when state does not change" do
|
907
|
+
called = []
|
908
|
+
fsm = FiniteMachine.define do
|
909
|
+
initial :active
|
910
|
+
|
911
|
+
events {
|
912
|
+
event :advance, :active => :inactive, if: -> { false }
|
913
|
+
event :advance, :inactive => :active, if: -> { false }
|
914
|
+
}
|
915
|
+
|
916
|
+
callbacks {
|
917
|
+
on_before do |event|
|
918
|
+
called << "before_#{event.name}_#{event.from}_#{event.to}"
|
919
|
+
end
|
920
|
+
on_after do |event|
|
921
|
+
called << "after_#{event.name}_#{event.from}_#{event.to}"
|
922
|
+
end
|
923
|
+
}
|
924
|
+
end
|
925
|
+
expect(fsm.current).to eq(:active)
|
926
|
+
fsm.advance
|
927
|
+
expect(fsm.current).to eq(:active)
|
928
|
+
expect(called).to eq([
|
929
|
+
'before_advance_active_inactive',
|
930
|
+
'after_advance_active_inactive'
|
931
|
+
])
|
932
|
+
end
|
905
933
|
end
|
data/spec/unit/choice_spec.rb
CHANGED
@@ -85,7 +85,7 @@ RSpec.describe FiniteMachine, '#choice' do
|
|
85
85
|
expect(fsm.current).to eq(:agreement_form)
|
86
86
|
end
|
87
87
|
|
88
|
-
it "
|
88
|
+
it "chooses state when skipped if/unless" do
|
89
89
|
fsm = FiniteMachine.define do
|
90
90
|
initial :company_form
|
91
91
|
|
@@ -102,7 +102,7 @@ RSpec.describe FiniteMachine, '#choice' do
|
|
102
102
|
expect(fsm.current).to eq(:promo_form)
|
103
103
|
end
|
104
104
|
|
105
|
-
it "
|
105
|
+
it "chooses default state when branching conditions don't match" do
|
106
106
|
fsm = FiniteMachine.define do
|
107
107
|
initial :company_form
|
108
108
|
|
@@ -213,6 +213,46 @@ RSpec.describe FiniteMachine, '#choice' do
|
|
213
213
|
expect(fsm.current).to eq(:fulfilled)
|
214
214
|
end
|
215
215
|
|
216
|
+
it "does not transition when no matching choice for multiple event definitions" do
|
217
|
+
ticket = double(:ticket, :pending? => true, :finished? => false)
|
218
|
+
called = []
|
219
|
+
fsm = FiniteMachine.define do
|
220
|
+
initial :inactive
|
221
|
+
|
222
|
+
target ticket
|
223
|
+
|
224
|
+
events {
|
225
|
+
event :advance, from: [:inactive, :paused, :fulfilled] do
|
226
|
+
choice :active, if: proc { |_ticket| !_ticket.pending? }
|
227
|
+
end
|
228
|
+
|
229
|
+
event :advance, from: [:inactive, :active, :fulfilled] do
|
230
|
+
choice :paused, if: proc { |_ticket| _ticket.pending? }
|
231
|
+
end
|
232
|
+
|
233
|
+
event :advance, from: [:inactive, :active, :paused] do
|
234
|
+
choice :fulfilled, if: proc { |_ticket| _ticket.finished? }
|
235
|
+
end
|
236
|
+
}
|
237
|
+
|
238
|
+
callbacks {
|
239
|
+
on_before(:advance) { called << 'on_before_advance' }
|
240
|
+
on_after(:advance) { called << 'on_after_advance' }
|
241
|
+
}
|
242
|
+
end
|
243
|
+
expect(fsm.current).to eq(:inactive)
|
244
|
+
fsm.advance
|
245
|
+
expect(fsm.current).to eq(:paused)
|
246
|
+
fsm.advance
|
247
|
+
expect(fsm.current).to eq(:paused)
|
248
|
+
expect(called).to eq([
|
249
|
+
'on_before_advance',
|
250
|
+
'on_after_advance',
|
251
|
+
'on_before_advance',
|
252
|
+
'on_after_advance'
|
253
|
+
])
|
254
|
+
end
|
255
|
+
|
216
256
|
it "sets callback properties correctly" do
|
217
257
|
expected = {name: :init, from: :none, to: :red, a: nil, b: nil, c: nil }
|
218
258
|
|
data/spec/unit/event/add_spec.rb
CHANGED
@@ -7,12 +7,10 @@ RSpec.describe FiniteMachine::Event, '#<<' do
|
|
7
7
|
|
8
8
|
let(:object) { described_class }
|
9
9
|
|
10
|
-
subject(:event) { object.new(machine, name: :test) }
|
11
|
-
|
12
10
|
it "adds multiple transitions" do
|
13
11
|
transition = double(:transition)
|
14
|
-
event
|
15
|
-
event << transition
|
12
|
+
event = object.new(machine)
|
13
|
+
event << transition << transition
|
16
14
|
expect(event.state_transitions).to match_array([transition, transition])
|
17
15
|
end
|
18
16
|
end
|
data/spec/unit/events_spec.rb
CHANGED
@@ -181,7 +181,10 @@ RSpec.describe FiniteMachine, 'events' do
|
|
181
181
|
|
182
182
|
expect(fsm.current).to eql(:green)
|
183
183
|
|
184
|
-
expect {
|
184
|
+
expect {
|
185
|
+
fsm.stop
|
186
|
+
}.to raise_error(FiniteMachine::InvalidStateError,
|
187
|
+
/inappropriate current state 'green'/)
|
185
188
|
end
|
186
189
|
|
187
190
|
it "allows to transition to any state" do
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'spec_helper'
|
4
4
|
|
5
|
-
RSpec.describe FiniteMachine::StateParser, '#
|
5
|
+
RSpec.describe FiniteMachine::StateParser, '#parse' do
|
6
6
|
let(:object) { described_class }
|
7
7
|
|
8
8
|
subject(:parser) { object.new(attrs) }
|
@@ -12,7 +12,7 @@ RSpec.describe FiniteMachine::StateParser, '#inspect' do
|
|
12
12
|
|
13
13
|
it "raises error for no transitions" do
|
14
14
|
expect {
|
15
|
-
parser.
|
15
|
+
parser.parse
|
16
16
|
}.to raise_error(FiniteMachine::NotEnoughTransitionsError)
|
17
17
|
end
|
18
18
|
end
|
@@ -21,7 +21,7 @@ RSpec.describe FiniteMachine::StateParser, '#inspect' do
|
|
21
21
|
let(:attrs) { { from: :green, to: :yellow }}
|
22
22
|
|
23
23
|
it "removes :from and :to keys" do
|
24
|
-
expect(parser.
|
24
|
+
expect(parser.parse).to eq({green: :yellow})
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
@@ -29,7 +29,7 @@ RSpec.describe FiniteMachine::StateParser, '#inspect' do
|
|
29
29
|
let(:attrs) { { from: :green }}
|
30
30
|
|
31
31
|
it "adds to state as copy of from" do
|
32
|
-
expect(parser.
|
32
|
+
expect(parser.parse).to eq({green: :green})
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
@@ -37,7 +37,7 @@ RSpec.describe FiniteMachine::StateParser, '#inspect' do
|
|
37
37
|
let(:attrs) { { to: :green }}
|
38
38
|
|
39
39
|
it "inserts :any from state" do
|
40
|
-
expect(parser.
|
40
|
+
expect(parser.parse).to eq({any: :green})
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
@@ -45,15 +45,15 @@ RSpec.describe FiniteMachine::StateParser, '#inspect' do
|
|
45
45
|
let(:attrs) { { green: :yellow } }
|
46
46
|
|
47
47
|
it "copies attributes over" do
|
48
|
-
expect(parser.
|
48
|
+
expect(parser.parse).to eq({green: :yellow})
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
52
|
context 'when array of from states' do
|
53
|
-
let(:attrs) { { [:green] => :yellow } }
|
53
|
+
let(:attrs) { { [:green, :red] => :yellow } }
|
54
54
|
|
55
55
|
it "extracts states" do
|
56
|
-
expect(parser.
|
56
|
+
expect(parser.parse).to include({red: :yellow, green: :yellow})
|
57
57
|
end
|
58
58
|
end
|
59
59
|
end
|
@@ -13,7 +13,7 @@ RSpec.describe FiniteMachine::Transition, 'parsed_states' do
|
|
13
13
|
it "groups states" do
|
14
14
|
expect(transition.from_states).to eq([:any])
|
15
15
|
expect(transition.to_states).to eq([:red])
|
16
|
-
expect(transition.
|
16
|
+
expect(transition.states).to eql({any: :red})
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: finite_machine
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.10.
|
4
|
+
version: 0.10.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Piotr Murach
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-05
|
11
|
+
date: 2015-07-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -56,7 +56,7 @@ files:
|
|
56
56
|
- lib/finite_machine/dsl.rb
|
57
57
|
- lib/finite_machine/env.rb
|
58
58
|
- lib/finite_machine/event.rb
|
59
|
-
- lib/finite_machine/
|
59
|
+
- lib/finite_machine/event_definition.rb
|
60
60
|
- lib/finite_machine/event_queue.rb
|
61
61
|
- lib/finite_machine/events_chain.rb
|
62
62
|
- lib/finite_machine/hook_event.rb
|
@@ -110,7 +110,7 @@ files:
|
|
110
110
|
- spec/unit/logger_spec.rb
|
111
111
|
- spec/unit/respond_to_spec.rb
|
112
112
|
- spec/unit/state_parser/inspect_spec.rb
|
113
|
-
- spec/unit/state_parser/
|
113
|
+
- spec/unit/state_parser/parse_spec.rb
|
114
114
|
- spec/unit/states_spec.rb
|
115
115
|
- spec/unit/subscribers_spec.rb
|
116
116
|
- spec/unit/target_spec.rb
|
@@ -180,7 +180,7 @@ test_files:
|
|
180
180
|
- spec/unit/logger_spec.rb
|
181
181
|
- spec/unit/respond_to_spec.rb
|
182
182
|
- spec/unit/state_parser/inspect_spec.rb
|
183
|
-
- spec/unit/state_parser/
|
183
|
+
- spec/unit/state_parser/parse_spec.rb
|
184
184
|
- spec/unit/states_spec.rb
|
185
185
|
- spec/unit/subscribers_spec.rb
|
186
186
|
- spec/unit/target_spec.rb
|