statemachine 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,7 +5,6 @@ context "State Machine Odds And Ends" do
5
5
 
6
6
  setup do
7
7
  create_switch
8
- @sm.run
9
8
  end
10
9
 
11
10
  specify "method missing delegates to super in case of no event" do
@@ -13,15 +12,58 @@ context "State Machine Odds And Ends" do
13
12
  end
14
13
 
15
14
  specify "set state with string" do
16
- @sm.state.id.should_be :off
15
+ @sm.state.should_be :off
17
16
  @sm.state = "on"
18
- @sm.state.id.should_be :on
17
+ @sm.state.should_be :on
19
18
  end
20
19
 
21
20
  specify "set state with symbol" do
22
- @sm.state.id.should_be :off
21
+ @sm.state.should_be :off
23
22
  @sm.state = :on
24
- @sm.state.id.should_be :on
23
+ @sm.state.should_be :on
24
+ end
25
+
26
+ specify "process event accepts strings" do
27
+ @sm.process_event("toggle")
28
+ @sm.state.should_be :on
25
29
  end
26
30
 
27
31
  end
32
+
33
+ context "Special States" do
34
+
35
+ setup do
36
+ @sm = StateMachine.build do |s|
37
+ s.superstate :operate do |o|
38
+ o.trans :on, :toggle, :off
39
+ o.trans :off, :toggle, :on
40
+ o.event :fiddle, :middle
41
+ end
42
+ s.trans :middle, :fiddle, :operate_H
43
+ s.trans :middle, :push, :stuck
44
+ s.trans :middle, :dream, :on_H
45
+ s.start_state :middle
46
+ end
47
+ end
48
+
49
+ specify "states without transitions are valid" do
50
+ @sm.push
51
+ @sm.state.should_be :stuck
52
+ end
53
+
54
+ specify "no history allowed for concrete states" do
55
+ lambda {
56
+ @sm.dream
57
+ }.should_raise(StateMachine::StateMachineException, "No history exists for 'on' state since it is not a super state.")
58
+ end
59
+
60
+ specify "error when trying to use history that doesn't exist yet" do
61
+ lambda {
62
+ @sm.fiddle
63
+ }.should_raise(StateMachine::StateMachineException, "'operate' superstate doesn't have any history yet.")
64
+ end
65
+
66
+ end
67
+
68
+
69
+
@@ -7,52 +7,19 @@ context "simple cases:" do
7
7
  @proc = Proc.new {@count = @count + 1}
8
8
  end
9
9
 
10
- specify "one transition has states" do
11
- @sm.add(:on, :flip, :off, @proc)
12
-
13
- @sm.states.length.should_be 2
14
- @sm[:on].should_not_be nil
15
- @sm[:off].should_not_be nil
16
- end
17
-
18
- specify "one trasition create connects states with transition" do
19
- @sm.add(:on, :flip, :off, @proc)
20
- origin = @sm[:on]
21
- destination = @sm[:off]
22
-
23
- origin.transitions.length.should_be 1
24
- destination.transitions.length.should_be 0
25
- transition = origin[:flip]
26
- check_transition(transition, :on, :off, :flip, @proc)
27
- end
28
-
29
10
  specify "reset" do
30
- @sm.add(:start, :blah, nil, @proc)
31
- @sm.run
11
+ StateMachine.build(@sm) { |s| s.trans :start, :blah, :end, @proc }
32
12
  @sm.process_event(:blah)
33
13
 
34
14
  @sm.reset
35
15
 
36
- @sm.state.should.be @sm[:start]
37
- end
38
-
39
- specify "exception when state machine is not running" do
40
- @sm.add(:on, :flip, :off)
41
-
42
- begin
43
- @sm.process_event(:flip)
44
- rescue StateMachine::StateMachineException => e
45
- e.message.should_equal "The state machine isn't in any state. Did you forget to call run?"
46
- end
16
+ @sm.state.should_be :start
47
17
  end
48
18
 
49
19
  specify "no proc in transition" do
50
- @sm.add(:on, :flip, :off)
51
- @sm.run
20
+ StateMachine.build(@sm) { |s| s.trans :on, :flip, :off }
52
21
 
53
22
  @sm.flip
54
23
  end
55
-
56
-
57
24
 
58
25
  end
@@ -7,16 +7,23 @@ context "Turn Stile" do
7
7
  create_turnstile
8
8
 
9
9
  @out_out_order = false
10
- @sm.add(:operative, :maintain, :maintenance, Proc.new { @out_of_order = true } )
11
- @sm.add(:maintenance, :operate, :operative, Proc.new { @out_of_order = false } )
12
- @sm[:operative].add_substates(:locked, :unlocked)
13
10
 
14
- @sm.run
11
+ @sm = StateMachine.build do |s|
12
+ s.superstate :operative do |o|
13
+ o.trans :locked, :coin, :unlocked, @unlock
14
+ o.trans :unlocked, :pass, :locked, @lock
15
+ o.trans :locked, :pass, :locked, @alarm
16
+ o.trans :unlocked, :coin, :locked, @thankyou
17
+ o.event :maintain, :maintenance, Proc.new { @out_of_order = true }
18
+ end
19
+ s.trans :maintenance, :operate, :operative, Proc.new { @out_of_order = false }
20
+ s.start_state :locked
21
+ end
15
22
  end
16
23
 
17
24
  specify "substates respond to superstate transitions" do
18
25
  @sm.process_event(:maintain)
19
- @sm.state.id.should_be :maintenance
26
+ @sm.state.should_be :maintenance
20
27
  @locked.should_be true
21
28
  @out_of_order.should_be true
22
29
  end
@@ -24,57 +31,9 @@ context "Turn Stile" do
24
31
  specify "after transitions, substates respond to superstate transitions" do
25
32
  @sm.coin
26
33
  @sm.maintain
27
- @sm.state.id.should_be :maintenance
34
+ @sm.state.should_be :maintenance
28
35
  @locked.should_be false
29
36
  @out_of_order.should_be true
30
37
  end
31
38
 
32
- specify "transitions back to superstate go to history state" do
33
- @sm[:operative].use_history
34
- @sm.maintain
35
- @sm.operate
36
- @sm.state.id.should_be :locked
37
- @out_of_order.should_be false
38
-
39
- @sm.coin
40
- @sm.maintain
41
- @sm.operate
42
- @sm.state.id.should_be :unlocked
43
- end
44
-
45
- specify "missing substates are added" do
46
- @sm[:operative].add_substates(:blah)
47
- @sm[:blah].should_not_be nil
48
- @sm[:blah].superstate.id.should_be :operative
49
- end
50
-
51
- specify "recursive superstates not allowed" do
52
- begin
53
- @sm[:operative].add_substates(:operative)
54
- self.should_fail_with_message("exception expected")
55
- rescue StateMachine::StateMachineException => e
56
- e.message.should_equal "Cyclic substates not allowed. (operative)"
57
- end
58
- end
59
-
60
- specify "recursive superstates (2 levels) not allowed" do
61
- begin
62
- @sm[:operative].add_substates(:blah)
63
- @sm[:blah].add_substates(:operative)
64
- self.should_fail_with_message("exception expected")
65
- rescue StateMachine::StateMachineException => e
66
- e.message.should_equal "Cyclic substates not allowed. (blah)"
67
- end
68
- end
69
-
70
- specify "exception when add_substates called without args" do
71
- begin
72
- @sm[:locked].add_substates()
73
- self.should_fail_with_message("exception expected")
74
- rescue StateMachine::StateMachineException => e
75
- e.message.should_equal "At least one parameter is required for add_substates."
76
- end
77
- end
78
-
79
-
80
39
  end
@@ -5,27 +5,25 @@ context "Turn Stile" do
5
5
 
6
6
  setup do
7
7
  create_turnstile
8
- @sm.run
9
8
  end
10
9
 
11
10
  specify "connections" do
12
- @sm.states.length.should_be 2
13
- locked_state = @sm[:locked]
14
- unlocked_state = @sm[:unlocked]
11
+ locked_state = @sm.get_state(:locked)
12
+ unlocked_state = @sm.get_state(:unlocked)
15
13
 
16
14
  locked_state.transitions.length.should_be 2
17
15
  unlocked_state.transitions.length.should_be 2
18
16
 
19
- check_transition(locked_state[:coin], :locked, :unlocked, :coin, @unlock)
20
- check_transition(locked_state[:pass], :locked, :locked, :pass, @alarm)
21
- check_transition(unlocked_state[:pass], :unlocked, :locked, :pass, @lock)
22
- check_transition(unlocked_state[:coin], :unlocked, :locked, :coin, @thankyou)
17
+ check_transition(locked_state.transitions[:coin], :locked, :unlocked, :coin, @unlock)
18
+ check_transition(locked_state.transitions[:pass], :locked, :locked, :pass, @alarm)
19
+ check_transition(unlocked_state.transitions[:pass], :unlocked, :locked, :pass, @lock)
20
+ check_transition(unlocked_state.transitions[:coin], :unlocked, :locked, :coin, @thankyou)
23
21
  end
24
22
 
25
23
  specify "start state" do
26
- @sm.run
27
- @sm.start_state.should.be @sm[:locked]
28
- @sm.state.should.be @sm[:locked]
24
+ @sm.reset
25
+ @sm.start_state.should.be :locked
26
+ @sm.state.should.be :locked
29
27
  end
30
28
 
31
29
  specify "bad event" do
@@ -33,31 +31,31 @@ context "Turn Stile" do
33
31
  @sm.process_event(:blah)
34
32
  self.should.fail_with_message("Exception expected")
35
33
  rescue Exception => e
36
- e.class.should.be StateMachine::MissingTransitionException
37
- e.to_s.should_equal "'locked' state does not respond to the 'blah' event."
34
+ e.class.should.be StateMachine::StateMachineException
35
+ e.to_s.should_eql "'locked' state does not respond to the 'blah' event."
38
36
  end
39
37
  end
40
38
 
41
39
  specify "locked state with a coin" do
42
40
  @sm.process_event(:coin)
43
41
 
44
- @sm.state.should.be @sm[:unlocked]
42
+ @sm.state.should.be :unlocked
45
43
  @locked.should.be false
46
44
  end
47
45
 
48
46
  specify "locked state with pass event" do
49
47
  @sm.process_event(:pass)
50
48
 
51
- @sm.state.should.be @sm[:locked]
49
+ @sm.state.should.be :locked
52
50
  @locked.should.be true
53
- @alarm.should.be true
51
+ @alarm_status.should.be true
54
52
  end
55
53
 
56
54
  specify "unlocked state with coin" do
57
55
  @sm.process_event(:coin)
58
56
  @sm.process_event(:coin)
59
57
 
60
- @sm.state.should.be @sm[:locked]
58
+ @sm.state.should.be :locked
61
59
  @thankyou_status.should.be true
62
60
  end
63
61
 
@@ -65,14 +63,14 @@ context "Turn Stile" do
65
63
  @sm.process_event(:coin)
66
64
  @sm.process_event(:pass)
67
65
 
68
- @sm.state.should.be @sm[:locked]
66
+ @sm.state.should.be :locked
69
67
  @locked.should.be true
70
68
  end
71
69
 
72
70
  specify "events invoked via method_missing" do
73
71
  @sm.coin
74
- @sm.state.should.be @sm[:unlocked]
72
+ @sm.state.should.be :unlocked
75
73
  @sm.pass
76
- @sm.state.should.be @sm[:locked]
74
+ @sm.state.should.be :locked
77
75
  end
78
76
  end
data/spec/spec_helper.rb CHANGED
@@ -1,21 +1,24 @@
1
- $:.unshift('lib')
1
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
2
+ require 'rubygems'
3
+ require 'spec'
2
4
  require 'statemachine'
3
5
 
4
6
  def check_transition(transition, origin_id, destination_id, event, action)
5
7
  transition.should_not_be nil
6
8
  transition.event.should_be event
7
9
  transition.action.should_be action
8
- transition.origin.id.should_be origin_id
9
- transition.destination.id.should_be destination_id
10
+ transition.origin_id.should_be origin_id
11
+ transition.destination_id.should_be destination_id
10
12
  end
11
13
 
12
14
  module SwitchStateMachine
13
15
 
14
16
  def create_switch
15
17
  @status = "off"
16
- @sm = StateMachine::StateMachine.new
17
- @sm.add(:off, :toggle, :on, Proc.new { @status = "on" } )
18
- @sm.add(:on, :toggle, :off, Proc.new { @status = "off" } )
18
+ @sm = StateMachine.build do |s|
19
+ s.trans :off, :toggle, :on, Proc.new { @status = "on" }
20
+ s.trans :on, :toggle, :off, Proc.new { @status = "off" }
21
+ end
19
22
  end
20
23
 
21
24
  end
@@ -31,11 +34,12 @@ module TurnstileStateMachine
31
34
  @alarm = Proc.new { @alarm_status = true }
32
35
  @thankyou = Proc.new { @thankyou_status = true }
33
36
 
34
- @sm = StateMachine::StateMachine.new
35
- @sm.add(:locked, :coin, :unlocked, @unlock)
36
- @sm.add(:unlocked, :pass, :locked, @lock)
37
- @sm.add(:locked, :pass, :locked, @alarm)
38
- @sm.add(:unlocked, :coin, :locked, @thankyou)
37
+ @sm = StateMachine.build do |s|
38
+ s.trans :locked, :coin, :unlocked, @unlock
39
+ s.trans :unlocked, :pass, :locked, @lock
40
+ s.trans :locked, :pass, :locked, @alarm
41
+ s.trans :unlocked, :coin, :locked, @thankyou
42
+ end
39
43
  end
40
44
 
41
45
  end
@@ -3,95 +3,105 @@ require File.dirname(__FILE__) + '/spec_helper'
3
3
  context "Transition Calculating Exits and Entries" do
4
4
 
5
5
  setup do
6
- @a = StateMachine::State.new("a", nil)
7
- @b = StateMachine::State.new("b", nil)
8
- @c = StateMachine::State.new("c", nil)
9
- @d = StateMachine::State.new("d", nil)
10
- @e = StateMachine::State.new("e", nil)
6
+ @transition = StateMachine::Transition.new(nil, nil, nil, nil)
11
7
  end
12
8
 
13
9
  specify "to nil" do
14
- transition = StateMachine::Transition.new(@a, nil, nil, nil)
15
- exits, entries = transition.exits_and_entries(@a)
16
- exits.to_s.should_equal [@a].to_s
17
- entries.to_s.should_equal [].to_s
10
+ @a = StateMachine::State.new("a", nil, nil)
11
+ exits, entries = @transition.exits_and_entries(@a, nil)
12
+ exits.to_s.should_eql [@a].to_s
13
+ entries.to_s.should_eql [].to_s
18
14
  entries.length.should_be 0
19
15
  end
20
16
 
21
17
  specify "to itself" do
22
- transition = StateMachine::Transition.new(@a, @a, nil, nil)
23
- exits, entries = transition.exits_and_entries(@a)
24
- exits.to_s.should_equal [@a].to_s
25
- entries.to_s.should_equal [@a].to_s
18
+ @a = StateMachine::State.new("a", nil, nil)
19
+ exits, entries = @transition.exits_and_entries(@a, @a)
20
+ exits.to_s.should_eql [@a].to_s
21
+ entries.to_s.should_eql [@a].to_s
26
22
  end
27
23
 
28
24
  specify "to friend" do
29
- transition = StateMachine::Transition.new(@a, @b, nil, nil)
30
- exits, entries = transition.exits_and_entries(@a)
31
- exits.to_s.should_equal [@a].to_s
32
- entries.to_s.should_equal [@b].to_s
25
+ @a = StateMachine::State.new("a", nil, nil)
26
+ @b = StateMachine::State.new("b", nil, nil)
27
+ exits, entries = @transition.exits_and_entries(@a, @b)
28
+ exits.to_s.should_eql [@a].to_s
29
+ entries.to_s.should_eql [@b].to_s
33
30
  end
34
31
 
35
32
  specify "to parent" do
36
- @a.superstate = @b
37
- transition = StateMachine::Transition.new(@a, @b, nil, nil)
38
- exits, entries = transition.exits_and_entries(@a)
39
- exits.to_s.should_equal [@a, @b].to_s
40
- entries.to_s.should_equal [@b].to_s
33
+ @b = StateMachine::State.new("b", nil, nil)
34
+ @a = StateMachine::State.new("a", @b, nil)
35
+ exits, entries = @transition.exits_and_entries(@a, @b)
36
+ exits.to_s.should_eql [@a, @b].to_s
37
+ entries.to_s.should_eql [@b].to_s
41
38
  end
42
39
 
43
40
  specify "to uncle" do
44
- @a.superstate = @b
45
- transition = StateMachine::Transition.new(@a, @c, nil, nil)
46
- exits, entries = transition.exits_and_entries(@a)
47
- exits.to_s.should_equal [@a, @b].to_s
48
- entries.to_s.should_equal [@c].to_s
41
+ @b = StateMachine::State.new("b", nil, nil)
42
+ @a = StateMachine::State.new("a", @b, nil)
43
+ @c = StateMachine::State.new("c", nil, nil)
44
+ exits, entries = @transition.exits_and_entries(@a, @c)
45
+ exits.to_s.should_eql [@a, @b].to_s
46
+ entries.to_s.should_eql [@c].to_s
49
47
  end
50
48
 
51
49
  specify "to cousin" do
52
- @a.superstate = @b
53
- @c.superstate = @d
54
- transition = StateMachine::Transition.new(@a, @c, nil, nil)
55
- exits, entries = transition.exits_and_entries(@a)
56
- exits.to_s.should_equal [@a, @b].to_s
57
- entries.to_s.should_equal [@d, @c].to_s
50
+ @b = StateMachine::State.new("b", nil, nil)
51
+ @d = StateMachine::State.new("d", nil, nil)
52
+ @a = StateMachine::State.new("a", @b, nil)
53
+ @c = StateMachine::State.new("c", @d, nil)
54
+ exits, entries = @transition.exits_and_entries(@a, @c)
55
+ exits.to_s.should_eql [@a, @b].to_s
56
+ entries.to_s.should_eql [@d, @c].to_s
58
57
  end
59
58
 
60
59
  specify "to nephew" do
61
- @a.superstate = @b
62
- transition = StateMachine::Transition.new(@c, @a, nil, nil)
63
- exits, entries = transition.exits_and_entries(@c)
64
- exits.to_s.should_equal [@c].to_s
65
- entries.to_s.should_equal [@b,@a].to_s
60
+ @b = StateMachine::State.new("b", nil, nil)
61
+ @c = StateMachine::State.new("c", nil, nil)
62
+ @a = StateMachine::State.new("a", @b, nil)
63
+ exits, entries = @transition.exits_and_entries(@c, @a)
64
+ exits.to_s.should_eql [@c].to_s
65
+ entries.to_s.should_eql [@b,@a].to_s
66
66
  end
67
67
 
68
68
  specify "to sister" do
69
- @a.superstate = @c
70
- @b.superstate = @c
71
- transition = StateMachine::Transition.new(@a, @b, nil, nil)
72
- exits, entries = transition.exits_and_entries(@a)
73
- exits.to_s.should_equal [@a].to_s
74
- entries.to_s.should_equal [@b].to_s
69
+ @c = StateMachine::State.new("c", nil, nil)
70
+ @a = StateMachine::State.new("a", @c, nil)
71
+ @b = StateMachine::State.new("b", @c, nil)
72
+ exits, entries = @transition.exits_and_entries(@a, @b)
73
+ exits.to_s.should_eql [@a].to_s
74
+ entries.to_s.should_eql [@b].to_s
75
75
  end
76
76
 
77
77
  specify "to second cousin" do
78
- @a.superstate = @b
79
- @b.superstate = @c
80
- @d.superstate = @e
81
- @e.superstate = @c
82
- transition = StateMachine::Transition.new(@a, @d, nil, nil)
83
- exits, entries = transition.exits_and_entries(@a)
84
- exits.to_s.should_equal [@a, @b].to_s
85
- entries.to_s.should_equal [@e, @d].to_s
78
+ @c = StateMachine::State.new("c", nil, nil)
79
+ @b = StateMachine::State.new("b", @c, nil)
80
+ @a = StateMachine::State.new("a", @b, nil)
81
+ @e = StateMachine::State.new("e", @c, nil)
82
+ @d = StateMachine::State.new("d", @e, nil)
83
+ exits, entries = @transition.exits_and_entries(@a, @d)
84
+ exits.to_s.should_eql [@a, @b].to_s
85
+ entries.to_s.should_eql [@e, @d].to_s
86
86
  end
87
87
 
88
88
  specify "to grandparent" do
89
- @a.superstate = @b
90
- @b.superstate = @c
91
- transition = StateMachine::Transition.new(@a, @c, nil, nil)
92
- exits, entries = transition.exits_and_entries(@a)
93
- exits.to_s.should_equal [@a, @b, @c].to_s
94
- entries.to_s.should_equal [@c].to_s
89
+ @c = StateMachine::State.new("c", nil, nil)
90
+ @b = StateMachine::State.new("b", @c, nil)
91
+ @a = StateMachine::State.new("a", @b, nil)
92
+ exits, entries = @transition.exits_and_entries(@a, @c)
93
+ exits.to_s.should_eql [@a, @b, @c].to_s
94
+ entries.to_s.should_eql [@c].to_s
95
+ end
96
+
97
+ specify "to parent's grandchild" do
98
+ @c = StateMachine::State.new("c", nil, nil)
99
+ @b = StateMachine::State.new("b", @c, nil)
100
+ @a = StateMachine::State.new("a", @b, nil)
101
+ @d = StateMachine::State.new("d", @c, nil)
102
+ exits, entries = @transition.exits_and_entries(@d, @a)
103
+ exits.to_s.should_eql [@d].to_s
104
+ entries.to_s.should_eql [@b, @a].to_s
95
105
  end
96
106
 
97
107
  end