MINT-statemachine 1.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/CHANGES +135 -0
  2. data/LICENSE +16 -0
  3. data/MINT-statemachine.gemspec +27 -0
  4. data/README.rdoc +69 -0
  5. data/Rakefile +88 -0
  6. data/TODO +2 -0
  7. data/lib/statemachine.rb +26 -0
  8. data/lib/statemachine/action_invokation.rb +83 -0
  9. data/lib/statemachine/builder.rb +383 -0
  10. data/lib/statemachine/generate/dot_graph.rb +1 -0
  11. data/lib/statemachine/generate/dot_graph/dot_graph_statemachine.rb +127 -0
  12. data/lib/statemachine/generate/java.rb +1 -0
  13. data/lib/statemachine/generate/java/java_statemachine.rb +265 -0
  14. data/lib/statemachine/generate/src_builder.rb +48 -0
  15. data/lib/statemachine/generate/util.rb +50 -0
  16. data/lib/statemachine/parallelstate.rb +196 -0
  17. data/lib/statemachine/state.rb +102 -0
  18. data/lib/statemachine/statemachine.rb +279 -0
  19. data/lib/statemachine/stub_context.rb +26 -0
  20. data/lib/statemachine/superstate.rb +53 -0
  21. data/lib/statemachine/transition.rb +76 -0
  22. data/lib/statemachine/version.rb +17 -0
  23. data/spec/action_invokation_spec.rb +101 -0
  24. data/spec/builder_spec.rb +243 -0
  25. data/spec/default_transition_spec.rb +111 -0
  26. data/spec/generate/dot_graph/dot_graph_stagemachine_spec.rb +27 -0
  27. data/spec/generate/java/java_statemachine_spec.rb +349 -0
  28. data/spec/history_spec.rb +107 -0
  29. data/spec/noodle.rb +23 -0
  30. data/spec/sm_action_parameterization_spec.rb +99 -0
  31. data/spec/sm_activation_spec.rb +116 -0
  32. data/spec/sm_entry_exit_actions_spec.rb +99 -0
  33. data/spec/sm_odds_n_ends_spec.rb +67 -0
  34. data/spec/sm_parallel_state_spec.rb +207 -0
  35. data/spec/sm_simple_spec.rb +26 -0
  36. data/spec/sm_super_state_spec.rb +55 -0
  37. data/spec/sm_turnstile_spec.rb +76 -0
  38. data/spec/spec_helper.rb +121 -0
  39. data/spec/transition_spec.rb +107 -0
  40. metadata +115 -0
@@ -0,0 +1,27 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+ require 'statemachine/generate/dot_graph/dot_graph_statemachine'
3
+
4
+ describe Statemachine::Statemachine, "(Turn Stile)" do
5
+ include TurnstileStatemachine
6
+
7
+ before(:each) do
8
+ remove_test_dir("dot")
9
+ @output = test_dir("dot")
10
+ create_turnstile
11
+ end
12
+
13
+ # it "should output to console when no output dir provided" do
14
+ # # Note - this test doesn't verify output to the console, but it does
15
+ # # ensure that the to_dot call does not fail if not output is provided.
16
+ # @sm.to_dot
17
+ # end
18
+
19
+ it "should generate a basic graph declaration" do
20
+ @sm.to_dot(:output => @output)
21
+
22
+ dot = load_lines(@output, "main.dot")
23
+
24
+ dot.should_not equal(nil)
25
+ dot[0].include?("digraph").should == true
26
+ end
27
+ end
@@ -0,0 +1,349 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/../../spec_helper")
2
+ require 'statemachine/generate/java/java_statemachine'
3
+
4
+ describe Statemachine::Statemachine, "(Java)" do
5
+
6
+ before(:each) do
7
+ remove_test_dir("java")
8
+ @output = test_dir("java")
9
+ @sm = Statemachine.build {}
10
+ end
11
+
12
+ def generate_turnstile_sm
13
+ @sm = Statemachine.build do
14
+ trans :locked, :coin, :unlocked, :unlock
15
+ trans :unlocked, :pass, :locked, :lock
16
+ trans :locked, :pass, :locked, :alarm
17
+ trans :unlocked, :coin, :locked, :thanks
18
+ end
19
+
20
+ @sm.to_java(:output => @output, :name => "JavaTurnstile", :package => "test.turnstile")
21
+ end
22
+
23
+ def generate_complex_turnstile_sm
24
+ @sm = Statemachine.build do
25
+ superstate :operational do
26
+ on_entry :operate
27
+ on_exit :beep
28
+ state :locked do
29
+ on_entry :lock
30
+ event :coin, :unlocked
31
+ event :pass, :locked, :alarm
32
+ end
33
+ state :unlocked do
34
+ on_entry :unlock
35
+ event :coin, :unlocked, :thanks
36
+ event :pass, :locked
37
+ end
38
+ event :diagnose, :diagnostics
39
+ end
40
+ state :diagnostics do
41
+ on_entry :disable
42
+ on_exit :beep
43
+ event :operate, :operational
44
+ end
45
+ stub_context :verbose => true
46
+ end
47
+
48
+ @sm.to_java(:output => @output, :name => "JavaTurnstile", :package => "test.turnstile")
49
+ end
50
+
51
+ # def load_lines(*segs)
52
+ # filename = File.join(*segs)
53
+ # File.should exist( filename)
54
+ # return IO.read(filename).split("\n")
55
+ # end
56
+
57
+ def empty_sm_lines
58
+ @sm.to_java(:name => "JavaTest", :output => @output, :package => "test.blank")
59
+ return load_lines(@output, "test", "blank", "JavaTest.java")
60
+ end
61
+
62
+ def turnstile_lines
63
+ generate_turnstile_sm
64
+ return load_lines(@output, "test", "turnstile", "JavaTurnstile.java")
65
+ end
66
+
67
+ def turnstile_context_lines
68
+ generate_turnstile_sm
69
+ return load_lines(@output, "test", "turnstile", "JavaTurnstileContext.java")
70
+ end
71
+
72
+ def complex_turnstile_lines
73
+ generate_complex_turnstile_sm
74
+ return load_lines(@output, "test", "turnstile", "JavaTurnstile.java")
75
+ end
76
+
77
+ def find_lines_after(lines, goose)
78
+ while !lines.empty?
79
+ return lines if lines.shift.strip == goose
80
+ end
81
+ raise "Can't find desired line: #{goose}"
82
+ end
83
+
84
+ it "should error if no output dir is specified" do
85
+ lambda { @sm.to_java }.should raise_error("Please specify an output directory. (:output => 'where/you/want/your/code')")
86
+ end
87
+
88
+ it "should error if output dir doesn't exist" do
89
+ lambda { @sm.to_java(:output => "/blah") }.should raise_error("Output dir '/blah' doesn't exist.")
90
+ end
91
+
92
+ it "should error if no class name is specified" do
93
+ lambda { @sm.to_java(:output => @output) }.should raise_error("Please specify a name for the statemachine. (:name => 'SomeName')")
94
+ end
95
+
96
+ it "should generate a class" do
97
+ @sm.to_java(:name => "JavaTest", :output => @output)
98
+ File.should exist(File.join(@output, "JavaTest.java"))
99
+ end
100
+
101
+ it "should generate a class in a package" do
102
+ lines = empty_sm_lines
103
+
104
+ lines.shift.should == "// This file was generated by the Ruby Statemachine Library (http://slagyr.github.com/statemachine)."
105
+ lines.shift.include?("// Generated at ").should == true
106
+ lines.shift.should == "package test.blank;"
107
+ lines.shift.should == ""
108
+ lines.shift.should == "public class JavaTest"
109
+ lines.shift.should == "{"
110
+ lines.last.should == "}"
111
+ end
112
+
113
+ it "should generate the constructor" do
114
+ lines = find_lines_after(empty_sm_lines, "// Statemachine constructor")
115
+
116
+ lines.shift.should == " public JavaTest(JavaTestContext context)"
117
+ lines.shift.should == " {"
118
+ lines.shift.should == " this.context = context;"
119
+ lines.shift.should == " }"
120
+ lines.shift.should == ""
121
+ end
122
+
123
+ it "should generate sm boilerplate code" do
124
+ lines = find_lines_after(empty_sm_lines, "// The following is boiler plate code standard to all statemachines")
125
+
126
+ lines.shift.should == " public JavaTestContext getContext()"
127
+ lines.shift.should == " {"
128
+ lines.shift.should == " return context;"
129
+ lines.shift.should == " }"
130
+ lines.shift.should == ""
131
+ lines.shift.should == " public State getState()"
132
+ lines.shift.should == " {"
133
+ lines.shift.should == " return state;"
134
+ lines.shift.should == " }"
135
+ lines.shift.should == ""
136
+ lines.shift.should == " public void setState(State newState)"
137
+ lines.shift.should == " {"
138
+ lines.shift.should == " state = newState;"
139
+ lines.shift.should == " }"
140
+ end
141
+
142
+ it "should generate the sm exception" do
143
+ lines = empty_sm_lines
144
+ lines = find_lines_after(lines, "// Standard exception class added to all statemachines.")
145
+
146
+ lines.shift.should == " public static class StatemachineException extends RuntimeException"
147
+ lines.shift.should == " {"
148
+ lines.shift.should == " public StatemachineException(State state, String event)"
149
+ lines.shift.should == " {"
150
+ lines.shift.should == " super(\"Missing transition from '\" + state.getClass().getSimpleName() + \"' with the '\" + event + \"' event.\");"
151
+ lines.shift.should == " }"
152
+ lines.shift.should == " }"
153
+ end
154
+
155
+ it "should generate base state" do
156
+ lines = empty_sm_lines
157
+ lines = find_lines_after(lines, "// The base state")
158
+
159
+ lines.shift.should == " public static abstract class State"
160
+ lines.shift.should == " {"
161
+ lines.shift.should == " protected JavaTest statemachine;"
162
+ lines.shift.should == ""
163
+ lines.shift.should == " public State(JavaTest statemachine)"
164
+ lines.shift.should == " {"
165
+ lines.shift.should == " this.statemachine = statemachine;"
166
+ lines.shift.should == " }"
167
+ lines.shift.should == ""
168
+ lines.shift.should == " }"
169
+ end
170
+
171
+ it "should generate the context" do
172
+ @sm.to_java(:name => "JavaTest", :output => @output, :package => "com.blah")
173
+
174
+ filename = File.join(@output, "com", "blah", "JavaTestContext.java")
175
+ File.should exist(filename)
176
+ lines = IO.read(filename).split("\n")
177
+
178
+ lines.shift.should == "// This file was generated by the Ruby Statemachine Library (http://slagyr.github.com/statemachine)."
179
+ lines.shift.include?("// Generated at ").should == true
180
+ lines.shift.should == "package com.blah;"
181
+ lines.shift.should == ""
182
+ lines.shift.should == "public interface JavaTestContext"
183
+ lines.shift.should == "{"
184
+ lines.shift.should == " // Actions"
185
+ lines.shift.should == "}"
186
+ lines.should be_empty
187
+ end
188
+
189
+ it "should generate the state instance variables" do
190
+ lines = find_lines_after(turnstile_lines, "// Instance variables")
191
+
192
+ lines.shift.should == " public final State LOCKED = new LockedState(this);"
193
+ lines.shift.should == " public final State UNLOCKED = new UnlockedState(this);"
194
+ lines.shift.should == " private State state = LOCKED;"
195
+ lines.shift.should == ""
196
+ lines.shift.should == " private JavaTurnstileContext context;"
197
+ end
198
+
199
+ it "should generate all the sm event handlers" do
200
+ lines = find_lines_after(turnstile_lines, "// Event delegation")
201
+
202
+ lines.shift.should == " public void coin()"
203
+ lines.shift.should == " {"
204
+ lines.shift.should == " state.coin();"
205
+ lines.shift.should == " }"
206
+ lines.shift.should == ""
207
+ lines.shift.should == " public void pass()"
208
+ lines.shift.should == " {"
209
+ lines.shift.should == " state.pass();"
210
+ lines.shift.should == " }"
211
+ lines.shift.should == ""
212
+ end
213
+
214
+ it "should generate all the default event handlers" do
215
+ lines = find_lines_after(turnstile_lines, "// The base state")
216
+
217
+ lines.shift.should == " public static abstract class State"
218
+ lines.shift.should == " {"
219
+ lines.shift.should == " protected JavaTurnstile statemachine;"
220
+ lines.shift.should == ""
221
+ lines.shift.should == " public State(JavaTurnstile statemachine)"
222
+ lines.shift.should == " {"
223
+ lines.shift.should == " this.statemachine = statemachine;"
224
+ lines.shift.should == " }"
225
+ lines.shift.should == ""
226
+ lines.shift.should == " public void coin()"
227
+ lines.shift.should == " {"
228
+ lines.shift.should == " throw new StatemachineException(this, \"coin\");"
229
+ lines.shift.should == " }"
230
+ lines.shift.should == ""
231
+ lines.shift.should == " public void pass()"
232
+ lines.shift.should == " {"
233
+ lines.shift.should == " throw new StatemachineException(this, \"pass\");"
234
+ lines.shift.should == " }"
235
+ lines.shift.should == ""
236
+ lines.shift.should == " }"
237
+ end
238
+
239
+ it "should generate state classes" do
240
+ lines = find_lines_after(turnstile_lines, "// State implementations")
241
+
242
+ lines.shift.should == " public static class LockedState extends State"
243
+ lines.shift.should == " {"
244
+ lines.shift.should == " public LockedState(JavaTurnstile statemachine)"
245
+ lines.shift.should == " {"
246
+ lines.shift.should == " super(statemachine);"
247
+ lines.shift.should == " }"
248
+ lines.shift.should == ""
249
+ lines.shift.should == " public void coin()"
250
+ lines.shift.should == " {"
251
+ lines.shift.should == " statemachine.getContext().unlock();"
252
+ lines.shift.should == " statemachine.setState(statemachine.UNLOCKED);"
253
+ lines.shift.should == " }"
254
+ lines.shift.should == ""
255
+ lines.shift.should == " public void pass()"
256
+ lines.shift.should == " {"
257
+ lines.shift.should == " statemachine.getContext().alarm();"
258
+ lines.shift.should == " statemachine.setState(statemachine.LOCKED);"
259
+ lines.shift.should == " }"
260
+ lines.shift.should == ""
261
+ lines.shift.should == " }"
262
+ lines.shift.should == ""
263
+ lines.shift.should == " public static class UnlockedState extends State"
264
+ lines.shift.should == " {"
265
+ lines.shift.should == " public UnlockedState(JavaTurnstile statemachine)"
266
+ lines.shift.should == " {"
267
+ lines.shift.should == " super(statemachine);"
268
+ lines.shift.should == " }"
269
+ lines.shift.should == ""
270
+ lines.shift.should == " public void coin()"
271
+ lines.shift.should == " {"
272
+ lines.shift.should == " statemachine.getContext().thanks();"
273
+ lines.shift.should == " statemachine.setState(statemachine.LOCKED);"
274
+ lines.shift.should == " }"
275
+ lines.shift.should == ""
276
+ lines.shift.should == " public void pass()"
277
+ lines.shift.should == " {"
278
+ lines.shift.should == " statemachine.getContext().lock();"
279
+ lines.shift.should == " statemachine.setState(statemachine.LOCKED);"
280
+ lines.shift.should == " }"
281
+ lines.shift.should == ""
282
+ lines.shift.should == " }"
283
+ lines.shift.should == ""
284
+ end
285
+
286
+ it "should generate all the context methods" do
287
+ lines = find_lines_after(turnstile_context_lines, "// Actions")
288
+
289
+ lines.shift.should == " void alarm();"
290
+ lines.shift.should == " void lock();"
291
+ lines.shift.should == " void thanks();"
292
+ lines.shift.should == " void unlock();"
293
+ end
294
+
295
+ it "should generate complext state instances" do
296
+ lines = find_lines_after(complex_turnstile_lines, "// Instance variables")
297
+
298
+ lines.shift.should == " public final State DIAGNOSTICS = new DiagnosticsState(this);"
299
+ lines.shift.should == " public final State LOCKED = new LockedState(this);"
300
+ lines.shift.should == " public final State UNLOCKED = new UnlockedState(this);"
301
+ lines.shift.should == " public final State OPERATIONAL = LOCKED;"
302
+ lines.shift.should == " private State state = LOCKED;"
303
+ end
304
+
305
+ it "should generate entry actions on startup" do
306
+ lines = find_lines_after(complex_turnstile_lines, "// Statemachine constructor")
307
+
308
+ lines.shift.should == " public JavaTurnstile(JavaTurnstileContext context)"
309
+ lines.shift.should == " {"
310
+ lines.shift.should == " this.context = context;"
311
+ lines.shift.should == " context.operate();"
312
+ lines.shift.should == " context.lock();"
313
+ lines.shift.should == " }"
314
+ lines.shift.should == ""
315
+ end
316
+
317
+ it "should add entry/exit actions to each transition" do
318
+ lines = find_lines_after(complex_turnstile_lines, "public static class LockedState extends State")
319
+
320
+ lines.shift.should == " {"
321
+ lines.shift.should == " public LockedState(JavaTurnstile statemachine)"
322
+ lines.shift.should == " {"
323
+ lines.shift.should == " super(statemachine);"
324
+ lines.shift.should == " }"
325
+ lines.shift.should == ""
326
+ lines.shift.should == " public void coin()"
327
+ lines.shift.should == " {"
328
+ lines.shift.should == " statemachine.setState(statemachine.UNLOCKED);"
329
+ lines.shift.should == " statemachine.getContext().unlock();"
330
+ lines.shift.should == " }"
331
+ lines.shift.should == ""
332
+ lines.shift.should == " public void diagnose()"
333
+ lines.shift.should == " {"
334
+ lines.shift.should == " statemachine.getContext().beep();"
335
+ lines.shift.should == " statemachine.setState(statemachine.DIAGNOSTICS);"
336
+ lines.shift.should == " statemachine.getContext().disable();"
337
+ lines.shift.should == " }"
338
+ lines.shift.should == ""
339
+ lines.shift.should == " public void pass()"
340
+ lines.shift.should == " {"
341
+ lines.shift.should == " statemachine.getContext().alarm();"
342
+ lines.shift.should == " statemachine.setState(statemachine.LOCKED);"
343
+ lines.shift.should == " statemachine.getContext().lock();" # this has been added since my modifications don't know why'
344
+ lines.shift.should == " }"
345
+ lines.shift.should == ""
346
+ lines.shift.should == " }"
347
+ end
348
+
349
+ end
@@ -0,0 +1,107 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe "History States" do
4
+
5
+ before(:each) do
6
+ @sm = Statemachine.build do
7
+ superstate :operate do
8
+ trans :on, :toggle, :off
9
+ trans :off, :toggle, :on
10
+ event :fiddle, :middle
11
+ end
12
+ trans :middle, :fiddle, :operate_H
13
+ trans :middle, :dream, :on_H
14
+ trans :middle, :faddle, :on
15
+ startstate :middle
16
+ end
17
+ end
18
+
19
+ it "no history allowed for concrete states" do
20
+ lambda {
21
+ @sm.dream
22
+ }.should raise_error(Statemachine::StatemachineException, "No history exists for 'on' state since it is not a super state.")
23
+ end
24
+
25
+ it "error when trying to use history that doesn't exist yet" do
26
+ lambda {
27
+ @sm.fiddle
28
+ }.should raise_error(Statemachine::StatemachineException, "'operate' superstate doesn't have any history yet.")
29
+ end
30
+
31
+ it "reseting the statemachine resets history" do
32
+ @sm.faddle
33
+ @sm.fiddle
34
+ @sm.get_state(:operate).history_id.should eql(:on)
35
+
36
+ @sm.reset
37
+ @sm.get_state(:operate).history_id.should eql(nil)
38
+ end
39
+
40
+ end
41
+
42
+ describe "History Default" do
43
+
44
+ before(:each) do
45
+ @sm = Statemachine.build do
46
+ superstate :operate do
47
+ trans :on, :toggle, :off
48
+ trans :off, :toggle, :on
49
+ event :fiddle, :middle
50
+ default_history :on
51
+ end
52
+ trans :middle, :fiddle, :operate_H
53
+ startstate :middle
54
+ trans :middle, :faddle, :on
55
+ end
56
+ end
57
+
58
+ it "default history" do
59
+ @sm.fiddle
60
+ @sm.state.should eql(:on)
61
+ end
62
+
63
+ it "reseting the statemachine resets history" do
64
+ @sm.faddle
65
+ @sm.toggle
66
+ @sm.fiddle
67
+ @sm.get_state(:operate).history_id.should eql(:off)
68
+
69
+ @sm.reset
70
+ @sm.get_state(:operate).history_id.should eql(:on)
71
+ end
72
+
73
+ end
74
+
75
+ describe "Nested Superstates" do
76
+
77
+ before(:each) do
78
+ @sm = Statemachine.build do
79
+
80
+ superstate :grandpa do
81
+ trans :start, :go, :daughter
82
+ event :sister, :great_auntie
83
+
84
+ superstate :papa do
85
+ state :son
86
+ state :daughter
87
+ end
88
+ end
89
+
90
+ state :great_auntie do
91
+ event :foo, :grandpa_H
92
+ end
93
+
94
+ end
95
+ end
96
+
97
+ it "should use history of sub superstates when transitioning itto it's own history" do
98
+ @sm.go
99
+ @sm.sister
100
+ @sm.foo
101
+
102
+ @sm.state.should eql(:daughter)
103
+ end
104
+
105
+ end
106
+
107
+