motion-state-machine 0.8.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +19 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +145 -0
- data/Rakefile +31 -0
- data/lib/motion-state-machine.rb +9 -0
- data/lib/motion-state-machine/base.rb +223 -0
- data/lib/motion-state-machine/spec_app_delegate.rb +7 -0
- data/lib/motion-state-machine/state.rb +379 -0
- data/lib/motion-state-machine/transition.rb +407 -0
- data/lib/motion-state-machine/version.rb +3 -0
- data/motion-state-machine.gemspec +19 -0
- data/spec/motion-state-machine/base_spec.rb +118 -0
- data/spec/motion-state-machine/benchmark_spec.rb +74 -0
- data/spec/motion-state-machine/notification_transition_spec.rb +57 -0
- data/spec/motion-state-machine/send_event_transition_spec.rb +53 -0
- data/spec/motion-state-machine/state_spec.rb +219 -0
- data/spec/motion-state-machine/timed_transition_spec.rb +48 -0
- data/spec/motion-state-machine/transition_spec.rb +157 -0
- metadata +90 -0
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/motion-state-machine/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Sebastian Burkhart"]
|
6
|
+
gem.email = ["sebastianburkhart@me.com"]
|
7
|
+
gem.description = %q{A finite state machine for RubyMotion with a flavor of Grand Central Dispatch.}
|
8
|
+
gem.summary = %q{Comes with a nice syntax for state and transition definition. Supports triggering via events, timeouts and NSNotifications.}
|
9
|
+
gem.homepage = "https://github.com/opyh/motion-state-machine"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "motion-state-machine"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = StateMachine::VERSION
|
17
|
+
|
18
|
+
gem.add_development_dependency 'rake'
|
19
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
describe StateMachine::Base do
|
2
|
+
|
3
|
+
describe "#initialize" do
|
4
|
+
|
5
|
+
it "should raise if not given a start state" do
|
6
|
+
lambda {state_machine = StateMachine::Base.new}.
|
7
|
+
should.raise(ArgumentError)
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "when given a start state" do
|
11
|
+
before do
|
12
|
+
@fsm = StateMachine::Base.new start_state: :start
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should initialize and use the internal state dictionary" do
|
16
|
+
dictionary = @fsm.instance_variable_get(:@state_symbols_to_states)
|
17
|
+
dictionary.class.should == Hash
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should create an internal waiting_for_start state" do
|
21
|
+
dictionary = @fsm.instance_variable_get(:@state_symbols_to_states)
|
22
|
+
dictionary.count.should == 2
|
23
|
+
state = dictionary[:waiting_for_start]
|
24
|
+
state.class.should == StateMachine::State
|
25
|
+
state.transition_map.count.should == 1
|
26
|
+
transition = state.transition_map[:on][:start].first
|
27
|
+
transition.options[:from].should == :waiting_for_start
|
28
|
+
transition.options[:to].should ==:start
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should set its current state to :waiting_for_start" do
|
32
|
+
state = @fsm.current_state
|
33
|
+
state.class.should == StateMachine::State
|
34
|
+
state.symbol.should == :waiting_for_start
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "after correct initialization" do
|
41
|
+
|
42
|
+
before do
|
43
|
+
@fsm = StateMachine::Base.new start_state: :awake
|
44
|
+
@fsm.when(:awake) do |state|
|
45
|
+
state.die :on => :terminate
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "#state(symbol, name = nil)" do
|
50
|
+
it "should create & return a new state for the given symbol if not existing" do
|
51
|
+
states_hash = @fsm.instance_variable_get(:@state_symbols_to_states)
|
52
|
+
state_count_before = states_hash.count
|
53
|
+
states_hash.has_key?(:some_other_state).should == false
|
54
|
+
state = @fsm.state(:some_other_state, "Fake State")
|
55
|
+
state.class.should == StateMachine::State
|
56
|
+
states_hash.count.should == state_count_before + 1
|
57
|
+
states_hash.has_key?(:some_other_state).should == true
|
58
|
+
state.name.should == "Fake State"
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should return the state with the given symbol if existing" do
|
62
|
+
states_hash = @fsm.instance_variable_get(:@state_symbols_to_states)
|
63
|
+
state_count_before = states_hash.count
|
64
|
+
states_hash[:foo] = "bar"
|
65
|
+
states_hash.count.should == state_count_before + 1
|
66
|
+
@fsm.state(:foo).should == "bar"
|
67
|
+
states_hash.count.should == state_count_before + 1
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "#start!" do
|
72
|
+
it "should remember the initial queue if called from main queue" do
|
73
|
+
Dispatch::Queue.current.to_s.should == Dispatch::Queue.main.to_s
|
74
|
+
@fsm.start!
|
75
|
+
@fsm.initial_queue.to_s.should == Dispatch::Queue.main.to_s
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should remember the initial queue if called from another queue" do
|
79
|
+
other_queue = Dispatch::Queue.concurrent(:default)
|
80
|
+
other_queue.to_s.should != Dispatch::Queue.main.to_s
|
81
|
+
other_queue.sync do
|
82
|
+
@fsm.start!
|
83
|
+
end
|
84
|
+
@fsm.initial_queue.to_s.should == other_queue.to_s
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should change the current state to the start state" do
|
88
|
+
@fsm.current_state.symbol.should == :waiting_for_start
|
89
|
+
@fsm.start!
|
90
|
+
@fsm.current_state.symbol.should == :awake
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe "#terminated?" do
|
95
|
+
it "should return false until the machine is terminated" do
|
96
|
+
@fsm.current_state.symbol.should == :waiting_for_start
|
97
|
+
@fsm.terminated?.should == false
|
98
|
+
@fsm.start!
|
99
|
+
@fsm.current_state.symbol.should == :awake
|
100
|
+
@fsm.terminated?.should == false
|
101
|
+
@fsm.event(:terminate)
|
102
|
+
@fsm.terminated?.should == true
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# describe "#stop_and_cleanup" do
|
107
|
+
# it "should unregister all notifications"
|
108
|
+
# it "should remove references to self from all registered states"
|
109
|
+
# end
|
110
|
+
|
111
|
+
# it "should not be too slow"
|
112
|
+
#
|
113
|
+
# it "should be thread-safe"
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# Benchmark that checks if the machine is fast enough.
|
2
|
+
# If it should ever happen that some implementation change
|
3
|
+
# slows it down, this spec will be red.
|
4
|
+
|
5
|
+
# Test state machine that loops in 3 states.
|
6
|
+
|
7
|
+
class LoopingThreeStateMachine < StateMachine::Base
|
8
|
+
attr_accessor :steps, :loops
|
9
|
+
attr_accessor :is_dead
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
super(start_state: :first_state)
|
13
|
+
|
14
|
+
@steps = 0
|
15
|
+
@loops = 0
|
16
|
+
|
17
|
+
self.when :first_state do |state|
|
18
|
+
state.transition_to :second_state, on: :next,
|
19
|
+
action: proc { @steps += 1 }
|
20
|
+
end
|
21
|
+
|
22
|
+
self.when :second_state do |state|
|
23
|
+
state.transition_to :third_state, on: :next,
|
24
|
+
action: proc { @steps += 1 }
|
25
|
+
end
|
26
|
+
|
27
|
+
self.when :third_state do |state|
|
28
|
+
state.transition_to :first_state, on: :next,
|
29
|
+
action: proc { @steps += 1; @loops += 1 }
|
30
|
+
end
|
31
|
+
|
32
|
+
self
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe LoopingThreeStateMachine do
|
37
|
+
before do
|
38
|
+
@fsm = LoopingThreeStateMachine.new
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should loop correctly" do
|
42
|
+
@fsm.start!
|
43
|
+
100.times { @fsm.event :next }
|
44
|
+
@fsm.steps.should == 100
|
45
|
+
@fsm.loops.should == @fsm.steps / 3
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should prove that the state machine can handle more than 10k events per second" do
|
49
|
+
other_queue = Dispatch::Queue.new('org.screenfashion.motion-state-machine')
|
50
|
+
other_queue.sync { @fsm.start! }
|
51
|
+
started_on = NSDate.date
|
52
|
+
dispatch_group = Dispatch::Group.new
|
53
|
+
event_count = 100000
|
54
|
+
|
55
|
+
event_count.times do |i|
|
56
|
+
other_queue.async(dispatch_group) { @fsm.event :next }
|
57
|
+
end
|
58
|
+
|
59
|
+
send_time = NSDate.date.timeIntervalSinceDate started_on
|
60
|
+
|
61
|
+
dispatch_group.wait # wait for the events to be handled
|
62
|
+
|
63
|
+
handle_time = NSDate.date.timeIntervalSinceDate started_on
|
64
|
+
|
65
|
+
send_time.should < 0.2
|
66
|
+
|
67
|
+
frequency = event_count / handle_time
|
68
|
+
frequency.should > 10000
|
69
|
+
|
70
|
+
puts "\nNeeded #{send_time}s to send #{event_count} events, #{handle_time}s to handle them."
|
71
|
+
puts "That's a frequency of #{frequency} state changes per second.\n"
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
describe StateMachine::NotificationTransition do
|
2
|
+
|
3
|
+
before do
|
4
|
+
@state_machine = StateMachine::Base.new start_state: :awaiting_notification
|
5
|
+
action = proc {@fired = true}
|
6
|
+
@state_machine.when :awaiting_notification do |state|
|
7
|
+
@transition = state.transition_to(:notified, on_notification: "SomeNotification", action: action).first
|
8
|
+
state.transition_to :canceled, on: :cancel
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should be created correctly" do
|
13
|
+
@transition.should.is_a(StateMachine::NotificationTransition)
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "when running in main queue" do
|
17
|
+
it "should be executed when receiving the notification and unarm correctly" do
|
18
|
+
@fired = false
|
19
|
+
@state_machine.start! # will arm the transition
|
20
|
+
@state_machine.current_state.symbol.should == :awaiting_notification
|
21
|
+
@fired.should == false
|
22
|
+
|
23
|
+
NSNotificationCenter.defaultCenter.postNotificationName "SomeNotification", object: nil
|
24
|
+
@state_machine.current_state.symbol.should == :notified
|
25
|
+
@fired.should == true
|
26
|
+
@fired = false
|
27
|
+
|
28
|
+
NSNotificationCenter.defaultCenter.postNotificationName "SomeNotification", object: nil
|
29
|
+
@state_machine.current_state.symbol.should == :notified
|
30
|
+
@fired.should == false
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "when running in other queue" do
|
35
|
+
it "should be executed when receiving the notification and unarm correctly" do
|
36
|
+
@fired = false
|
37
|
+
|
38
|
+
other_queue = Dispatch::Queue.concurrent
|
39
|
+
other_queue.sync do
|
40
|
+
@state_machine.start! # will arm the transition
|
41
|
+
end
|
42
|
+
@state_machine.current_state.symbol.should == :awaiting_notification
|
43
|
+
@fired.should == false
|
44
|
+
|
45
|
+
NSNotificationCenter.defaultCenter.postNotificationName "SomeNotification", object: nil
|
46
|
+
sleep 0.1
|
47
|
+
@state_machine.current_state.symbol.should == :notified
|
48
|
+
@fired.should == true
|
49
|
+
@fired = false
|
50
|
+
|
51
|
+
NSNotificationCenter.defaultCenter.postNotificationName "SomeNotification", object: nil
|
52
|
+
@state_machine.current_state.symbol.should == :notified
|
53
|
+
@fired.should == false
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
describe StateMachine::SendEventTransition do
|
2
|
+
before do
|
3
|
+
@state_machine = StateMachine::Base.new start_state: :awake
|
4
|
+
@source_state = @state_machine.state :awake
|
5
|
+
@destination_state = @state_machine.state :tired
|
6
|
+
@options = {
|
7
|
+
state_machine: @state_machine,
|
8
|
+
from: :awake,
|
9
|
+
to: :tired,
|
10
|
+
type: :on,
|
11
|
+
on: :work_done
|
12
|
+
}
|
13
|
+
@transition = StateMachine::Transition.make @options
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should correctly register in the factory" do
|
17
|
+
@transition.should.is_a(StateMachine::SendEventTransition)
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "#initialize(options)" do
|
21
|
+
it "should not arm the transition" do
|
22
|
+
@state_machine.event(:work_done)
|
23
|
+
@state_machine.current_state.symbol.should == :waiting_for_start
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#arm" do
|
28
|
+
it "should execute the transition when the event is sent to the state machine" do
|
29
|
+
@state_machine.start!
|
30
|
+
@state_machine.current_state.symbol.should == :awake
|
31
|
+
@state_machine.event(:work_done)
|
32
|
+
@state_machine.current_state.symbol.should == :awake
|
33
|
+
@transition.arm
|
34
|
+
# necessary for the event to work
|
35
|
+
@source_state.register(@transition)
|
36
|
+
@state_machine.event(:work_done)
|
37
|
+
@state_machine.current_state.symbol.should == :tired
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "#unarm" do
|
42
|
+
it "should make sure the transition is not executed when the event is sent to the state machine" do
|
43
|
+
transition = StateMachine::Transition.make @options
|
44
|
+
@state_machine.start!
|
45
|
+
@state_machine.current_state.symbol.should == :awake
|
46
|
+
@transition.unarm
|
47
|
+
@source_state.register(@transition)
|
48
|
+
@state_machine.event(:work_done)
|
49
|
+
@state_machine.current_state.symbol.should == :awake
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,219 @@
|
|
1
|
+
describe StateMachine::State do
|
2
|
+
before do
|
3
|
+
@living = StateMachine::State.new "stub", symbol: :awake, name: "Living"
|
4
|
+
end
|
5
|
+
|
6
|
+
describe "Entering/exiting" do
|
7
|
+
before do
|
8
|
+
# These seem not to be correctly deinitialized after single specs,
|
9
|
+
# so we have to reinitialize them ourselves.
|
10
|
+
|
11
|
+
@entry_action_called = false
|
12
|
+
@other_entry_action_called = false
|
13
|
+
@exit_action_called = false
|
14
|
+
@other_exit_action_called = false
|
15
|
+
|
16
|
+
@state_machine = StateMachine::Base.new start_state: :awake
|
17
|
+
@state_machine.when(:awake) do |state|
|
18
|
+
state.transition_to :tired, on: :work_done
|
19
|
+
state.on_exit do
|
20
|
+
@exit_action_called = true
|
21
|
+
end
|
22
|
+
state.on_exit do
|
23
|
+
@other_exit_action_called = true
|
24
|
+
end
|
25
|
+
end
|
26
|
+
@state_machine.when(:tired) do |state|
|
27
|
+
state.transition_to :very_excited, on: :something_happened
|
28
|
+
state.on_entry do
|
29
|
+
@entry_action_called = true
|
30
|
+
end
|
31
|
+
state.on_entry do
|
32
|
+
@other_entry_action_called = true
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
@state_machine.start!
|
37
|
+
@state_machine.current_state.symbol.should == :awake
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "#enter!" do
|
41
|
+
it "should set the state machine's current state" do
|
42
|
+
@state_machine.state(:tired).send :enter!
|
43
|
+
@state_machine.current_state.symbol.should == :tired
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should execute the entry actions" do
|
47
|
+
@entry_action_called.should == false
|
48
|
+
@other_entry_action_called.should == false
|
49
|
+
@state_machine.event(:work_done)
|
50
|
+
@entry_action_called.should == true
|
51
|
+
@other_entry_action_called.should == true
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should arm the transitions" do
|
55
|
+
@state_machine.event(:something_happened) # should be ignored
|
56
|
+
@state_machine.current_state.symbol.should == :awake
|
57
|
+
@state_machine.event(:work_done)
|
58
|
+
@state_machine.current_state.symbol.should == :tired
|
59
|
+
@state_machine.event(:something_happened)
|
60
|
+
@state_machine.current_state.symbol.should == :very_excited
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "#exit!" do
|
66
|
+
it "should set the state machine's current state to nil" do
|
67
|
+
@state_machine.state(:awake).send :exit!
|
68
|
+
@state_machine.current_state.should == nil
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should execute the exit actions" do
|
72
|
+
@exit_action_called.should == false
|
73
|
+
@other_exit_action_called.should == false
|
74
|
+
@state_machine.event(:work_done)
|
75
|
+
@exit_action_called.should == true
|
76
|
+
@other_exit_action_called.should == true
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should unarm the transitions" do
|
80
|
+
@state_machine.state(:awake).send :exit!
|
81
|
+
@state_machine.event(:work_done) # should be ignored
|
82
|
+
@state_machine.current_state.should == nil
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
describe "#guarded_execute(options)" do
|
89
|
+
before do
|
90
|
+
@state_machine = StateMachine::Base.new start_state: :awake
|
91
|
+
end
|
92
|
+
it "should not do anything when on a terminating state" do
|
93
|
+
@state_machine.when :awake do |state|
|
94
|
+
state.die on: :kill
|
95
|
+
end
|
96
|
+
@state_machine.start!
|
97
|
+
@state_machine.current_state.symbol.should == :awake
|
98
|
+
@state_machine.current_state.send :guarded_execute, :on, :kill
|
99
|
+
@state_machine.current_state.should.be.terminating
|
100
|
+
end
|
101
|
+
|
102
|
+
describe ":if and :unless guards" do
|
103
|
+
|
104
|
+
# if/unless logic table is tested in transition specs
|
105
|
+
|
106
|
+
it "should raise if multiple non-guarded transitions would be possible for the same event" do
|
107
|
+
@state_machine.when :awake do |state|
|
108
|
+
state.transition_to :state2, on: :work_done
|
109
|
+
state.transition_to :state3, on: :work_done
|
110
|
+
end
|
111
|
+
@state_machine.start!
|
112
|
+
lambda {@state_machine.current_state.send :guarded_execute, :on, :work_done}.should.raise RuntimeError
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should raise if multiple guarded transitions would be possible for the same event" do
|
116
|
+
@state_machine.when :awake do |state|
|
117
|
+
state.transition_to :state2, on: :work_done, :if => proc { true }
|
118
|
+
state.transition_to :state3, on: :work_done, :if => proc { true }
|
119
|
+
end
|
120
|
+
@state_machine.start!
|
121
|
+
lambda {@state_machine.current_state.send :guarded_execute, :on, :work_done}.should.raise RuntimeError
|
122
|
+
end
|
123
|
+
|
124
|
+
it "should execute right transition of multiple are allowed" do
|
125
|
+
@state_machine.when :awake do |state|
|
126
|
+
state.transition_to :state2, on: :work_done, :if => proc { false }
|
127
|
+
state.transition_to :state3, on: :work_done, :if => proc { true }
|
128
|
+
end
|
129
|
+
@state_machine.start!
|
130
|
+
@state_machine.current_state.send :guarded_execute, :on, :work_done
|
131
|
+
@state_machine.current_state.symbol.should == :state3
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should not execute any transition if all are disallowed" do
|
135
|
+
@state_machine.when :awake do |state|
|
136
|
+
state.transition_to :state2, on: :work_done, :if => proc { false }
|
137
|
+
state.transition_to :state3, on: :work_done, :if => proc { false }
|
138
|
+
end
|
139
|
+
@state_machine.start!
|
140
|
+
@state_machine.current_state.send :guarded_execute, :on, :work_done
|
141
|
+
@state_machine.current_state.symbol.should == :awake
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
|
148
|
+
describe "Definition DSL" do
|
149
|
+
before do
|
150
|
+
@state_machine = StateMachine::Base.new start_state: :awake
|
151
|
+
end
|
152
|
+
|
153
|
+
describe "#transition_to" do
|
154
|
+
it "should return an array of created transitions" do
|
155
|
+
@state_machine.when(:awake) do |state|
|
156
|
+
transitions = state.transition_to(:tired, after: 42)
|
157
|
+
transitions.class.should == Array
|
158
|
+
transitions.first.class.should == StateMachine::TimedTransition
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
describe "Argument error handling" do
|
163
|
+
it "should raise if not given a destination state symbol" do
|
164
|
+
proc do
|
165
|
+
@state_machine.when(:awake) do |state|
|
166
|
+
state.transition_to Hash.new, on: :eat
|
167
|
+
end
|
168
|
+
end.should.raise ArgumentError, /No destination state given/
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should raise if not given a trigger event" do
|
172
|
+
proc do
|
173
|
+
@state_machine.when(:awake) do |state|
|
174
|
+
state.transition_to :sleepy
|
175
|
+
end
|
176
|
+
end.should.raise ArgumentError, /No trigger event/
|
177
|
+
end
|
178
|
+
|
179
|
+
it "should create the destination state if not existent" do
|
180
|
+
created = proc {@state_machine.states.collect(&:symbol).include?(:sleepy)}
|
181
|
+
created.call.should == false
|
182
|
+
@state_machine.when(:awake) do |state|
|
183
|
+
state.transition_to :sleepy, on: :hard_work_done
|
184
|
+
end
|
185
|
+
created.call.should == true
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
describe "#die" do
|
191
|
+
it "should return an array of created transitions" do
|
192
|
+
@state_machine.when(:awake) do |state|
|
193
|
+
transitions = state.die(on: :eat)
|
194
|
+
transitions.class.should == Array
|
195
|
+
transitions.first.class.should == StateMachine::SendEventTransition
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
it "should create termination states with different symbols" do
|
200
|
+
@state_machine.when(:awake) do |state|
|
201
|
+
# so many options!
|
202
|
+
transitions1 = state.die on: :suffocation
|
203
|
+
transitions2 = state.die on: :starving
|
204
|
+
transitions1.first.destination_state.symbol.should != transitions2.first.destination_state.symbol
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
it "should set the termination states' terminating flags" do
|
209
|
+
@state_machine.when(:awake) do |state|
|
210
|
+
non_terminating_transitions = state.transition_to :tired, on: :work_done
|
211
|
+
terminating_transitions = state.die on: :suffocation
|
212
|
+
non_terminating_transitions.first.destination_state.should.not.be.terminating
|
213
|
+
terminating_transitions.first.destination_state.should.be.terminating
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
end
|