statemachine 0.0.3 → 0.1.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.
@@ -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