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.
@@ -6,56 +6,58 @@ module StateMachine
6
6
 
7
7
  include ProcCalling
8
8
 
9
- attr_reader :origin, :event, :action
10
- attr_accessor :destination
9
+ attr_reader :origin_id, :event, :action
10
+ attr_accessor :destination_id
11
11
 
12
- def initialize(origin, destination, event, action)
13
- @origin = origin
14
- @destination = destination
12
+ def initialize(origin_id, destination_id, event, action)
13
+ @origin_id = origin_id
14
+ @destination_id = destination_id
15
15
  @event = event
16
16
  @action = action
17
17
  end
18
18
 
19
- def invoke(origin, args)
20
- exits, entries = exits_and_entries(origin)
19
+ def invoke(origin, statemachine, args)
20
+ destination = statemachine.get_state(@destination_id)
21
+ exits, entries = exits_and_entries(origin, destination)
21
22
  exits.each { |exited_state| exited_state.exit(args) }
22
23
 
23
- call_proc(@action, args, "transition action from #{origin} invoked by '#{event}' event") if @action
24
-
25
- entries.each { |entered_state| entered_state.enter(args) }
24
+ call_proc(@action, args, "transition action from #{origin} invoked by '#{@event}' event") if @action
26
25
 
27
- terminal_state = @destination
28
- while terminal_state and terminal_state.is_superstate?
26
+ terminal_state = destination
27
+ while terminal_state and not terminal_state.is_concrete?
29
28
  terminal_state = terminal_state.start_state
30
- terminal_state.enter(args)
29
+ entries << terminal_state
31
30
  end
31
+ terminal_state.activate if terminal_state
32
+
33
+ entries.each { |entered_state| entered_state.enter(args) }
32
34
  end
33
35
 
34
- def exits_and_entries(origin)
36
+ def exits_and_entries(origin, destination)
35
37
  exits = []
36
- entries = exits_and_entries_helper(exits, origin)
38
+ entries = exits_and_entries_helper(exits, origin, destination)
37
39
 
38
40
  return exits, entries.reverse
39
41
  end
40
42
 
41
43
  def to_s
42
- return "#{origin.id} ---#{event}---> #{destination.id} : #{action}"
44
+ return "#{@origin_id} ---#{@event}---> #{@destination_id} : #{action}"
43
45
  end
44
46
 
45
47
  private
46
48
 
47
- def exits_and_entries_helper(exits, exit_state)
48
- entries = entries_to_destination(exit_state)
49
+ def exits_and_entries_helper(exits, exit_state, destination)
50
+ entries = entries_to_destination(exit_state, destination)
49
51
  return entries if entries
50
52
  return [] if exit_state == nil
51
53
 
52
54
  exits << exit_state
53
- exits_and_entries_helper(exits, exit_state.superstate)
55
+ exits_and_entries_helper(exits, exit_state.superstate, destination)
54
56
  end
55
57
 
56
- def entries_to_destination(exit_state)
58
+ def entries_to_destination(exit_state, destination)
57
59
  entries = []
58
- state = @destination
60
+ state = destination
59
61
  while state
60
62
  entries << state
61
63
  return entries if exit_state == state.superstate
@@ -2,8 +2,8 @@ module StateMachine
2
2
  module VERSION
3
3
  unless defined? MAJOR
4
4
  MAJOR = 0
5
- MINOR = 0
6
- TINY = 3
5
+ MINOR = 1
6
+ TINY = 0
7
7
 
8
8
  STRING = [MAJOR, MINOR, TINY].join('.')
9
9
  TAG = "REL_" + [MAJOR, MINOR, TINY].join('_')
@@ -0,0 +1,131 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ context "Builder" do
4
+
5
+ setup do
6
+ @log = []
7
+ end
8
+
9
+ def check_switch(sm)
10
+ sm.state.should_be :off
11
+
12
+ sm.toggle
13
+ @log[0].should_eql "toggle on"
14
+ sm.state.should_be :on
15
+
16
+ sm.toggle
17
+ @log[1].should_eql "toggle off"
18
+ sm.state.should_be :off
19
+ end
20
+
21
+ specify "Building a the switch, relaxed" do
22
+ sm = StateMachine.build do |b|
23
+ b.trans :off, :toggle, :on, Proc.new { @log << "toggle on" }
24
+ b.trans :on, :toggle, :off, Proc.new { @log << "toggle off" }
25
+ end
26
+
27
+ check_switch sm
28
+ end
29
+
30
+ specify "Building a the switch, strict" do
31
+ sm = StateMachine.build do |b|
32
+ b.state(:off) { |s| s.event :toggle, :on, Proc.new { @log << "toggle on" } }
33
+ b.state(:on) { |s| s.event :toggle, :off, Proc.new { @log << "toggle off" } }
34
+ end
35
+
36
+ check_switch sm
37
+ end
38
+
39
+ specify "Adding a superstate to the switch" do
40
+ sm = StateMachine.build do |b|
41
+ b.superstate :operation do |o|
42
+ o.event :admin, :testing, lambda { @log << "testing" }
43
+ o.trans :off, :toggle, :on, lambda { @log << "toggle on" }
44
+ o.trans :on, :toggle, :off, lambda { @log << "toggle off" }
45
+ o.start_state :on
46
+ end
47
+ b.trans :testing, :resume, :operation, lambda { @log << "resuming" }
48
+ b.start_state :off
49
+ end
50
+
51
+ sm.state.should_be :off
52
+ sm.toggle
53
+ sm.admin
54
+ sm.state.should_be :testing
55
+ sm.resume
56
+ sm.state.should_be :on
57
+ @log.join(",").should_eql "toggle on,testing,resuming"
58
+ end
59
+
60
+ specify "entry exit actions" do
61
+ sm = StateMachine.build do |sm|
62
+ sm.state :off do |off|
63
+ off.on_entry { @log << "enter off" }
64
+ off.event :toggle, :on, lambda { @log << "toggle on" }
65
+ off.on_exit { @log << "exit off" }
66
+ end
67
+ sm.trans :on, :toggle, :off, lambda { @log << "toggle off" }
68
+ end
69
+
70
+ sm.toggle
71
+ sm.state.should_be :on
72
+ sm.toggle
73
+ sm.state.should_be :off
74
+
75
+ @log.join(",").should_eql "exit off,toggle on,toggle off,enter off"
76
+ end
77
+
78
+ specify "History state" do
79
+ sm = StateMachine.build do |b|
80
+ b.superstate :operation do |o|
81
+ o.event :admin, :testing, lambda { @log << "testing" }
82
+ o.state :off do |off|
83
+ off.on_entry { @log << "enter off" }
84
+ off.event :toggle, :on, lambda { @log << "toggle on" }
85
+ end
86
+ o.trans :on, :toggle, :off, lambda { @log << "toggle off" }
87
+ o.start_state :on
88
+ end
89
+ b.trans :testing, :resume, :operation_H, lambda { @log << "resuming" }
90
+ b.start_state :off
91
+ end
92
+
93
+ sm.admin
94
+ sm.resume
95
+ sm.state.should_be :off
96
+
97
+ @log.join(",").should_eql "testing,resuming,enter off"
98
+ end
99
+
100
+ specify "entry and exit action created from superstate builder" do
101
+ sm = StateMachine.build do |b|
102
+ b.trans :off, :toggle, :on, Proc.new { @log << "toggle on" }
103
+ b.on_entry_of(:off) { @log << "entering off" }
104
+ b.trans :on, :toggle, :off, Proc.new { @log << "toggle off" }
105
+ b.on_exit_of(:on) { @log << "exiting on" }
106
+ end
107
+
108
+ sm.toggle
109
+ sm.toggle
110
+
111
+ @log.join(",").should_eql "toggle on,exiting on,toggle off,entering off"
112
+
113
+ end
114
+
115
+ specify "superstate as startstate" do
116
+
117
+ lambda do
118
+ sm = StateMachine.build do |b|
119
+ b.superstate :mario_bros do |m|
120
+ m.trans :luigi, :bother, :mario
121
+ end
122
+ end
123
+
124
+ sm.state.should_be :luigi
125
+ end.should_not_raise(Exception)
126
+ end
127
+
128
+
129
+
130
+ end
131
+
@@ -5,110 +5,95 @@ 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 "action with one parameter" do
12
- @sm.add(:off, :set, :on, Proc.new { |value| @status = value } )
11
+ StateMachine.build(@sm) { |s| s.trans :off, :set, :on, Proc.new { |value| @status = value } }
13
12
  @sm.set "blue"
14
- @status.should_equal "blue"
15
- @sm.state.id.should_be :on
13
+ @status.should_eql "blue"
14
+ @sm.state.should_be :on
16
15
  end
17
16
 
18
17
  specify "action with two parameters" do
19
- @sm.add(:off, :set, :on, Proc.new { |a, b| @status = [a, b].join(",") } )
18
+ StateMachine.build(@sm) { |s| s.trans :off, :set, :on, Proc.new { |a, b| @status = [a, b].join(",") } }
20
19
  @sm.set "blue", "green"
21
- @status.should_equal "blue,green"
22
- @sm.state.id.should_be :on
20
+ @status.should_eql "blue,green"
21
+ @sm.state.should_be :on
23
22
  end
24
23
 
25
24
  specify "action with three parameters" do
26
- @sm.add(:off, :set, :on, Proc.new { |a, b, c| @status = [a, b, c].join(",") } )
25
+ StateMachine.build(@sm) { |s| s.trans :off, :set, :on, Proc.new { |a, b, c| @status = [a, b, c].join(",") } }
27
26
  @sm.set "blue", "green", "red"
28
- @status.should_equal "blue,green,red"
29
- @sm.state.id.should_be :on
27
+ @status.should_eql "blue,green,red"
28
+ @sm.state.should_be :on
30
29
  end
31
30
 
32
31
  specify "action with four parameters" do
33
- @sm.add(:off, :set, :on, Proc.new { |a, b, c, d| @status = [a, b, c, d].join(",") } )
32
+ StateMachine.build(@sm) { |s| s.trans :off, :set, :on, Proc.new { |a, b, c, d| @status = [a, b, c, d].join(",") } }
34
33
  @sm.set "blue", "green", "red", "orange"
35
- @status.should_equal "blue,green,red,orange"
36
- @sm.state.id.should_be :on
34
+ @status.should_eql "blue,green,red,orange"
35
+ @sm.state.should_be :on
37
36
  end
38
37
 
39
38
  specify "action with five parameters" do
40
- @sm.add(:off, :set, :on, Proc.new { |a, b, c, d, e| @status = [a, b, c, d, e].join(",") } )
39
+ StateMachine.build(@sm) { |s| s.trans :off, :set, :on, Proc.new { |a, b, c, d, e| @status = [a, b, c, d, e].join(",") } }
41
40
  @sm.set "blue", "green", "red", "orange", "yellow"
42
- @status.should_equal "blue,green,red,orange,yellow"
43
- @sm.state.id.should_be :on
41
+ @status.should_eql "blue,green,red,orange,yellow"
42
+ @sm.state.should_be :on
44
43
  end
45
44
 
46
45
  specify "action with six parameters" do
47
- @sm.add(:off, :set, :on, Proc.new { |a, b, c, d, e, f| @status = [a, b, c, d, e, f].join(",") } )
46
+ StateMachine.build(@sm) { |s| s.trans :off, :set, :on, Proc.new { |a, b, c, d, e, f| @status = [a, b, c, d, e, f].join(",") } }
48
47
  @sm.set "blue", "green", "red", "orange", "yellow", "indigo"
49
- @status.should_equal "blue,green,red,orange,yellow,indigo"
50
- @sm.state.id.should_be :on
48
+ @status.should_eql "blue,green,red,orange,yellow,indigo"
49
+ @sm.state.should_be :on
51
50
  end
52
51
 
53
52
  specify "action with seven parameters" do
54
- @sm.add(:off, :set, :on, Proc.new { |a, b, c, d, e, f, g| @status = [a, b, c, d, e, f, g].join(",") } )
53
+ StateMachine.build(@sm) { |s| s.trans :off, :set, :on, Proc.new { |a, b, c, d, e, f, g| @status = [a, b, c, d, e, f, g].join(",") } }
55
54
  @sm.set "blue", "green", "red", "orange", "yellow", "indigo", "violet"
56
- @status.should_equal "blue,green,red,orange,yellow,indigo,violet"
57
- @sm.state.id.should_be :on
55
+ @status.should_eql "blue,green,red,orange,yellow,indigo,violet"
56
+ @sm.state.should_be :on
58
57
  end
59
58
 
60
59
  specify "action with eight parameters" do
61
- @sm.add(:off, :set, :on, Proc.new { |a, b, c, d, e, f, g, h| @status = [a, b, c, d, e, f, g, h].join(",") } )
60
+ StateMachine.build(@sm) { |s| s.trans :off, :set, :on, Proc.new { |a, b, c, d, e, f, g, h| @status = [a, b, c, d, e, f, g, h].join(",") } }
62
61
  @sm.set "blue", "green", "red", "orange", "yellow", "indigo", "violet", "ultra-violet"
63
- @status.should_equal "blue,green,red,orange,yellow,indigo,violet,ultra-violet"
64
- @sm.state.id.should_be :on
65
- end
66
-
67
- specify "To many parameters" do
68
- @sm.add(:off, :set, :on, Proc.new { |a, b, c, d, e, f, g, h, i| @status = [a, b, c, d, e, f, g, h, i].join(",") } )
69
- begin
70
- @sm.process_event(:set, "blue", "green", "red", "orange", "yellow", "indigo", "violet", "ultra-violet", "Yikes!")
71
- rescue StateMachine::StateMachineException => e
72
- e.message.should_equal "Too many arguments(9). (transition action from 'off' state invoked by 'set' event)"
73
- end
62
+ @status.should_eql "blue,green,red,orange,yellow,indigo,violet,ultra-violet"
63
+ @sm.state.should_be :on
74
64
  end
75
65
 
76
66
  specify "calling process_event with parameters" do
77
- @sm.add(:off, :set, :on, Proc.new { |a, b, c| @status = [a, b, c].join(",") } )
67
+ StateMachine.build(@sm) { |s| s.trans :off, :set, :on, Proc.new { |a, b, c| @status = [a, b, c].join(",") } }
78
68
  @sm.process_event(:set, "blue", "green", "red")
79
- @status.should_equal "blue,green,red"
80
- @sm.state.id.should_be :on
69
+ @status.should_eql "blue,green,red"
70
+ @sm.state.should_be :on
81
71
  end
82
72
 
83
73
  specify "Insufficient params" do
84
- @sm.add(:off, :set, :on, Proc.new { |a, b, c| @status = [a, b, c].join(",") } )
85
- begin
86
- @sm.set "blue", "green"
87
- rescue StateMachine::StateMachineException => e
88
- e.message.should_equal "Insufficient parameters. (transition action from 'off' state invoked by 'set' event)"
89
- end
74
+ StateMachine.build(@sm) { |s| s.trans :off, :set, :on, Proc.new { |a, b, c| @status = [a, b, c].join(",") } }
75
+ lambda { @sm.set "blue", "green" }.should_raise(StateMachine::StateMachineException,
76
+ "Insufficient parameters. (transition action from 'off' state invoked by 'set' event)")
90
77
  end
91
78
 
92
79
  specify "infinate args" do
93
- @sm.add(:off, :set, :on, Proc.new { |*a| @status = a.join(",") } )
80
+ StateMachine.build(@sm) { |s| s.trans :off, :set, :on, Proc.new { |*a| @status = a.join(",") } }
94
81
  @sm.set(1, 2, 3)
95
- @status.should_equal "1,2,3"
82
+ @status.should_eql "1,2,3"
96
83
 
97
84
  @sm.state = :off
98
85
  @sm.set(1, 2, 3, 4, 5, 6)
99
- @status.should_equal "1,2,3,4,5,6"
86
+ @status.should_eql "1,2,3,4,5,6"
100
87
  end
101
88
 
102
89
  specify "Insufficient params when params are infinate" do
103
- @sm.add(:off, :set, :on, Proc.new { |a, *b| @status = a.to_s + ":" + b.join(",") } )
90
+ StateMachine.build(@sm) { |s| s.trans :off, :set, :on, Proc.new { |a, *b| @status = a.to_s + ":" + b.join(",") } }
104
91
  @sm.set(1, 2, 3)
105
- @status.should_equal "1:2,3"
92
+ @status.should_eql "1:2,3"
106
93
 
107
94
  @sm.state = :off
108
- begin
109
- @sm.set
110
- rescue StateMachine::StateMachineException => e
111
- e.message.should_equal "Insufficient parameters. (transition action from 'off' state invoked by 'set' event)"
112
- end
95
+
96
+ lambda { @sm.set }.should_raise(StateMachine::StateMachineException,
97
+ "Insufficient parameters. (transition action from 'off' state invoked by 'set' event)")
113
98
  end
114
99
  end
@@ -4,73 +4,81 @@ context "State Machine Entry and Exit Actions" do
4
4
 
5
5
  setup do
6
6
  @log = []
7
- @sm = StateMachine::StateMachine.new
8
- @sm.add(:off, :toggle, :on, Proc.new { @log << "on" } )
9
- @sm.add(:on, :toggle, :off, Proc.new { @log << "off" } )
10
- @sm.run
7
+ @sm = StateMachine.build do |s|
8
+ s.trans :off, :toggle, :on, Proc.new { @log << "on" }
9
+ s.trans :on, :toggle, :off, Proc.new { @log << "off" }
10
+ end
11
11
  end
12
12
 
13
13
  specify "entry action" do
14
- @sm[:on].on_entry Proc.new { @log << "entered_on" }
14
+ @sm.get_state(:on).entry_action = Proc.new { @log << "entered_on" }
15
15
 
16
16
  @sm.toggle
17
17
 
18
- @log.join(",").should_equal "on,entered_on"
18
+ @log.join(",").should_eql "on,entered_on"
19
19
  end
20
20
 
21
21
  specify "exit action" do
22
- @sm[:off].on_exit Proc.new { @log << "exited_off" }
22
+ @sm.get_state(:off).exit_action = Proc.new { @log << "exited_off" }
23
23
 
24
24
  @sm.toggle
25
25
 
26
- @log.join(",").should_equal "exited_off,on"
26
+ @log.join(",").should_eql "exited_off,on"
27
27
  end
28
28
 
29
29
  specify "exit and entry" do
30
- @sm[:off].on_exit Proc.new { @log << "exited_off" }
31
- @sm[:on].on_entry Proc.new { @log << "entered_on" }
30
+ @sm.get_state(:off).exit_action = Proc.new { @log << "exited_off" }
31
+ @sm.get_state(:on).entry_action = Proc.new { @log << "entered_on" }
32
32
 
33
33
  @sm.toggle
34
34
 
35
- @log.join(",").should_equal "exited_off,on,entered_on"
35
+ @log.join(",").should_eql "exited_off,on,entered_on"
36
36
  end
37
37
 
38
38
  specify "entry and exit actions may be parameterized" do
39
- @sm[:off].on_exit Proc.new { |a| @log << "exited_off(#{a})" }
40
- @sm[:on].on_entry Proc.new { |a, b| @log << "entered_on(#{a},#{b})" }
39
+ @sm.get_state(:off).exit_action = Proc.new { |a| @log << "exited_off(#{a})" }
40
+ @sm.get_state(:on).entry_action = Proc.new { |a, b| @log << "entered_on(#{a},#{b})" }
41
41
 
42
42
  @sm.toggle "one", "two"
43
43
 
44
- @log.join(",").should_equal "exited_off(one),on,entered_on(one,two)"
44
+ @log.join(",").should_eql "exited_off(one),on,entered_on(one,two)"
45
45
  end
46
46
 
47
47
  specify "current state is set prior to exit and entry actions" do
48
- @sm[:off].on_exit Proc.new { @log << @sm.state.id }
49
- @sm[:on].on_entry Proc.new { @log << @sm.state.id }
48
+ @sm.get_state(:off).exit_action = Proc.new { @log << @sm.state }
49
+ @sm.get_state(:on).entry_action = Proc.new { @log << @sm.state }
50
50
 
51
51
  @sm.toggle
52
52
 
53
- @log.join(",").should_equal "off,on,on"
53
+ @log.join(",").should_eql "off,on,on"
54
54
  end
55
55
 
56
56
  specify "current state is set prior to exit and entry actions even with super states" do
57
- @sm.add(:off_super, :toggle, :on, Proc.new { @log << "super_on" } )
58
- @sm.add(:on_super, :toggle, :off, Proc.new { @log << "super_off" } )
59
- @sm[:off_super].add_substates(:off)
60
- @sm[:on_super].add_substates(:on)
61
- @sm[:off_super].on_exit Proc.new { @log << @sm.state.id }
62
- @sm[:on_super].on_entry Proc.new { @log << @sm.state.id }
57
+ @sm = StateMachine::StateMachine.new
58
+ StateMachine.build(@sm) do |s|
59
+ s.superstate :off_super do |off_s|
60
+ off_s.on_exit {@log << @sm.state}
61
+ off_s.trans :off, :toggle, :on, Proc.new { @log << "on" }
62
+ off_s.event :toggle, :on, Proc.new { @log << "super_on" }
63
+ end
64
+ s.superstate :on_super do |on_s|
65
+ on_s.on_entry { @log << @sm.state }
66
+ on_s.trans :on, :toggle, :off, Proc.new { @log << "off" }
67
+ on_s.event :toggle, :off, Proc.new { @log << "super_off" }
68
+ end
69
+ s.start_state :off
70
+ end
63
71
 
64
72
  @sm.toggle
65
- @log.join(",").should_equal "off_super,super_on,on_super"
73
+ @log.join(",").should_eql "off,super_on,on"
66
74
  end
67
75
 
68
76
  specify "entry actions invokes another event" do
69
- @sm[:on].on_entry Proc.new { @sm.toggle }
77
+ @sm.get_state(:on).entry_action = Proc.new { @sm.toggle }
70
78
 
71
79
  @sm.toggle
72
- @log.join(",").should_equal "on,off"
73
- @sm.state.id.should_be :off
80
+ @log.join(",").should_eql "on,off"
81
+ @sm.state.should_be :off
74
82
  end
75
83
 
76
84