finite_machine 0.10.1 → 0.10.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|