finite_machine 0.11.2 → 0.14.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 +5 -5
- data/CHANGELOG.md +80 -0
- data/LICENSE.txt +1 -1
- data/README.md +679 -624
- data/lib/finite_machine.rb +35 -45
- data/lib/finite_machine/async_call.rb +5 -21
- data/lib/finite_machine/callable.rb +4 -4
- data/lib/finite_machine/catchable.rb +24 -14
- data/lib/finite_machine/choice_merger.rb +20 -20
- data/lib/finite_machine/const.rb +16 -0
- data/lib/finite_machine/definition.rb +3 -3
- data/lib/finite_machine/dsl.rb +98 -151
- data/lib/finite_machine/env.rb +4 -2
- data/lib/finite_machine/event_definition.rb +7 -15
- data/lib/finite_machine/{events_chain.rb → events_map.rb} +40 -53
- data/lib/finite_machine/hook_event.rb +60 -61
- data/lib/finite_machine/hooks.rb +44 -36
- data/lib/finite_machine/listener.rb +2 -2
- data/lib/finite_machine/logger.rb +5 -4
- data/lib/finite_machine/{event_queue.rb → message_queue.rb} +76 -32
- data/lib/finite_machine/observer.rb +71 -34
- data/lib/finite_machine/safety.rb +16 -14
- data/lib/finite_machine/state_definition.rb +3 -5
- data/lib/finite_machine/state_machine.rb +93 -76
- data/lib/finite_machine/state_parser.rb +55 -83
- data/lib/finite_machine/subscribers.rb +2 -2
- data/lib/finite_machine/threadable.rb +3 -1
- data/lib/finite_machine/transition.rb +34 -34
- data/lib/finite_machine/transition_builder.rb +23 -32
- data/lib/finite_machine/transition_event.rb +12 -11
- data/lib/finite_machine/two_phase_lock.rb +8 -6
- data/lib/finite_machine/undefined_transition.rb +5 -6
- data/lib/finite_machine/version.rb +2 -2
- metadata +58 -142
- data/.gitignore +0 -18
- data/.rspec +0 -5
- data/.ruby-gemset +0 -1
- data/.ruby-version +0 -1
- data/.travis.yml +0 -26
- data/Gemfile +0 -15
- data/Rakefile +0 -8
- data/assets/finite_machine_logo.png +0 -0
- data/examples/atm.rb +0 -45
- data/examples/bug_system.rb +0 -145
- data/finite_machine.gemspec +0 -23
- data/lib/finite_machine/async_proxy.rb +0 -30
- data/spec/integration/system_spec.rb +0 -95
- data/spec/spec_helper.rb +0 -33
- data/spec/unit/alias_target_spec.rb +0 -108
- data/spec/unit/async_events_spec.rb +0 -138
- data/spec/unit/callable/call_spec.rb +0 -113
- data/spec/unit/callbacks_spec.rb +0 -942
- data/spec/unit/can_spec.rb +0 -98
- data/spec/unit/choice_spec.rb +0 -331
- data/spec/unit/define_spec.rb +0 -55
- data/spec/unit/definition_spec.rb +0 -115
- data/spec/unit/event_names_spec.rb +0 -19
- data/spec/unit/event_queue_spec.rb +0 -52
- data/spec/unit/events_chain/add_spec.rb +0 -25
- data/spec/unit/events_chain/cancel_transitions_spec.rb +0 -22
- data/spec/unit/events_chain/choice_transition_spec.rb +0 -28
- data/spec/unit/events_chain/clear_spec.rb +0 -15
- data/spec/unit/events_chain/events_spec.rb +0 -18
- data/spec/unit/events_chain/inspect_spec.rb +0 -24
- data/spec/unit/events_chain/match_transition_spec.rb +0 -37
- data/spec/unit/events_chain/move_to_spec.rb +0 -48
- data/spec/unit/events_chain/states_for_spec.rb +0 -17
- data/spec/unit/events_spec.rb +0 -459
- data/spec/unit/handlers_spec.rb +0 -152
- data/spec/unit/hook_event/build_spec.rb +0 -15
- data/spec/unit/hook_event/eql_spec.rb +0 -36
- data/spec/unit/hook_event/infer_default_name_spec.rb +0 -13
- data/spec/unit/hook_event/initialize_spec.rb +0 -25
- data/spec/unit/hook_event/notify_spec.rb +0 -14
- data/spec/unit/hooks/call_spec.rb +0 -24
- data/spec/unit/hooks/clear_spec.rb +0 -16
- data/spec/unit/hooks/inspect_spec.rb +0 -17
- data/spec/unit/hooks/register_spec.rb +0 -22
- data/spec/unit/if_unless_spec.rb +0 -353
- data/spec/unit/initial_spec.rb +0 -222
- data/spec/unit/inspect_spec.rb +0 -17
- data/spec/unit/is_spec.rb +0 -55
- data/spec/unit/log_transitions_spec.rb +0 -30
- data/spec/unit/logger_spec.rb +0 -38
- data/spec/unit/respond_to_spec.rb +0 -38
- data/spec/unit/state_parser/inspect_spec.rb +0 -25
- data/spec/unit/state_parser/parse_spec.rb +0 -59
- data/spec/unit/states_spec.rb +0 -34
- data/spec/unit/subscribers_spec.rb +0 -42
- data/spec/unit/target_spec.rb +0 -225
- data/spec/unit/terminated_spec.rb +0 -95
- data/spec/unit/transition/check_conditions_spec.rb +0 -54
- data/spec/unit/transition/inspect_spec.rb +0 -25
- data/spec/unit/transition/matches_spec.rb +0 -23
- data/spec/unit/transition/states_spec.rb +0 -31
- data/spec/unit/transition/to_state_spec.rb +0 -27
- data/spec/unit/trigger_spec.rb +0 -22
- data/spec/unit/undefined_transition/eql_spec.rb +0 -17
- data/tasks/console.rake +0 -11
- data/tasks/coverage.rake +0 -11
- data/tasks/spec.rake +0 -29
@@ -1,108 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
RSpec.describe FiniteMachine::Definition, '#alias_target' do
|
6
|
-
|
7
|
-
before do
|
8
|
-
stub_const("Car", Class.new do
|
9
|
-
def turn_reverse_lights_off
|
10
|
-
@reverse_lights = false
|
11
|
-
end
|
12
|
-
|
13
|
-
def turn_reverse_lights_on
|
14
|
-
@reverse_lights = true
|
15
|
-
end
|
16
|
-
|
17
|
-
def reverse_lights?
|
18
|
-
@reverse_lights ||= false
|
19
|
-
end
|
20
|
-
end)
|
21
|
-
end
|
22
|
-
|
23
|
-
it "aliases target" do
|
24
|
-
car = Car.new
|
25
|
-
fsm = FiniteMachine.new
|
26
|
-
fsm.target(car)
|
27
|
-
|
28
|
-
expect(fsm.target).to eq(car)
|
29
|
-
expect { fsm.car }.to raise_error(NoMethodError)
|
30
|
-
|
31
|
-
fsm.alias_target(:delorean)
|
32
|
-
expect(fsm.delorean).to eq(car)
|
33
|
-
end
|
34
|
-
|
35
|
-
it "scopes the target alias to a state machine instance" do
|
36
|
-
delorean = Car.new
|
37
|
-
batmobile = Car.new
|
38
|
-
fsm_a = FiniteMachine.new
|
39
|
-
fsm_a.target(delorean)
|
40
|
-
fsm_b = FiniteMachine.new
|
41
|
-
fsm_b.target(batmobile)
|
42
|
-
|
43
|
-
fsm_a.alias_target(:delorean)
|
44
|
-
fsm_b.alias_target(:batmobile)
|
45
|
-
|
46
|
-
expect(fsm_a.delorean).to eq(delorean)
|
47
|
-
expect { fsm_a.batmobile }.to raise_error(NoMethodError)
|
48
|
-
|
49
|
-
expect(fsm_b.batmobile).to eq(batmobile)
|
50
|
-
expect { fsm_b.delorean }.to raise_error(NoMethodError)
|
51
|
-
end
|
52
|
-
|
53
|
-
context 'when inside definition' do
|
54
|
-
before do
|
55
|
-
class Engine < FiniteMachine::Definition
|
56
|
-
initial :neutral
|
57
|
-
|
58
|
-
alias_target :car
|
59
|
-
|
60
|
-
events {
|
61
|
-
event :forward, [:reverse, :neutral] => :one
|
62
|
-
event :shift, :one => :two
|
63
|
-
event :shift, :two => :one
|
64
|
-
event :back, [:neutral, :one] => :reverse
|
65
|
-
}
|
66
|
-
|
67
|
-
callbacks {
|
68
|
-
on_enter :reverse do |event|
|
69
|
-
car.turn_reverse_lights_on
|
70
|
-
end
|
71
|
-
|
72
|
-
on_exit :reverse do |event|
|
73
|
-
car.turn_reverse_lights_off
|
74
|
-
end
|
75
|
-
}
|
76
|
-
|
77
|
-
handlers {
|
78
|
-
handle FiniteMachine::InvalidStateError do |exception| end
|
79
|
-
}
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
it "creates unique instances" do
|
84
|
-
engine_a = Engine.new
|
85
|
-
engine_b = Engine.new
|
86
|
-
expect(engine_a).not_to be(engine_b)
|
87
|
-
|
88
|
-
engine_a.forward
|
89
|
-
expect(engine_a.current).to eq(:one)
|
90
|
-
expect(engine_b.current).to eq(:neutral)
|
91
|
-
end
|
92
|
-
|
93
|
-
it "allows to create standalone machine" do
|
94
|
-
car = Car.new
|
95
|
-
engine = Engine.new
|
96
|
-
engine.target car
|
97
|
-
expect(engine.current).to eq(:neutral)
|
98
|
-
|
99
|
-
engine.forward
|
100
|
-
expect(engine.current).to eq(:one)
|
101
|
-
expect(car.reverse_lights?).to be false
|
102
|
-
|
103
|
-
engine.back
|
104
|
-
expect(engine.current).to eq(:reverse)
|
105
|
-
expect(car.reverse_lights?).to be true
|
106
|
-
end
|
107
|
-
end
|
108
|
-
end
|
@@ -1,138 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
RSpec.describe FiniteMachine, 'async_events' do
|
6
|
-
|
7
|
-
it 'runs events asynchronously' do
|
8
|
-
called = []
|
9
|
-
fsm = FiniteMachine.define do
|
10
|
-
initial :green
|
11
|
-
|
12
|
-
events {
|
13
|
-
event :slow, :green => :yellow
|
14
|
-
event :stop, :yellow => :red
|
15
|
-
event :ready, :red => :yellow
|
16
|
-
event :go, :yellow => :green
|
17
|
-
}
|
18
|
-
|
19
|
-
callbacks {
|
20
|
-
on_enter :yellow do |event, a| called << "on_enter_yellow_#{a}" end
|
21
|
-
on_enter :red do |event, a| called << "on_enter_red_#{a}" end
|
22
|
-
}
|
23
|
-
end
|
24
|
-
|
25
|
-
expect(fsm.current).to eql(:green)
|
26
|
-
fsm.async.slow(:foo)
|
27
|
-
fsm.event_queue.join 0.01
|
28
|
-
expect(fsm.current).to eql(:yellow)
|
29
|
-
expect(called).to eql([
|
30
|
-
'on_enter_yellow_foo'
|
31
|
-
])
|
32
|
-
fsm.async(:stop, :bar) # execute directly
|
33
|
-
fsm.event_queue.join 0.01
|
34
|
-
expect(fsm.current).to eql(:red)
|
35
|
-
expect(called).to match_array([
|
36
|
-
'on_enter_yellow_foo',
|
37
|
-
'on_enter_red_bar'
|
38
|
-
])
|
39
|
-
end
|
40
|
-
|
41
|
-
it 'correctly passes parameters to conditionals' do
|
42
|
-
called = []
|
43
|
-
fsm = FiniteMachine.define do
|
44
|
-
events {
|
45
|
-
event :go, :none => :green,
|
46
|
-
if: proc { |context, arg|
|
47
|
-
called << "cond_none_green(#{context},#{arg})"; true
|
48
|
-
}
|
49
|
-
|
50
|
-
event :stop, from: :any do
|
51
|
-
choice :red, if: proc { |context, arg|
|
52
|
-
called << "cond_any_red(#{context},#{arg})"; true
|
53
|
-
}
|
54
|
-
end
|
55
|
-
}
|
56
|
-
end
|
57
|
-
expect(fsm.current).to eql(:none)
|
58
|
-
fsm.async.go(:foo)
|
59
|
-
fsm.event_queue.join 0.02
|
60
|
-
expect(fsm.current).to eql(:green)
|
61
|
-
expect(called).to eql(["cond_none_green(#{fsm},foo)"])
|
62
|
-
|
63
|
-
expect(fsm.current).to eql(:green)
|
64
|
-
fsm.async.stop(:bar)
|
65
|
-
fsm.event_queue.join 0.02
|
66
|
-
expect(fsm.current).to eql(:red)
|
67
|
-
expect(called).to match_array([
|
68
|
-
"cond_none_green(#{fsm},foo)",
|
69
|
-
"cond_any_red(#{fsm},bar)"
|
70
|
-
])
|
71
|
-
end
|
72
|
-
|
73
|
-
it "ensure queue per thread" do
|
74
|
-
called = []
|
75
|
-
fsmFoo = nil
|
76
|
-
fsmBar = nil
|
77
|
-
foo_thread = Thread.new {
|
78
|
-
fsmFoo = FiniteMachine.define do
|
79
|
-
initial :green
|
80
|
-
events { event :slow, :green => :yellow }
|
81
|
-
|
82
|
-
callbacks {
|
83
|
-
on_enter :yellow do |event, a| called << "(foo)on_enter_yellow_#{a}" end
|
84
|
-
}
|
85
|
-
end
|
86
|
-
fsmFoo.async.slow(:foo)
|
87
|
-
}
|
88
|
-
bar_thread = Thread.new {
|
89
|
-
fsmBar = FiniteMachine.define do
|
90
|
-
initial :green
|
91
|
-
events { event :slow, :green => :yellow }
|
92
|
-
|
93
|
-
callbacks {
|
94
|
-
on_enter :yellow do |event, a| called << "(bar)on_enter_yellow_#{a}" end
|
95
|
-
}
|
96
|
-
end
|
97
|
-
fsmBar.async.slow(:bar)
|
98
|
-
}
|
99
|
-
ThreadsWait.all_waits(foo_thread, bar_thread)
|
100
|
-
fsmFoo.event_queue.join(0.01)
|
101
|
-
fsmBar.event_queue.join(0.01)
|
102
|
-
expect(called).to match_array([
|
103
|
-
'(foo)on_enter_yellow_foo',
|
104
|
-
'(bar)on_enter_yellow_bar'
|
105
|
-
])
|
106
|
-
expect(fsmFoo.current).to eql(:yellow)
|
107
|
-
expect(fsmBar.current).to eql(:yellow)
|
108
|
-
end
|
109
|
-
|
110
|
-
it "permits async callback" do
|
111
|
-
called = []
|
112
|
-
fsm = FiniteMachine.define do
|
113
|
-
initial :green, silent: false
|
114
|
-
|
115
|
-
events {
|
116
|
-
event :slow, :green => :yellow
|
117
|
-
event :go, :yellow => :green
|
118
|
-
}
|
119
|
-
|
120
|
-
callbacks {
|
121
|
-
on_enter :green, :async do |event| called << 'on_enter_green' end
|
122
|
-
on_before :slow, :async do |event| called << 'on_before_slow' end
|
123
|
-
on_exit :yellow, :async do |event| called << 'on_exit_yellow' end
|
124
|
-
on_after :go, :async do |event| called << 'on_after_go' end
|
125
|
-
}
|
126
|
-
end
|
127
|
-
fsm.slow
|
128
|
-
fsm.go
|
129
|
-
sleep 0.1
|
130
|
-
expect(called).to match_array([
|
131
|
-
'on_enter_green',
|
132
|
-
'on_before_slow',
|
133
|
-
'on_exit_yellow',
|
134
|
-
'on_enter_green',
|
135
|
-
'on_after_go'
|
136
|
-
])
|
137
|
-
end
|
138
|
-
end
|
@@ -1,113 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
RSpec.describe FiniteMachine::Callable, '#call' do
|
6
|
-
|
7
|
-
before(:each) {
|
8
|
-
stub_const("Car", Class.new do
|
9
|
-
attr_reader :result
|
10
|
-
|
11
|
-
def initialize
|
12
|
-
@engine_on = false
|
13
|
-
end
|
14
|
-
|
15
|
-
def turn_engine_on
|
16
|
-
@result = 'turn_engine_on'
|
17
|
-
@engine_on = true
|
18
|
-
end
|
19
|
-
|
20
|
-
def set_engine(value = :on)
|
21
|
-
@result = "set_engine(#{value})"
|
22
|
-
@engine = value.to_sym == :on
|
23
|
-
end
|
24
|
-
|
25
|
-
def turn_engine_off
|
26
|
-
@result = 'turn_engine_off'
|
27
|
-
@engine_on = false
|
28
|
-
end
|
29
|
-
|
30
|
-
def engine_on?
|
31
|
-
@result = 'engine_on'
|
32
|
-
!!@engine_on
|
33
|
-
end
|
34
|
-
end)
|
35
|
-
}
|
36
|
-
|
37
|
-
let(:called) { [] }
|
38
|
-
|
39
|
-
let(:target) { Car.new }
|
40
|
-
|
41
|
-
let(:instance) { described_class.new(object) }
|
42
|
-
|
43
|
-
context 'when string' do
|
44
|
-
let(:object) { 'engine_on?' }
|
45
|
-
|
46
|
-
it 'executes method on target' do
|
47
|
-
instance.call(target)
|
48
|
-
expect(target.result).to eql('engine_on')
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
context 'when string' do
|
53
|
-
let(:object) { 'set_engine(:on)' }
|
54
|
-
|
55
|
-
it 'executes method with arguments' do
|
56
|
-
instance.call(target)
|
57
|
-
expect(target.result).to eql('set_engine(on)')
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
context 'when string with arguments' do
|
62
|
-
let(:object) { 'set_engine' }
|
63
|
-
|
64
|
-
it 'executes method with arguments' do
|
65
|
-
instance.call(target, :off)
|
66
|
-
expect(target.result).to eql('set_engine(off)')
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
context 'when symbol' do
|
71
|
-
let(:object) { :set_engine }
|
72
|
-
|
73
|
-
it 'executes method on target' do
|
74
|
-
instance.call(target)
|
75
|
-
expect(target.result).to eql('set_engine(on)')
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
context 'when symbol with arguments' do
|
80
|
-
let(:object) { :set_engine }
|
81
|
-
|
82
|
-
it 'executes method on target' do
|
83
|
-
instance.call(target, :off)
|
84
|
-
expect(target.result).to eql('set_engine(off)')
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
context 'when proc without args' do
|
89
|
-
let(:object) { proc { |a| called << "block_with(#{a})" } }
|
90
|
-
|
91
|
-
it 'passes arguments' do
|
92
|
-
instance.call(target)
|
93
|
-
expect(called).to eql(["block_with(#{target})"])
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
context 'when proc with args' do
|
98
|
-
let(:object) { proc { |a,b| called << "block_with(#{a},#{b})" } }
|
99
|
-
|
100
|
-
it 'passes arguments' do
|
101
|
-
instance.call(target, :red)
|
102
|
-
expect(called).to eql(["block_with(#{target},red)"])
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
context 'when unknown' do
|
107
|
-
let(:object) { Object.new }
|
108
|
-
|
109
|
-
it 'raises error' do
|
110
|
-
expect { instance.call(target) }.to raise_error(ArgumentError)
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
data/spec/unit/callbacks_spec.rb
DELETED
@@ -1,942 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
RSpec.describe FiniteMachine, 'callbacks' do
|
6
|
-
|
7
|
-
it "triggers default init event" do
|
8
|
-
called = []
|
9
|
-
fsm = FiniteMachine.define do
|
10
|
-
initial :green, defer: true, silent: false
|
11
|
-
|
12
|
-
callbacks {
|
13
|
-
# generic state callbacks
|
14
|
-
on_enter do |event| called << 'on_enter' end
|
15
|
-
on_transition do |event| called << 'on_transition' end
|
16
|
-
on_exit do |event| called << 'on_exit' end
|
17
|
-
|
18
|
-
# generic event callbacks
|
19
|
-
on_before do |event| called << 'on_before' end
|
20
|
-
on_after do |event| called << 'on_after' end
|
21
|
-
|
22
|
-
# state callbacks
|
23
|
-
on_enter :none do |event| called << 'on_enter_none' end
|
24
|
-
on_enter :green do |event| called << 'on_enter_green' end
|
25
|
-
|
26
|
-
on_transition :none do |event| called << 'on_transition_none' end
|
27
|
-
on_transition :green do |event| called << 'on_transition_green' end
|
28
|
-
|
29
|
-
on_exit :none do |event| called << 'on_exit_none' end
|
30
|
-
on_exit :green do |event| called << 'on_exit_green' end
|
31
|
-
|
32
|
-
# event callbacks
|
33
|
-
on_before :init do |event| called << 'on_before_init' end
|
34
|
-
on_after :init do |event| called << 'on_after_init' end
|
35
|
-
}
|
36
|
-
end
|
37
|
-
|
38
|
-
expect(fsm.current).to eql(:none)
|
39
|
-
fsm.init
|
40
|
-
expect(called).to eql([
|
41
|
-
'on_before_init',
|
42
|
-
'on_before',
|
43
|
-
'on_exit_none',
|
44
|
-
'on_exit',
|
45
|
-
'on_transition_green',
|
46
|
-
'on_transition',
|
47
|
-
'on_enter_green',
|
48
|
-
'on_enter',
|
49
|
-
'on_after_init',
|
50
|
-
'on_after'
|
51
|
-
])
|
52
|
-
end
|
53
|
-
|
54
|
-
it "executes callbacks in order" do
|
55
|
-
called = []
|
56
|
-
fsm = FiniteMachine.define do
|
57
|
-
initial :green, silent: false
|
58
|
-
|
59
|
-
events {
|
60
|
-
event :slow, :green => :yellow
|
61
|
-
event :stop, :yellow => :red
|
62
|
-
event :ready, :red => :yellow
|
63
|
-
event :go, :yellow => :green
|
64
|
-
}
|
65
|
-
|
66
|
-
callbacks {
|
67
|
-
# generic callbacks
|
68
|
-
on_enter do |event| called << 'on_enter' end
|
69
|
-
on_transition do |event| called << 'on_transition' end
|
70
|
-
on_exit do |event| called << 'on_exit' end
|
71
|
-
|
72
|
-
on_before do |event| called << 'on_before' end
|
73
|
-
on_after do |event| called << 'on_after' end
|
74
|
-
|
75
|
-
# state callbacks
|
76
|
-
on_enter :green do |event| called << 'on_enter_green' end
|
77
|
-
on_enter :yellow do |event| called << "on_enter_yellow" end
|
78
|
-
on_enter :red do |event| called << "on_enter_red" end
|
79
|
-
|
80
|
-
on_transition :green do |event| called << 'on_transition_green' end
|
81
|
-
on_transition :yellow do |event| called << "on_transition_yellow" end
|
82
|
-
on_transition :red do |event| called << "on_transition_red" end
|
83
|
-
|
84
|
-
on_exit :green do |event| called << 'on_exit_green' end
|
85
|
-
on_exit :yellow do |event| called << "on_exit_yellow" end
|
86
|
-
on_exit :red do |event| called << "on_exit_red" end
|
87
|
-
|
88
|
-
# event callbacks
|
89
|
-
on_before :slow do |event| called << 'on_before_slow' end
|
90
|
-
on_before :stop do |event| called << "on_before_stop" end
|
91
|
-
on_before :ready do |event| called << "on_before_ready" end
|
92
|
-
on_before :go do |event| called << "on_before_go" end
|
93
|
-
|
94
|
-
on_after :slow do |event| called << 'on_after_slow' end
|
95
|
-
on_after :stop do |event| called << "on_after_stop" end
|
96
|
-
on_after :ready do |event| called << "on_after_ready" end
|
97
|
-
on_after :go do |event| called << "on_after_go" end
|
98
|
-
}
|
99
|
-
end
|
100
|
-
|
101
|
-
expect(fsm.current).to eq(:green)
|
102
|
-
expect(called).to eq([
|
103
|
-
'on_before',
|
104
|
-
'on_exit',
|
105
|
-
'on_transition_green',
|
106
|
-
'on_transition',
|
107
|
-
'on_enter_green',
|
108
|
-
'on_enter',
|
109
|
-
'on_after'
|
110
|
-
])
|
111
|
-
|
112
|
-
called = []
|
113
|
-
fsm.slow
|
114
|
-
expect(called).to eql([
|
115
|
-
'on_before_slow',
|
116
|
-
'on_before',
|
117
|
-
'on_exit_green',
|
118
|
-
'on_exit',
|
119
|
-
'on_transition_yellow',
|
120
|
-
'on_transition',
|
121
|
-
'on_enter_yellow',
|
122
|
-
'on_enter',
|
123
|
-
'on_after_slow',
|
124
|
-
'on_after'
|
125
|
-
])
|
126
|
-
|
127
|
-
called = []
|
128
|
-
fsm.stop
|
129
|
-
expect(called).to eql([
|
130
|
-
'on_before_stop',
|
131
|
-
'on_before',
|
132
|
-
'on_exit_yellow',
|
133
|
-
'on_exit',
|
134
|
-
'on_transition_red',
|
135
|
-
'on_transition',
|
136
|
-
'on_enter_red',
|
137
|
-
'on_enter',
|
138
|
-
'on_after_stop',
|
139
|
-
'on_after'
|
140
|
-
])
|
141
|
-
|
142
|
-
called = []
|
143
|
-
fsm.ready
|
144
|
-
expect(called).to eql([
|
145
|
-
'on_before_ready',
|
146
|
-
'on_before',
|
147
|
-
'on_exit_red',
|
148
|
-
'on_exit',
|
149
|
-
'on_transition_yellow',
|
150
|
-
'on_transition',
|
151
|
-
'on_enter_yellow',
|
152
|
-
'on_enter',
|
153
|
-
'on_after_ready',
|
154
|
-
'on_after'
|
155
|
-
])
|
156
|
-
|
157
|
-
called = []
|
158
|
-
fsm.go
|
159
|
-
expect(called).to eql([
|
160
|
-
'on_before_go',
|
161
|
-
'on_before',
|
162
|
-
'on_exit_yellow',
|
163
|
-
'on_exit',
|
164
|
-
'on_transition_green',
|
165
|
-
'on_transition',
|
166
|
-
'on_enter_green',
|
167
|
-
'on_enter',
|
168
|
-
'on_after_go',
|
169
|
-
'on_after'
|
170
|
-
])
|
171
|
-
end
|
172
|
-
|
173
|
-
it "maintains transition execution sequence from UML statechart" do
|
174
|
-
called = []
|
175
|
-
fsm = FiniteMachine.define do
|
176
|
-
initial :previous, silent: false
|
177
|
-
|
178
|
-
events {
|
179
|
-
event :go, :previous => :next, if: -> { called << 'guard'; true}
|
180
|
-
}
|
181
|
-
|
182
|
-
callbacks {
|
183
|
-
on_exit { |event| called << "exit_#{event.from}" }
|
184
|
-
on_before { |event| called << "before_#{event.name}" }
|
185
|
-
on_transition { |event| called << "transition_#{event.from}_#{event.to}"}
|
186
|
-
on_enter { |event| called << "enter_#{event.to}"}
|
187
|
-
on_after { |event| called << "after_#{event.name}" }
|
188
|
-
}
|
189
|
-
end
|
190
|
-
expect(fsm.current).to eq(:previous)
|
191
|
-
fsm.go
|
192
|
-
expect(called).to eq([
|
193
|
-
'before_init',
|
194
|
-
'exit_none',
|
195
|
-
'transition_none_previous',
|
196
|
-
'enter_previous',
|
197
|
-
'after_init',
|
198
|
-
'before_go',
|
199
|
-
'guard',
|
200
|
-
'exit_previous',
|
201
|
-
'transition_previous_next',
|
202
|
-
'enter_next',
|
203
|
-
'after_go'
|
204
|
-
])
|
205
|
-
end
|
206
|
-
|
207
|
-
it "allows multiple callbacks for the same state" do
|
208
|
-
called = []
|
209
|
-
fsm = FiniteMachine.define do
|
210
|
-
initial :green, silent: false
|
211
|
-
|
212
|
-
events {
|
213
|
-
event :slow, :green => :yellow
|
214
|
-
event :stop, :yellow => :red
|
215
|
-
event :ready, :red => :yellow
|
216
|
-
event :go, :yellow => :green
|
217
|
-
}
|
218
|
-
|
219
|
-
callbacks {
|
220
|
-
# generic state callbacks
|
221
|
-
on_enter do |event| called << 'on_enter' end
|
222
|
-
on_transition do |event| called << 'on_transition' end
|
223
|
-
on_exit do |event| called << 'on_exit' end
|
224
|
-
|
225
|
-
# generic event callbacks
|
226
|
-
on_before do |event| called << 'on_before' end
|
227
|
-
on_after do |event| called << 'on_after' end
|
228
|
-
|
229
|
-
# state callbacks
|
230
|
-
on_exit :green do |event| called << 'on_exit_green_1' end
|
231
|
-
on_exit :green do |event| called << 'on_exit_green_2' end
|
232
|
-
on_enter :yellow do |event| called << 'on_enter_yellow_1' end
|
233
|
-
on_enter :yellow do |event| called << 'on_enter_yellow_2' end
|
234
|
-
on_transition :yellow do |event| called << 'on_transition_yellow_1' end
|
235
|
-
on_transition :yellow do |event| called << 'on_transition_yellow_2' end
|
236
|
-
|
237
|
-
# event callbacks
|
238
|
-
on_before :slow do |event| called << 'on_before_slow_1' end
|
239
|
-
on_before :slow do |event| called << 'on_before_slow_2' end
|
240
|
-
on_after :slow do |event| called << 'on_after_slow_1' end
|
241
|
-
on_after :slow do |event| called << 'on_after_slow_2' end
|
242
|
-
}
|
243
|
-
end
|
244
|
-
|
245
|
-
expect(fsm.current).to eql(:green)
|
246
|
-
expect(called).to eql([
|
247
|
-
'on_before',
|
248
|
-
'on_exit',
|
249
|
-
'on_transition',
|
250
|
-
'on_enter',
|
251
|
-
'on_after'
|
252
|
-
])
|
253
|
-
called = []
|
254
|
-
fsm.slow
|
255
|
-
expect(fsm.current).to eql(:yellow)
|
256
|
-
expect(called).to eql([
|
257
|
-
'on_before_slow_1',
|
258
|
-
'on_before_slow_2',
|
259
|
-
'on_before',
|
260
|
-
'on_exit_green_1',
|
261
|
-
'on_exit_green_2',
|
262
|
-
'on_exit',
|
263
|
-
'on_transition_yellow_1',
|
264
|
-
'on_transition_yellow_2',
|
265
|
-
'on_transition',
|
266
|
-
'on_enter_yellow_1',
|
267
|
-
'on_enter_yellow_2',
|
268
|
-
'on_enter',
|
269
|
-
'on_after_slow_1',
|
270
|
-
'on_after_slow_2',
|
271
|
-
'on_after'
|
272
|
-
])
|
273
|
-
end
|
274
|
-
|
275
|
-
it "allows for fluid callback definition" do
|
276
|
-
called = []
|
277
|
-
fsm = FiniteMachine.define do
|
278
|
-
initial :green
|
279
|
-
|
280
|
-
events {
|
281
|
-
event :slow, :green => :yellow
|
282
|
-
event :stop, :yellow => :red
|
283
|
-
event :ready, :red => :yellow
|
284
|
-
event :go, :yellow => :green
|
285
|
-
}
|
286
|
-
|
287
|
-
callbacks {
|
288
|
-
# state callbacks
|
289
|
-
on_exit_green do |event| called << 'on_exit_green' end
|
290
|
-
on_enter_yellow do |event| called << 'on_enter_yellow' end
|
291
|
-
on_transition_yellow do |event| called << 'on_transition_yellow' end
|
292
|
-
|
293
|
-
# event callbacks
|
294
|
-
on_before_slow do |event| called << 'on_before_slow' end
|
295
|
-
on_after_slow do |event| called << 'on_after_slow' end
|
296
|
-
}
|
297
|
-
end
|
298
|
-
|
299
|
-
called = []
|
300
|
-
fsm.slow
|
301
|
-
expect(fsm.current).to eql(:yellow)
|
302
|
-
expect(called).to eql([
|
303
|
-
'on_before_slow',
|
304
|
-
'on_exit_green',
|
305
|
-
'on_transition_yellow',
|
306
|
-
'on_enter_yellow',
|
307
|
-
'on_after_slow'
|
308
|
-
])
|
309
|
-
end
|
310
|
-
|
311
|
-
it "passes event object to callback" do
|
312
|
-
evt = nil
|
313
|
-
fsm = FiniteMachine.define do
|
314
|
-
initial :green
|
315
|
-
|
316
|
-
events {
|
317
|
-
event :slow, :green => :yellow
|
318
|
-
}
|
319
|
-
|
320
|
-
callbacks {
|
321
|
-
on_enter(:yellow) { |e| evt = e }
|
322
|
-
}
|
323
|
-
end
|
324
|
-
|
325
|
-
expect(fsm.current).to eql(:green)
|
326
|
-
fsm.slow
|
327
|
-
expect(fsm.current).to eql(:yellow)
|
328
|
-
|
329
|
-
expect(evt.from).to eql(:green)
|
330
|
-
expect(evt.to).to eql(:yellow)
|
331
|
-
expect(evt.name).to eql(:slow)
|
332
|
-
end
|
333
|
-
|
334
|
-
it "identifies the from state for callback event parameter" do
|
335
|
-
evt = nil
|
336
|
-
fsm = FiniteMachine.define do
|
337
|
-
initial :green
|
338
|
-
|
339
|
-
events {
|
340
|
-
event :slow, [:red, :blue, :green] => :yellow
|
341
|
-
event :fast, :red => :purple
|
342
|
-
}
|
343
|
-
|
344
|
-
callbacks {
|
345
|
-
on_enter(:yellow) { |e| evt = e }
|
346
|
-
}
|
347
|
-
end
|
348
|
-
|
349
|
-
expect(fsm.current).to eql(:green)
|
350
|
-
fsm.slow
|
351
|
-
expect(fsm.current).to eql(:yellow)
|
352
|
-
|
353
|
-
expect(evt.from).to eql(:green)
|
354
|
-
expect(evt.to).to eql(:yellow)
|
355
|
-
expect(evt.name).to eql(:slow)
|
356
|
-
end
|
357
|
-
|
358
|
-
it "passes extra parameters to callbacks" do
|
359
|
-
expected = {name: :init, from: :none, to: :green, a: nil, b: nil, c: nil }
|
360
|
-
|
361
|
-
callback = Proc.new { |event, a, b, c|
|
362
|
-
target.expect(event.from).to target.eql(expected[:from])
|
363
|
-
target.expect(event.to).to target.eql(expected[:to])
|
364
|
-
target.expect(event.name).to target.eql(expected[:name])
|
365
|
-
target.expect(a).to target.eql(expected[:a])
|
366
|
-
target.expect(b).to target.eql(expected[:b])
|
367
|
-
target.expect(c).to target.eql(expected[:c])
|
368
|
-
}
|
369
|
-
context = self
|
370
|
-
|
371
|
-
fsm = FiniteMachine.define do
|
372
|
-
initial :green
|
373
|
-
|
374
|
-
target context
|
375
|
-
|
376
|
-
events {
|
377
|
-
event :slow, :green => :yellow
|
378
|
-
event :stop, :yellow => :red
|
379
|
-
event :ready, :red => :yellow
|
380
|
-
event :go, :yellow => :green
|
381
|
-
}
|
382
|
-
|
383
|
-
callbacks {
|
384
|
-
# generic state callbacks
|
385
|
-
on_enter(&callback)
|
386
|
-
on_transition(&callback)
|
387
|
-
on_exit(&callback)
|
388
|
-
|
389
|
-
# generic event callbacks
|
390
|
-
on_before(&callback)
|
391
|
-
on_after(&callback)
|
392
|
-
|
393
|
-
# state callbacks
|
394
|
-
on_enter :green, &callback
|
395
|
-
on_enter :yellow, &callback
|
396
|
-
on_enter :red, &callback
|
397
|
-
|
398
|
-
on_transition :green , &callback
|
399
|
-
on_transition :yellow, &callback
|
400
|
-
on_transition :red , &callback
|
401
|
-
|
402
|
-
on_exit :green , &callback
|
403
|
-
on_exit :yellow, &callback
|
404
|
-
on_exit :red , &callback
|
405
|
-
|
406
|
-
# event callbacks
|
407
|
-
on_before :slow , &callback
|
408
|
-
on_before :stop , &callback
|
409
|
-
on_before :ready, &callback
|
410
|
-
on_before :go , &callback
|
411
|
-
|
412
|
-
on_after :slow , &callback
|
413
|
-
on_after :stop , &callback
|
414
|
-
on_after :ready, &callback
|
415
|
-
on_after :go , &callback
|
416
|
-
}
|
417
|
-
end
|
418
|
-
|
419
|
-
expected = {name: :slow, from: :green, to: :yellow, a: 1, b: 2, c: 3}
|
420
|
-
fsm.slow(1, 2, 3)
|
421
|
-
|
422
|
-
expected = {name: :stop, from: :yellow, to: :red, a: 'foo', b: 'bar'}
|
423
|
-
fsm.stop('foo', 'bar')
|
424
|
-
|
425
|
-
expected = {name: :ready, from: :red, to: :yellow, a: :foo, b: :bar}
|
426
|
-
fsm.ready(:foo, :bar)
|
427
|
-
|
428
|
-
expected = {name: :go, from: :yellow, to: :green, a: nil, b: nil}
|
429
|
-
fsm.go(nil, nil)
|
430
|
-
end
|
431
|
-
|
432
|
-
it "sets callback parameters correctly for transition from :any state" do
|
433
|
-
expected = {name: :init, from: :none, to: :green, a: nil, b: nil, c: nil }
|
434
|
-
|
435
|
-
callback = Proc.new { |event, a, b, c|
|
436
|
-
target.expect(event.from).to target.eql(expected[:from])
|
437
|
-
target.expect(event.to).to target.eql(expected[:to])
|
438
|
-
target.expect(event.name).to target.eql(expected[:name])
|
439
|
-
target.expect(a).to target.eql(expected[:a])
|
440
|
-
target.expect(b).to target.eql(expected[:b])
|
441
|
-
target.expect(c).to target.eql(expected[:c])
|
442
|
-
}
|
443
|
-
|
444
|
-
context = self
|
445
|
-
|
446
|
-
fsm = FiniteMachine.define do
|
447
|
-
initial :red
|
448
|
-
|
449
|
-
target context
|
450
|
-
|
451
|
-
events {
|
452
|
-
event :power_on, :off => :red
|
453
|
-
event :power_off, :any => :off
|
454
|
-
event :go, :red => :green
|
455
|
-
event :slow, :green => :yellow
|
456
|
-
event :stop, :yellow => :red
|
457
|
-
}
|
458
|
-
|
459
|
-
callbacks {
|
460
|
-
# generic state callbacks
|
461
|
-
on_enter(&callback)
|
462
|
-
on_transition(&callback)
|
463
|
-
on_exit(&callback)
|
464
|
-
|
465
|
-
# generic event callbacks
|
466
|
-
on_before(&callback)
|
467
|
-
on_after(&callback)
|
468
|
-
|
469
|
-
# state callbacks
|
470
|
-
on_enter :green, &callback
|
471
|
-
on_enter :yellow, &callback
|
472
|
-
on_enter :red, &callback
|
473
|
-
on_enter :off, &callback
|
474
|
-
on_enter :off, &callback
|
475
|
-
|
476
|
-
on_transition :green, &callback
|
477
|
-
on_transition :yellow, &callback
|
478
|
-
on_transition :red, &callback
|
479
|
-
on_transition :off, &callback
|
480
|
-
on_transition :off, &callback
|
481
|
-
|
482
|
-
on_exit :green, &callback
|
483
|
-
on_exit :yellow, &callback
|
484
|
-
on_exit :red, &callback
|
485
|
-
on_exit :off, &callback
|
486
|
-
on_exit :off, &callback
|
487
|
-
|
488
|
-
# event callbacks
|
489
|
-
on_before :power_on, &callback
|
490
|
-
on_before :power_off, &callback
|
491
|
-
on_before :go, &callback
|
492
|
-
on_before :slow, &callback
|
493
|
-
on_before :stop, &callback
|
494
|
-
|
495
|
-
on_after :power_on, &callback
|
496
|
-
on_after :power_off, &callback
|
497
|
-
on_after :go, &callback
|
498
|
-
on_after :slow, &callback
|
499
|
-
on_after :stop, &callback
|
500
|
-
}
|
501
|
-
end
|
502
|
-
|
503
|
-
expect(fsm.current).to eq(:red)
|
504
|
-
|
505
|
-
expected = {name: :go, from: :red, to: :green, a: 1, b: 2, c: 3 }
|
506
|
-
fsm.go(1, 2, 3)
|
507
|
-
|
508
|
-
expected = {name: :slow, from: :green, to: :yellow, a: 4, b: 5, c: 6}
|
509
|
-
fsm.slow(4, 5, 6)
|
510
|
-
|
511
|
-
expected = {name: :stop, from: :yellow, to: :red, a: 7, b: 8, c: 9}
|
512
|
-
fsm.stop(7, 8, 9)
|
513
|
-
|
514
|
-
expected = {name: :power_off, from: :red, to: :off, a: 10, b: 11, c: 12}
|
515
|
-
fsm.power_off(10, 11, 12)
|
516
|
-
end
|
517
|
-
|
518
|
-
it "raises an error with invalid callback name" do
|
519
|
-
expect {
|
520
|
-
FiniteMachine.define do
|
521
|
-
initial :green
|
522
|
-
|
523
|
-
events {
|
524
|
-
event :slow, :green => :yellow
|
525
|
-
}
|
526
|
-
|
527
|
-
callbacks {
|
528
|
-
on_enter(:magic) { |event| called << 'on_enter'}
|
529
|
-
}
|
530
|
-
end
|
531
|
-
}.to raise_error(FiniteMachine::InvalidCallbackNameError, /\"magic\" is not a valid callback name/)
|
532
|
-
end
|
533
|
-
|
534
|
-
it "doesn't allow to mix state callback with event name" do
|
535
|
-
expect {
|
536
|
-
FiniteMachine.define do
|
537
|
-
events { event :slow, :green => :yellow }
|
538
|
-
|
539
|
-
callbacks { on_enter_slow do |event| end }
|
540
|
-
end
|
541
|
-
}.to raise_error(FiniteMachine::InvalidCallbackNameError, "\"on_enter\" callback is a state listener and cannot be used with \"slow\" event name. Please use on_before or on_after instead.")
|
542
|
-
end
|
543
|
-
|
544
|
-
it "doesn't allow to mix event callback with state name" do
|
545
|
-
expect {
|
546
|
-
FiniteMachine.define do
|
547
|
-
events { event :slow, :green => :yellow }
|
548
|
-
|
549
|
-
callbacks { on_before_green do |event| end }
|
550
|
-
end
|
551
|
-
}.to raise_error(FiniteMachine::InvalidCallbackNameError, '"on_before" callback is an event listener and cannot be used with "green" state name. Please use on_enter, on_transition or on_exit instead.')
|
552
|
-
end
|
553
|
-
|
554
|
-
it "propagates exceptions raised inside callback" do
|
555
|
-
fsm = FiniteMachine.define do
|
556
|
-
initial :green
|
557
|
-
|
558
|
-
events { event :slow, :green => :yellow }
|
559
|
-
|
560
|
-
callbacks { on_enter(:yellow) { raise RuntimeError } }
|
561
|
-
end
|
562
|
-
|
563
|
-
expect(fsm.current).to eql(:green)
|
564
|
-
expect { fsm.slow }.to raise_error(RuntimeError)
|
565
|
-
end
|
566
|
-
|
567
|
-
it "executes callbacks with multiple 'from' transitions" do
|
568
|
-
called = []
|
569
|
-
fsm = FiniteMachine.define do
|
570
|
-
initial :green
|
571
|
-
|
572
|
-
events {
|
573
|
-
event :stop, :green => :yellow
|
574
|
-
event :stop, :yellow => :red
|
575
|
-
}
|
576
|
-
|
577
|
-
callbacks {
|
578
|
-
on_before_stop do |event|
|
579
|
-
called << 'on_before_stop'
|
580
|
-
end
|
581
|
-
}
|
582
|
-
end
|
583
|
-
expect(fsm.current).to eql(:green)
|
584
|
-
fsm.stop
|
585
|
-
expect(fsm.current).to eql(:yellow)
|
586
|
-
fsm.stop
|
587
|
-
expect(fsm.current).to eql(:red)
|
588
|
-
expect(called).to eql([
|
589
|
-
'on_before_stop',
|
590
|
-
'on_before_stop'
|
591
|
-
])
|
592
|
-
end
|
593
|
-
|
594
|
-
it "allows to define callbacks on machine instance" do
|
595
|
-
called = []
|
596
|
-
fsm = FiniteMachine.define do
|
597
|
-
initial :green
|
598
|
-
|
599
|
-
events {
|
600
|
-
event :slow, :green => :yellow
|
601
|
-
event :stop, :yellow => :red
|
602
|
-
event :ready, :red => :yellow
|
603
|
-
event :go, :yellow => :green
|
604
|
-
}
|
605
|
-
end
|
606
|
-
|
607
|
-
fsm.on_enter_yellow do |event|
|
608
|
-
called << 'on_enter_yellow'
|
609
|
-
end
|
610
|
-
|
611
|
-
expect(fsm.current).to eql(:green)
|
612
|
-
fsm.slow
|
613
|
-
expect(called).to eql([
|
614
|
-
'on_enter_yellow'
|
615
|
-
])
|
616
|
-
end
|
617
|
-
|
618
|
-
it "raises error for unknown callback" do
|
619
|
-
expect { FiniteMachine.define do
|
620
|
-
initial :green
|
621
|
-
|
622
|
-
events {
|
623
|
-
event :slow, :green => :yellow
|
624
|
-
event :stop, :yellow => :red
|
625
|
-
event :ready, :red => :yellow
|
626
|
-
event :go, :yellow => :green
|
627
|
-
}
|
628
|
-
|
629
|
-
callbacks {
|
630
|
-
on_enter_unknown do |event| end
|
631
|
-
}
|
632
|
-
end }.to raise_error(NoMethodError)
|
633
|
-
end
|
634
|
-
|
635
|
-
it "triggers callbacks only once" do
|
636
|
-
called = []
|
637
|
-
fsm = FiniteMachine.define do
|
638
|
-
initial :green, silent: false
|
639
|
-
|
640
|
-
events {
|
641
|
-
event :slow, :green => :yellow
|
642
|
-
event :go, :yellow => :green
|
643
|
-
}
|
644
|
-
|
645
|
-
callbacks {
|
646
|
-
# state callbacks
|
647
|
-
once_on_enter_green do |event| called << 'once_on_enter_green' end
|
648
|
-
once_on_enter_yellow do |event| called << 'once_on_enter_yellow' end
|
649
|
-
|
650
|
-
once_on_transition_green do |event| called << 'once_on_transition_green' end
|
651
|
-
once_on_transition_yellow do |event| called << 'once_on_transition_yellow' end
|
652
|
-
once_on_exit_none do |event| called << 'once_on_exit_none' end
|
653
|
-
once_on_exit_green do |event| called << 'once_on_exit_green' end
|
654
|
-
once_on_exit_yellow do |event| called << 'once_on_exit_yellow' end
|
655
|
-
|
656
|
-
# event callbacks
|
657
|
-
once_on_before_init do |event| called << 'once_on_before_init' end
|
658
|
-
once_on_before_slow do |event| called << 'once_on_before_slow' end
|
659
|
-
once_on_before_go do |event| called << 'once_on_before_go' end
|
660
|
-
|
661
|
-
once_on_after_init do |event| called << 'once_on_after_init' end
|
662
|
-
once_on_after_slow do |event| called << 'once_on_after_slow' end
|
663
|
-
once_on_after_go do |event| called << 'once_on_after_go' end
|
664
|
-
}
|
665
|
-
end
|
666
|
-
expect(fsm.current).to eql(:green)
|
667
|
-
fsm.slow
|
668
|
-
expect(fsm.current).to eql(:yellow)
|
669
|
-
fsm.go
|
670
|
-
expect(fsm.current).to eql(:green)
|
671
|
-
fsm.slow
|
672
|
-
expect(fsm.current).to eql(:yellow)
|
673
|
-
expect(called).to eql([
|
674
|
-
'once_on_before_init',
|
675
|
-
'once_on_exit_none',
|
676
|
-
'once_on_transition_green',
|
677
|
-
'once_on_enter_green',
|
678
|
-
'once_on_after_init',
|
679
|
-
'once_on_before_slow',
|
680
|
-
'once_on_exit_green',
|
681
|
-
'once_on_transition_yellow',
|
682
|
-
'once_on_enter_yellow',
|
683
|
-
'once_on_after_slow',
|
684
|
-
'once_on_before_go',
|
685
|
-
'once_on_exit_yellow',
|
686
|
-
'once_on_after_go'
|
687
|
-
])
|
688
|
-
end
|
689
|
-
|
690
|
-
it "cancels transition on event callback" do
|
691
|
-
fsm = FiniteMachine.define do
|
692
|
-
initial :green
|
693
|
-
|
694
|
-
events {
|
695
|
-
event :slow, :green => :yellow
|
696
|
-
event :go, :yellow => :green
|
697
|
-
}
|
698
|
-
|
699
|
-
callbacks {
|
700
|
-
on_exit :green do |event|
|
701
|
-
FiniteMachine::CANCELLED
|
702
|
-
end
|
703
|
-
}
|
704
|
-
end
|
705
|
-
|
706
|
-
expect(fsm.current).to eql(:green)
|
707
|
-
fsm.slow
|
708
|
-
expect(fsm.current).to eql(:green)
|
709
|
-
end
|
710
|
-
|
711
|
-
it "stops executing callbacks when cancelled" do
|
712
|
-
called = []
|
713
|
-
|
714
|
-
fsm = FiniteMachine.define do
|
715
|
-
initial :initial
|
716
|
-
|
717
|
-
events { event :bump, initial: :low }
|
718
|
-
|
719
|
-
callbacks {
|
720
|
-
on_before do |event|
|
721
|
-
called << "enter_#{event.name}_#{event.from}_#{event.to}"
|
722
|
-
|
723
|
-
FiniteMachine::CANCELLED
|
724
|
-
end
|
725
|
-
|
726
|
-
on_exit :initial do |event| called << "exit_initial" end
|
727
|
-
on_exit do |event| called << "exit_any" end
|
728
|
-
on_enter :low do |event| called << "enter_low" end
|
729
|
-
on_after :bump do |event| called << "after_#{event.name}" end
|
730
|
-
on_after do |event| called << "after_any" end
|
731
|
-
}
|
732
|
-
end
|
733
|
-
|
734
|
-
fsm.bump
|
735
|
-
|
736
|
-
expect(called).to eq(['enter_bump_initial_low'])
|
737
|
-
end
|
738
|
-
|
739
|
-
xit "groups callbacks"
|
740
|
-
|
741
|
-
it "groups states from separate events with the same name" do
|
742
|
-
callbacks = []
|
743
|
-
fsm = FiniteMachine.define do
|
744
|
-
initial :initial, silent: false
|
745
|
-
|
746
|
-
events {
|
747
|
-
event :bump, :initial => :low
|
748
|
-
event :bump, :low => :medium
|
749
|
-
event :bump, :medium => :high
|
750
|
-
}
|
751
|
-
|
752
|
-
callbacks {
|
753
|
-
on_enter do |event|
|
754
|
-
callbacks << "enter_#{event.name}_#{event.from}_#{event.to}"
|
755
|
-
end
|
756
|
-
on_exit do |event|
|
757
|
-
callbacks << "exit_#{event.name}_#{event.from}_#{event.to}"
|
758
|
-
end
|
759
|
-
on_before do |event|
|
760
|
-
callbacks << "before_#{event.name}_#{event.from}_#{event.to}"
|
761
|
-
end
|
762
|
-
on_after do |event|
|
763
|
-
callbacks << "after_#{event.name}_#{event.from}_#{event.to}"
|
764
|
-
end
|
765
|
-
}
|
766
|
-
end
|
767
|
-
expect(fsm.current).to eq(:initial)
|
768
|
-
fsm.bump
|
769
|
-
expect(callbacks).to eq([
|
770
|
-
'before_init_none_initial',
|
771
|
-
'exit_init_none_initial',
|
772
|
-
'enter_init_none_initial',
|
773
|
-
'after_init_none_initial',
|
774
|
-
'before_bump_initial_low',
|
775
|
-
'exit_bump_initial_low',
|
776
|
-
'enter_bump_initial_low',
|
777
|
-
'after_bump_initial_low'
|
778
|
-
])
|
779
|
-
fsm.bump
|
780
|
-
expect(callbacks).to eq([
|
781
|
-
'before_init_none_initial',
|
782
|
-
'exit_init_none_initial',
|
783
|
-
'enter_init_none_initial',
|
784
|
-
'after_init_none_initial',
|
785
|
-
'before_bump_initial_low',
|
786
|
-
'exit_bump_initial_low',
|
787
|
-
'enter_bump_initial_low',
|
788
|
-
'after_bump_initial_low',
|
789
|
-
'before_bump_low_medium',
|
790
|
-
'exit_bump_low_medium',
|
791
|
-
'enter_bump_low_medium',
|
792
|
-
'after_bump_low_medium'
|
793
|
-
])
|
794
|
-
fsm.bump
|
795
|
-
expect(callbacks).to eq([
|
796
|
-
'before_init_none_initial',
|
797
|
-
'exit_init_none_initial',
|
798
|
-
'enter_init_none_initial',
|
799
|
-
'after_init_none_initial',
|
800
|
-
'before_bump_initial_low',
|
801
|
-
'exit_bump_initial_low',
|
802
|
-
'enter_bump_initial_low',
|
803
|
-
'after_bump_initial_low',
|
804
|
-
'before_bump_low_medium',
|
805
|
-
'exit_bump_low_medium',
|
806
|
-
'enter_bump_low_medium',
|
807
|
-
'after_bump_low_medium',
|
808
|
-
'before_bump_medium_high',
|
809
|
-
'exit_bump_medium_high',
|
810
|
-
'enter_bump_medium_high',
|
811
|
-
'after_bump_medium_high'
|
812
|
-
])
|
813
|
-
end
|
814
|
-
|
815
|
-
it "groups states under event name" do
|
816
|
-
callbacks = []
|
817
|
-
fsm = FiniteMachine.define do
|
818
|
-
initial :initial, silent: false
|
819
|
-
|
820
|
-
events {
|
821
|
-
event :bump, :initial => :low,
|
822
|
-
:low => :medium,
|
823
|
-
:medium => :high
|
824
|
-
}
|
825
|
-
|
826
|
-
callbacks {
|
827
|
-
on_enter do |event|
|
828
|
-
callbacks << "enter_#{event.name}_#{event.from}_#{event.to}"
|
829
|
-
end
|
830
|
-
on_before do |event|
|
831
|
-
callbacks << "before_#{event.name}_#{event.from}_#{event.to}"
|
832
|
-
end
|
833
|
-
}
|
834
|
-
end
|
835
|
-
expect(fsm.current).to eq(:initial)
|
836
|
-
fsm.bump
|
837
|
-
expect(callbacks).to eq([
|
838
|
-
'before_init_none_initial',
|
839
|
-
'enter_init_none_initial',
|
840
|
-
'before_bump_initial_low',
|
841
|
-
'enter_bump_initial_low'
|
842
|
-
])
|
843
|
-
fsm.bump
|
844
|
-
expect(callbacks).to eq([
|
845
|
-
'before_init_none_initial',
|
846
|
-
'enter_init_none_initial',
|
847
|
-
'before_bump_initial_low',
|
848
|
-
'enter_bump_initial_low',
|
849
|
-
'before_bump_low_medium',
|
850
|
-
'enter_bump_low_medium'
|
851
|
-
])
|
852
|
-
fsm.bump
|
853
|
-
expect(callbacks).to eq([
|
854
|
-
'before_init_none_initial',
|
855
|
-
'enter_init_none_initial',
|
856
|
-
'before_bump_initial_low',
|
857
|
-
'enter_bump_initial_low',
|
858
|
-
'before_bump_low_medium',
|
859
|
-
'enter_bump_low_medium',
|
860
|
-
'before_bump_medium_high',
|
861
|
-
'enter_bump_medium_high'
|
862
|
-
])
|
863
|
-
end
|
864
|
-
|
865
|
-
it "permits state and event with the same name" do
|
866
|
-
called = []
|
867
|
-
fsm = FiniteMachine.define do
|
868
|
-
initial :on_hook, silent: false
|
869
|
-
|
870
|
-
events {
|
871
|
-
event :off_hook, :on_hook => :off_hook
|
872
|
-
event :on_hook, :off_hook => :on_hook
|
873
|
-
}
|
874
|
-
|
875
|
-
callbacks {
|
876
|
-
on_before(:on_hook) { |event| called << "on_before_#{event.name}"}
|
877
|
-
on_enter(:on_hook) { |event| called << "on_enter_#{event.to}"}
|
878
|
-
}
|
879
|
-
end
|
880
|
-
expect(fsm.current).to eq(:on_hook)
|
881
|
-
expect(called).to eq([
|
882
|
-
'on_enter_on_hook'
|
883
|
-
])
|
884
|
-
fsm.off_hook
|
885
|
-
expect(fsm.current).to eq(:off_hook)
|
886
|
-
fsm.on_hook
|
887
|
-
expect(called).to eq([
|
888
|
-
'on_enter_on_hook',
|
889
|
-
'on_before_on_hook',
|
890
|
-
'on_enter_on_hook'
|
891
|
-
]);
|
892
|
-
end
|
893
|
-
|
894
|
-
it "allows to selectively silence events" do
|
895
|
-
called = []
|
896
|
-
fsm = FiniteMachine.define do
|
897
|
-
initial :yellow
|
898
|
-
|
899
|
-
events {
|
900
|
-
event :go, :yellow => :green, silent: true
|
901
|
-
event :stop, :green => :red
|
902
|
-
}
|
903
|
-
|
904
|
-
callbacks {
|
905
|
-
on_enter :green do |event| called << 'on_enter_yellow' end
|
906
|
-
on_enter :red do |event| called << 'on_enter_red' end
|
907
|
-
}
|
908
|
-
end
|
909
|
-
expect(fsm.current).to eq(:yellow)
|
910
|
-
fsm.go
|
911
|
-
fsm.stop
|
912
|
-
expect(called).to eq(['on_enter_red'])
|
913
|
-
end
|
914
|
-
|
915
|
-
it "executes event-based callbacks even when state does not change" do
|
916
|
-
called = []
|
917
|
-
fsm = FiniteMachine.define do
|
918
|
-
initial :active
|
919
|
-
|
920
|
-
events {
|
921
|
-
event :advance, :active => :inactive, if: -> { false }
|
922
|
-
event :advance, :inactive => :active, if: -> { false }
|
923
|
-
}
|
924
|
-
|
925
|
-
callbacks {
|
926
|
-
on_before do |event|
|
927
|
-
called << "before_#{event.name}_#{event.from}_#{event.to}"
|
928
|
-
end
|
929
|
-
on_after do |event|
|
930
|
-
called << "after_#{event.name}_#{event.from}_#{event.to}"
|
931
|
-
end
|
932
|
-
}
|
933
|
-
end
|
934
|
-
expect(fsm.current).to eq(:active)
|
935
|
-
fsm.advance
|
936
|
-
expect(fsm.current).to eq(:active)
|
937
|
-
expect(called).to eq([
|
938
|
-
'before_advance_active_inactive',
|
939
|
-
'after_advance_active_inactive'
|
940
|
-
])
|
941
|
-
end
|
942
|
-
end
|