golem_statemachine 0.9
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.
- data/.gitignore +3 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +472 -0
- data/Rakefile +23 -0
- data/examples/document.rb +44 -0
- data/examples/monster.rb +100 -0
- data/examples/seminar.rb +138 -0
- data/examples/seminar_enrollment.rb +141 -0
- data/init.rb +5 -0
- data/install.rb +1 -0
- data/lib/golem.rb +207 -0
- data/lib/golem/dsl/decision_def.rb +38 -0
- data/lib/golem/dsl/event_def.rb +89 -0
- data/lib/golem/dsl/state_def.rb +34 -0
- data/lib/golem/dsl/state_machine_def.rb +73 -0
- data/lib/golem/dsl/transition_def.rb +51 -0
- data/lib/golem/model/callback.rb +32 -0
- data/lib/golem/model/condition.rb +12 -0
- data/lib/golem/model/event.rb +12 -0
- data/lib/golem/model/state.rb +26 -0
- data/lib/golem/model/state_machine.rb +224 -0
- data/lib/golem/model/transition.rb +37 -0
- data/lib/golem/util/element_collection.rb +33 -0
- data/tasks/golem_statemachine_tasks.rake +4 -0
- data/test/active_record_test.rb +189 -0
- data/test/dsl_test.rb +429 -0
- data/test/monster_test.rb +110 -0
- data/test/problematic_test.rb +95 -0
- data/test/seminar_test.rb +106 -0
- data/test/statemachine_assertions.rb +79 -0
- data/test/test_helper.rb +5 -0
- data/uninstall.rb +1 -0
- metadata +119 -0
data/test/dsl_test.rb
ADDED
@@ -0,0 +1,429 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'statemachine_assertions'
|
3
|
+
require File.dirname(__FILE__)+'/../lib/golem'
|
4
|
+
|
5
|
+
# Because Monster has two statemachines, 'affect' and 'mouth', this test
|
6
|
+
# allows for validating statemachine behaviour when there are multiple
|
7
|
+
# statemachines defined within the same class.
|
8
|
+
class MonsterTest < Test::Unit::TestCase
|
9
|
+
include StatemachineAssertions
|
10
|
+
|
11
|
+
def setup
|
12
|
+
@klass = Class.new
|
13
|
+
@klass.instance_eval do
|
14
|
+
include Golem
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_define_statemachine
|
19
|
+
@klass.instance_eval do
|
20
|
+
define_statemachine do
|
21
|
+
initial_state :test
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
assert_kind_of Golem::Model::StateMachine, @klass.statemachines[:statemachine]
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_define_invalid_statemachine
|
29
|
+
assert_raise Golem::DefinitionSyntaxError do
|
30
|
+
@klass.instance_eval do
|
31
|
+
define_statemachine do
|
32
|
+
# no inital_state
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_define_multiple_statemachines
|
39
|
+
@klass.instance_eval do
|
40
|
+
define_statemachine(:alpha) do
|
41
|
+
initial_state :test
|
42
|
+
end
|
43
|
+
|
44
|
+
define_statemachine(:beta) do
|
45
|
+
initial_state :test
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
assert_kind_of Golem::Model::StateMachine, @klass.statemachines[:alpha]
|
50
|
+
assert_kind_of Golem::Model::StateMachine, @klass.statemachines[:beta]
|
51
|
+
assert_not_same @klass.statemachines[:alpha], @klass.statemachines[:beta]
|
52
|
+
|
53
|
+
# check that defining a default statemachine after defining named statemachines is okay
|
54
|
+
@klass.instance_eval do
|
55
|
+
define_statemachine do
|
56
|
+
initial_state :test
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
assert_kind_of Golem::Model::StateMachine, @klass.statemachines[:statemachine]
|
61
|
+
assert_not_same @klass.statemachines[:alpha], @klass.statemachines[:statemachine]
|
62
|
+
assert_not_same @klass.statemachines[:beta], @klass.statemachines[:statemachine]
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_define_duplicate_statemachine
|
66
|
+
@klass.instance_eval do
|
67
|
+
define_statemachine(:alpha) do
|
68
|
+
initial_state :test
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
assert_raise ArgumentError do
|
73
|
+
@klass.instance_eval do
|
74
|
+
define_statemachine(:alpha) do
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_define_multiple_invalid_statemachines
|
81
|
+
assert_raise Golem::DefinitionSyntaxError do
|
82
|
+
@klass.instance_eval do
|
83
|
+
define_statemachine(:alpha) do
|
84
|
+
initial_state :one
|
85
|
+
end
|
86
|
+
|
87
|
+
define_statemachine(:beta) do
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_define_state_attribute
|
94
|
+
@klass.instance_eval do
|
95
|
+
define_statemachine do
|
96
|
+
initial_state :one
|
97
|
+
end
|
98
|
+
|
99
|
+
define_statemachine(:alpha) do
|
100
|
+
initial_state :two
|
101
|
+
end
|
102
|
+
|
103
|
+
define_statemachine(:beta) do
|
104
|
+
state_attribute :foo
|
105
|
+
initial_state :three
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
obj = @klass.new
|
110
|
+
assert_equal :one, obj.state
|
111
|
+
assert_equal :two, obj.alpha_state
|
112
|
+
assert_equal :three, obj.foo
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
def test_define_states
|
117
|
+
@klass.instance_eval do
|
118
|
+
define_statemachine do
|
119
|
+
initial_state :one
|
120
|
+
state :one
|
121
|
+
state :two
|
122
|
+
end
|
123
|
+
|
124
|
+
define_statemachine(:alpha) do
|
125
|
+
initial_state :one
|
126
|
+
state :one
|
127
|
+
state :two
|
128
|
+
end
|
129
|
+
|
130
|
+
define_statemachine(:beta) do
|
131
|
+
initial_state :three
|
132
|
+
state :one
|
133
|
+
state :two
|
134
|
+
state :three
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
assert_equal :one, @klass.statemachines[:statemachine].states[:one].name
|
139
|
+
assert_equal 2, ([:one, :two] & @klass.statemachines[:statemachine].states.collect{|s| s.name}).size
|
140
|
+
assert_equal 2, ([:one, :two] & @klass.statemachines[:alpha].states.collect{|s| s.name}).size
|
141
|
+
assert_equal 3, ([:one, :two, :three] & @klass.statemachines[:beta].states.collect{|s| s.name}).size
|
142
|
+
assert_not_same @klass.statemachines[:statemachine].states[:one], @klass.statemachines[:statemachine].states[:two]
|
143
|
+
assert_not_same @klass.statemachines[:alpha].states[:one], @klass.statemachines[:beta].states[:one]
|
144
|
+
end
|
145
|
+
|
146
|
+
def test_define_events
|
147
|
+
@klass.instance_eval do
|
148
|
+
define_statemachine(:alpha) do
|
149
|
+
initial_state :one
|
150
|
+
state :one do
|
151
|
+
on :go, :to => :two
|
152
|
+
end
|
153
|
+
state :two do
|
154
|
+
on :go do
|
155
|
+
transition :to => :three
|
156
|
+
end
|
157
|
+
end
|
158
|
+
state :three, :to => :one do
|
159
|
+
on :go, :to => :one do
|
160
|
+
transition
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
define_statemachine(:beta) do
|
166
|
+
initial_state :b
|
167
|
+
state :a do
|
168
|
+
on :doit, :to => :b
|
169
|
+
on :foo, :action => Proc.new{|arg| puts arg}
|
170
|
+
end
|
171
|
+
state :b do
|
172
|
+
on :doit do
|
173
|
+
transition :to => :a
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
assert_equal [:go], @klass.statemachines[:alpha].events.collect{|e| e.name}
|
180
|
+
ev = @klass.statemachines[:alpha].events[:go]
|
181
|
+
assert_equal :go, ev.name
|
182
|
+
|
183
|
+
assert_equal_arrays [:doit, :foo], @klass.statemachines[:beta].events.collect{|e| e.name}
|
184
|
+
end
|
185
|
+
|
186
|
+
|
187
|
+
def test_define_transitions_in_event_to
|
188
|
+
sm = sm_def_helper do
|
189
|
+
state :a do
|
190
|
+
on :go, :to => :b
|
191
|
+
end
|
192
|
+
state :b do
|
193
|
+
on :go, :to => :a do
|
194
|
+
transition # inherits on :to
|
195
|
+
transition :to => :b # overrides on :to
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
assert_transition_on_event sm, :go, :a, :b
|
201
|
+
assert_transition_on_event sm, :go, :b, :a
|
202
|
+
assert_transition_on_event sm, :go, :b, :b
|
203
|
+
assert_no_transition_on_event sm, :go, :a, :a
|
204
|
+
end
|
205
|
+
|
206
|
+
|
207
|
+
def test_define_self_transitions
|
208
|
+
sm = sm_def_helper do
|
209
|
+
state :a do
|
210
|
+
on :go
|
211
|
+
end
|
212
|
+
state :b do
|
213
|
+
on :go do
|
214
|
+
transition :to => :self
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
assert_transition_on_event sm, :go, :a, :a
|
220
|
+
assert_transition_on_event sm, :go, :b, :b
|
221
|
+
assert_no_transition_on_event sm, :go, :a, :b
|
222
|
+
assert_no_transition_on_event sm, :go, :b, :a
|
223
|
+
end
|
224
|
+
|
225
|
+
|
226
|
+
def test_define_state_actions
|
227
|
+
enter_b_proc = Proc.new{puts "hi!"}
|
228
|
+
|
229
|
+
sm = sm_def_helper do
|
230
|
+
state :a do
|
231
|
+
enter :hi
|
232
|
+
exit :bye
|
233
|
+
end
|
234
|
+
state :b do
|
235
|
+
enter enter_b_proc
|
236
|
+
exit do
|
237
|
+
puts "bye!"
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
assert_equal :hi, sm.states[:a].callbacks[:on_enter].callback
|
243
|
+
assert_equal :bye, sm.states[:a].callbacks[:on_exit].callback
|
244
|
+
assert_equal enter_b_proc, sm.states[:b].callbacks[:on_enter].callback
|
245
|
+
assert_equal Proc, sm.states[:b].callbacks[:on_exit].callback.class
|
246
|
+
end
|
247
|
+
|
248
|
+
|
249
|
+
def test_define_transition_guards
|
250
|
+
sm = sm_def_helper do
|
251
|
+
state :a do
|
252
|
+
on :go_1 do
|
253
|
+
transition :to => :self do
|
254
|
+
guard :ready?
|
255
|
+
guard :steady?
|
256
|
+
end
|
257
|
+
end
|
258
|
+
on :go_2 do
|
259
|
+
transition :to => :self, :if => :ready?
|
260
|
+
end
|
261
|
+
on :go_3 do
|
262
|
+
transition :to => :self, :if => :ready? do
|
263
|
+
guard :steady?
|
264
|
+
end
|
265
|
+
end
|
266
|
+
on :go_4, :if => :ready?
|
267
|
+
on :go_5, :if => :ready? do
|
268
|
+
transition :to => :b, :if => :steady? do
|
269
|
+
guard :maybe?
|
270
|
+
end
|
271
|
+
transition :to => :c, :if => :maybe?
|
272
|
+
transition :to => :self
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
assert_transition_on_event_has_guard sm, :go_1, :a, :a, :ready?
|
278
|
+
assert_transition_on_event_has_guard sm, :go_1, :a, :a, :steady?
|
279
|
+
|
280
|
+
assert_transition_on_event_has_guard sm, :go_2, :a, :a, :ready?
|
281
|
+
|
282
|
+
assert_transition_on_event_has_guard sm, :go_3, :a, :a, :ready?
|
283
|
+
assert_transition_on_event_has_guard sm, :go_3, :a, :a, :steady?
|
284
|
+
|
285
|
+
assert_transition_on_event_has_guard sm, :go_4, :a, :a, :ready?
|
286
|
+
|
287
|
+
assert_transition_on_event_has_guard sm, :go_5, :a, :b, :ready?
|
288
|
+
assert_transition_on_event_has_guard sm, :go_5, :a, :b, :steady?
|
289
|
+
assert_transition_on_event_has_guard sm, :go_5, :a, :b, :maybe?
|
290
|
+
|
291
|
+
assert_transition_on_event_has_guard sm, :go_5, :a, :c, :ready?
|
292
|
+
assert_transition_on_event_has_guard sm, :go_5, :a, :c, :maybe?
|
293
|
+
assert_transition_on_event_does_not_have_guard sm, :go_5, :a, :c, :steady?
|
294
|
+
|
295
|
+
assert_transition_on_event_has_guard sm, :go_5, :a, :a, :ready?
|
296
|
+
assert_transition_on_event_does_not_have_guard sm, :go_5, :a, :a, :maybe?
|
297
|
+
assert_transition_on_event_does_not_have_guard sm, :go_5, :a, :a, :steady?
|
298
|
+
end
|
299
|
+
|
300
|
+
def test_define_transition_guards_failure_messages
|
301
|
+
sm = sm_def_helper do
|
302
|
+
state :a do
|
303
|
+
on :move, :if => :movable?, :guard_options => {:failure_message => "it's not movable"} do
|
304
|
+
transition :to => :b do
|
305
|
+
guard :not_stuck?, :failure_message => "it's stuck"
|
306
|
+
guard :not_busy?, :failure_message => "it's busy"
|
307
|
+
end
|
308
|
+
transition :to => :c do
|
309
|
+
guard :not_tired?, :failure_message => "it's tired"
|
310
|
+
end
|
311
|
+
transition :to => :self, :if => :not_busy?
|
312
|
+
end
|
313
|
+
on :spin do
|
314
|
+
transition :to => :self do
|
315
|
+
guard :not_tired?, :failure_message => "it's tired"
|
316
|
+
guard :not_busy?, :failure_message => "it's busy"
|
317
|
+
end
|
318
|
+
end
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
@klass.instance_eval do
|
323
|
+
attr_accessor :tired
|
324
|
+
attr_accessor :stuck
|
325
|
+
attr_accessor :movable
|
326
|
+
attr_accessor :busy
|
327
|
+
|
328
|
+
define_method(:movable?) do
|
329
|
+
@movable
|
330
|
+
end
|
331
|
+
|
332
|
+
define_method(:not_busy?) do
|
333
|
+
!@busy
|
334
|
+
end
|
335
|
+
|
336
|
+
define_method(:not_tired?) do
|
337
|
+
!@tired
|
338
|
+
end
|
339
|
+
|
340
|
+
define_method(:not_stuck?) do
|
341
|
+
!@stuck
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
assert_transition_on_event_has_guard sm, :move, :a, :b, :movable?
|
346
|
+
assert_transition_on_event_has_guard sm, :move, :a, :b, :not_stuck?
|
347
|
+
assert_transition_on_event_has_guard sm, :move, :a, :b, :not_busy?
|
348
|
+
assert_transition_on_event_does_not_have_guard sm, :move, :a, :b, :not_tired?
|
349
|
+
|
350
|
+
assert_transition_on_event_has_guard sm, :move, :a, :c, :movable?
|
351
|
+
assert_transition_on_event_does_not_have_guard sm, :move, :a, :c, :not_stuck?
|
352
|
+
assert_transition_on_event_does_not_have_guard sm, :move, :a, :c, :not_busy?
|
353
|
+
assert_transition_on_event_has_guard sm, :move, :a, :c, :not_tired?
|
354
|
+
|
355
|
+
assert_transition_on_event_has_guard sm, :move, :a, :a, :movable?
|
356
|
+
assert_transition_on_event_does_not_have_guard sm, :move, :a, :a, :not_stuck?
|
357
|
+
assert_transition_on_event_does_not_have_guard sm, :move, :a, :a, :not_tired?
|
358
|
+
assert_transition_on_event_has_guard sm, :move, :a, :a, :not_busy?
|
359
|
+
|
360
|
+
assert_transition_on_event_does_not_have_guard sm, :spin, :a, :a, :movable?
|
361
|
+
assert_transition_on_event_has_guard sm, :spin, :a, :a, :not_busy?
|
362
|
+
assert_transition_on_event_has_guard sm, :spin, :a, :a, :not_tired?
|
363
|
+
assert_transition_on_event_does_not_have_guard sm, :spin, :a, :a, :not_stuck?
|
364
|
+
|
365
|
+
obj = @klass.new
|
366
|
+
obj.movable = false
|
367
|
+
|
368
|
+
raised = false
|
369
|
+
begin
|
370
|
+
obj.move!
|
371
|
+
rescue Golem::ImpossibleEvent => e
|
372
|
+
assert_match(/because it's not movable/, e.message)
|
373
|
+
raised = true
|
374
|
+
ensure
|
375
|
+
# be careful -- we end up here if assert_match fails!
|
376
|
+
assert raised
|
377
|
+
end
|
378
|
+
|
379
|
+
obj.stuck = true
|
380
|
+
|
381
|
+
raised = false
|
382
|
+
begin
|
383
|
+
obj.move!
|
384
|
+
rescue Golem::ImpossibleEvent => e
|
385
|
+
assert_match(/because it's not movable/, e.message)
|
386
|
+
raised = true
|
387
|
+
ensure
|
388
|
+
# be careful -- we end up here if assert_match fails!
|
389
|
+
assert raised
|
390
|
+
end
|
391
|
+
|
392
|
+
obj.tired = true
|
393
|
+
obj.movable = true
|
394
|
+
|
395
|
+
assert_nothing_raised do
|
396
|
+
obj.move! # follows only available transition --> to self
|
397
|
+
end
|
398
|
+
assert_equal :a, obj.state
|
399
|
+
|
400
|
+
|
401
|
+
obj.busy = true
|
402
|
+
obj.tired = false
|
403
|
+
|
404
|
+
# make sure second the guard (is_busy?) works
|
405
|
+
raised = false
|
406
|
+
begin
|
407
|
+
obj.spin!
|
408
|
+
rescue Golem::ImpossibleEvent => e
|
409
|
+
assert_match(/because it's busy/, e.message)
|
410
|
+
raised = true
|
411
|
+
ensure
|
412
|
+
# be careful -- we end up here if assert_match fails!
|
413
|
+
assert raised
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
private
|
418
|
+
|
419
|
+
def sm_def_helper(&block)
|
420
|
+
@klass.instance_eval do
|
421
|
+
define_statemachine do
|
422
|
+
initial_state :a
|
423
|
+
instance_eval(&block) if block_given?
|
424
|
+
end
|
425
|
+
end
|
426
|
+
return @klass.statemachines[:statemachine]
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require File.dirname(__FILE__)+'/../examples/monster'
|
3
|
+
|
4
|
+
# Because the Monster class has two statemachines ('affect' and 'mouth')
|
5
|
+
# this test allows for validating statemachine behaviour when there are
|
6
|
+
# multiple statemachines defined within the same class.
|
7
|
+
class MonsterTest < Test::Unit::TestCase
|
8
|
+
|
9
|
+
def setup
|
10
|
+
@monster = Monster.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_initial_state
|
14
|
+
assert_equal :sleeping, @monster.affect.initial_state
|
15
|
+
assert_equal :closed, @monster.mouth.initial_state
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_non_existent_event
|
19
|
+
assert_raise(NoMethodError){@monster.dance!}
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_machine_state_accessors
|
23
|
+
assert_equal :sleeping, @monster.affect_state
|
24
|
+
|
25
|
+
assert_equal :closed, @monster.mouth_state
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_event_possibility_within_states
|
29
|
+
assert_equal :sleeping, @monster.affect_state
|
30
|
+
assert_equal :closed, @monster.mouth_state
|
31
|
+
|
32
|
+
assert_raise(Golem::ImpossibleEvent){@monster.feed!}
|
33
|
+
assert_raise(Golem::ImpossibleEvent){@monster.lullaby!}
|
34
|
+
assert_nothing_raised{@monster.wake_up!}
|
35
|
+
assert_equal :hungry, @monster.affect_state
|
36
|
+
|
37
|
+
assert_raise(Golem::ImpossibleEvent){@monster.wake_up!}
|
38
|
+
assert_nothing_raised{@monster.lullaby!}
|
39
|
+
assert_equal :hungry, @monster.affect_state
|
40
|
+
|
41
|
+
assert_raise(Golem::ImpossibleEvent){@monster.feed!(:hamburger)}
|
42
|
+
assert_nothing_raised{@monster.tickle!}
|
43
|
+
assert_nothing_raised{@monster.feed!(:hamburger)}
|
44
|
+
assert_equal :satiated, @monster.affect_state
|
45
|
+
|
46
|
+
assert_raise(Golem::ImpossibleEvent){@monster.feed!}
|
47
|
+
assert_raise(Golem::ImpossibleEvent){@monster.wake_up!}
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_transition_decisions
|
51
|
+
@monster.wake_up!
|
52
|
+
|
53
|
+
assert_equal :hungry, @monster.affect_state
|
54
|
+
|
55
|
+
@monster.tickle!
|
56
|
+
|
57
|
+
@monster.feed!(:toast) # monster doesn't care for toast
|
58
|
+
|
59
|
+
assert_equal :hungry, @monster.affect_state
|
60
|
+
assert @monster.deeds.include?(:ate_toast)
|
61
|
+
assert !@monster.deeds.include?(:barfed)
|
62
|
+
|
63
|
+
@monster.feed!(:tofu) # monster hates tofu
|
64
|
+
|
65
|
+
assert @monster.deeds.include?(:barfed)
|
66
|
+
assert_equal :hungry, @monster.affect_state
|
67
|
+
|
68
|
+
@monster.feed!(:hamburger) # monster likes hamburger
|
69
|
+
|
70
|
+
assert_equal :satiated, @monster.affect_state
|
71
|
+
assert @monster.deeds.include?(:ate_tasty_hamburger)
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_state_actions
|
75
|
+
@monster.wake_up!
|
76
|
+
|
77
|
+
assert_raise(Golem::ImpossibleEvent){@monster.wake_up!}
|
78
|
+
assert_equal [:stretched, :grumbled], @monster.deeds
|
79
|
+
# The transition didn't take place, so :hungry's enter action should not have been executed
|
80
|
+
# (i.e. we should only see one :grumbled).
|
81
|
+
|
82
|
+
@monster.tickle!
|
83
|
+
@monster.feed!(:toast)
|
84
|
+
@monster.lullaby!
|
85
|
+
@monster.tickle!
|
86
|
+
@monster.feed!(:tofu)
|
87
|
+
|
88
|
+
assert_equal [:stretched, :grumbled, :giggled, :grumbled, :ate_toast, :grumbled, :yawned, :grumbled, :giggled,
|
89
|
+
:grumbled, :barfed, :grumbled], @monster.deeds
|
90
|
+
# Note that `grumble` is executed even when we transition from :hungry back to :hungry.
|
91
|
+
# Enter and exit actions are to be executed even for self-transitions, however they should NOT be executed when
|
92
|
+
# transition fails to occur (i.e. when an ImpossibleEvent is raised).
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_transition_actions
|
96
|
+
@monster.wake_up!
|
97
|
+
@monster.tickle!
|
98
|
+
@monster.feed!(:toast)
|
99
|
+
@monster.lullaby!
|
100
|
+
@monster.tickle!
|
101
|
+
@monster.feed!(:tofu)
|
102
|
+
@monster.feed!(:hamburger)
|
103
|
+
@monster.lullaby!
|
104
|
+
@monster.lullaby!
|
105
|
+
|
106
|
+
assert_equal [:stretched, :grumbled, :giggled, :grumbled, :ate_toast, :grumbled, :yawned, :grumbled, :giggled,
|
107
|
+
:grumbled, :barfed, :grumbled, :ate_tasty_hamburger, :yawned], @monster.deeds
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|