verborghs-state_machine 0.9.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. data/CHANGELOG.rdoc +360 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +635 -0
  4. data/Rakefile +77 -0
  5. data/examples/AutoShop_state.png +0 -0
  6. data/examples/Car_state.png +0 -0
  7. data/examples/TrafficLight_state.png +0 -0
  8. data/examples/Vehicle_state.png +0 -0
  9. data/examples/auto_shop.rb +11 -0
  10. data/examples/car.rb +19 -0
  11. data/examples/merb-rest/controller.rb +51 -0
  12. data/examples/merb-rest/model.rb +28 -0
  13. data/examples/merb-rest/view_edit.html.erb +24 -0
  14. data/examples/merb-rest/view_index.html.erb +23 -0
  15. data/examples/merb-rest/view_new.html.erb +13 -0
  16. data/examples/merb-rest/view_show.html.erb +17 -0
  17. data/examples/rails-rest/controller.rb +43 -0
  18. data/examples/rails-rest/migration.rb +11 -0
  19. data/examples/rails-rest/model.rb +23 -0
  20. data/examples/rails-rest/view_edit.html.erb +25 -0
  21. data/examples/rails-rest/view_index.html.erb +23 -0
  22. data/examples/rails-rest/view_new.html.erb +14 -0
  23. data/examples/rails-rest/view_show.html.erb +17 -0
  24. data/examples/traffic_light.rb +7 -0
  25. data/examples/vehicle.rb +31 -0
  26. data/init.rb +1 -0
  27. data/lib/state_machine/assertions.rb +36 -0
  28. data/lib/state_machine/callback.rb +241 -0
  29. data/lib/state_machine/condition_proxy.rb +106 -0
  30. data/lib/state_machine/eval_helpers.rb +83 -0
  31. data/lib/state_machine/event.rb +267 -0
  32. data/lib/state_machine/event_collection.rb +122 -0
  33. data/lib/state_machine/extensions.rb +149 -0
  34. data/lib/state_machine/guard.rb +230 -0
  35. data/lib/state_machine/initializers/merb.rb +1 -0
  36. data/lib/state_machine/initializers/rails.rb +5 -0
  37. data/lib/state_machine/initializers.rb +4 -0
  38. data/lib/state_machine/integrations/active_model/locale.rb +11 -0
  39. data/lib/state_machine/integrations/active_model/observer.rb +45 -0
  40. data/lib/state_machine/integrations/active_model.rb +445 -0
  41. data/lib/state_machine/integrations/active_record/locale.rb +20 -0
  42. data/lib/state_machine/integrations/active_record.rb +522 -0
  43. data/lib/state_machine/integrations/data_mapper/observer.rb +175 -0
  44. data/lib/state_machine/integrations/data_mapper.rb +379 -0
  45. data/lib/state_machine/integrations/mongo_mapper.rb +309 -0
  46. data/lib/state_machine/integrations/sequel.rb +356 -0
  47. data/lib/state_machine/integrations.rb +83 -0
  48. data/lib/state_machine/machine.rb +1645 -0
  49. data/lib/state_machine/machine_collection.rb +64 -0
  50. data/lib/state_machine/matcher.rb +123 -0
  51. data/lib/state_machine/matcher_helpers.rb +54 -0
  52. data/lib/state_machine/node_collection.rb +152 -0
  53. data/lib/state_machine/state.rb +260 -0
  54. data/lib/state_machine/state_collection.rb +112 -0
  55. data/lib/state_machine/transition.rb +399 -0
  56. data/lib/state_machine/transition_collection.rb +244 -0
  57. data/lib/state_machine.rb +421 -0
  58. data/lib/tasks/state_machine.rake +1 -0
  59. data/lib/tasks/state_machine.rb +27 -0
  60. data/test/files/en.yml +9 -0
  61. data/test/files/switch.rb +11 -0
  62. data/test/functional/state_machine_test.rb +980 -0
  63. data/test/test_helper.rb +4 -0
  64. data/test/unit/assertions_test.rb +40 -0
  65. data/test/unit/callback_test.rb +728 -0
  66. data/test/unit/condition_proxy_test.rb +328 -0
  67. data/test/unit/eval_helpers_test.rb +222 -0
  68. data/test/unit/event_collection_test.rb +324 -0
  69. data/test/unit/event_test.rb +795 -0
  70. data/test/unit/guard_test.rb +909 -0
  71. data/test/unit/integrations/active_model_test.rb +956 -0
  72. data/test/unit/integrations/active_record_test.rb +1918 -0
  73. data/test/unit/integrations/data_mapper_test.rb +1814 -0
  74. data/test/unit/integrations/mongo_mapper_test.rb +1382 -0
  75. data/test/unit/integrations/sequel_test.rb +1492 -0
  76. data/test/unit/integrations_test.rb +50 -0
  77. data/test/unit/invalid_event_test.rb +7 -0
  78. data/test/unit/invalid_transition_test.rb +7 -0
  79. data/test/unit/machine_collection_test.rb +565 -0
  80. data/test/unit/machine_test.rb +2349 -0
  81. data/test/unit/matcher_helpers_test.rb +37 -0
  82. data/test/unit/matcher_test.rb +155 -0
  83. data/test/unit/node_collection_test.rb +207 -0
  84. data/test/unit/state_collection_test.rb +280 -0
  85. data/test/unit/state_machine_test.rb +31 -0
  86. data/test/unit/state_test.rb +848 -0
  87. data/test/unit/transition_collection_test.rb +2098 -0
  88. data/test/unit/transition_test.rb +1384 -0
  89. metadata +176 -0
@@ -0,0 +1,795 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ class EventByDefaultTest < Test::Unit::TestCase
4
+ def setup
5
+ @klass = Class.new
6
+ @machine = StateMachine::Machine.new(@klass)
7
+ @event = StateMachine::Event.new(@machine, :ignite)
8
+
9
+ @object = @klass.new
10
+ end
11
+
12
+ def test_should_have_a_machine
13
+ assert_equal @machine, @event.machine
14
+ end
15
+
16
+ def test_should_have_a_name
17
+ assert_equal :ignite, @event.name
18
+ end
19
+
20
+ def test_should_have_a_qualified_name
21
+ assert_equal :ignite, @event.qualified_name
22
+ end
23
+
24
+ def test_should_have_a_human_name
25
+ assert_equal 'ignite', @event.human_name
26
+ end
27
+
28
+ def test_should_not_have_any_guards
29
+ assert @event.guards.empty?
30
+ end
31
+
32
+ def test_should_have_no_known_states
33
+ assert @event.known_states.empty?
34
+ end
35
+
36
+ def test_should_not_be_able_to_fire
37
+ assert !@event.can_fire?(@object)
38
+ end
39
+
40
+ def test_should_not_have_a_transition
41
+ assert_nil @event.transition_for(@object)
42
+ end
43
+
44
+ def test_should_define_a_predicate
45
+ assert @object.respond_to?(:can_ignite?)
46
+ end
47
+
48
+ def test_should_define_a_transition_accessor
49
+ assert @object.respond_to?(:ignite_transition)
50
+ end
51
+
52
+ def test_should_define_an_action
53
+ assert @object.respond_to?(:ignite)
54
+ end
55
+
56
+ def test_should_define_a_bang_action
57
+ assert @object.respond_to?(:ignite!)
58
+ end
59
+ end
60
+
61
+ class EventTest < Test::Unit::TestCase
62
+ def setup
63
+ @machine = StateMachine::Machine.new(Class.new)
64
+ @event = StateMachine::Event.new(@machine, :ignite)
65
+ @event.transition :parked => :idling
66
+ end
67
+
68
+ def test_should_allow_changing_machine
69
+ new_machine = StateMachine::Machine.new(Class.new)
70
+ @event.machine = new_machine
71
+ assert_equal new_machine, @event.machine
72
+ end
73
+
74
+ def test_should_allow_changing_human_name
75
+ @event.human_name = 'Stop'
76
+ assert_equal 'Stop', @event.human_name
77
+ end
78
+
79
+ def test_should_provide_matcher_helpers_during_initialization
80
+ matchers = []
81
+
82
+ @event.instance_eval do
83
+ matchers = [all, any, same]
84
+ end
85
+
86
+ assert_equal [StateMachine::AllMatcher.instance, StateMachine::AllMatcher.instance, StateMachine::LoopbackMatcher.instance], matchers
87
+ end
88
+
89
+ def test_should_use_pretty_inspect
90
+ assert_match "#<StateMachine::Event name=:ignite transitions=[:parked => :idling]>", @event.inspect
91
+ end
92
+ end
93
+
94
+ class EventWithHumanNameTest < Test::Unit::TestCase
95
+ def setup
96
+ @klass = Class.new
97
+ @machine = StateMachine::Machine.new(@klass)
98
+ @event = StateMachine::Event.new(@machine, :ignite, :human_name => 'start')
99
+ end
100
+
101
+ def test_should_use_custom_human_name
102
+ assert_equal 'start', @event.human_name
103
+ end
104
+ end
105
+
106
+ class EventWithDynamicHumanNameTest < Test::Unit::TestCase
107
+ def setup
108
+ @klass = Class.new
109
+ @machine = StateMachine::Machine.new(@klass)
110
+ @event = StateMachine::Event.new(@machine, :ignite, :human_name => lambda {|event, object| ['start', object]})
111
+ end
112
+
113
+ def test_should_use_custom_human_name
114
+ human_name, klass = @event.human_name
115
+ assert_equal 'start', human_name
116
+ assert_equal @klass, klass
117
+ end
118
+
119
+ def test_should_allow_custom_class_to_be_passed_through
120
+ human_name, klass = @event.human_name(1)
121
+ assert_equal 'start', human_name
122
+ assert_equal 1, klass
123
+ end
124
+
125
+ def test_should_not_cache_value
126
+ assert_not_same @event.human_name, @event.human_name
127
+ end
128
+ end
129
+
130
+ class EventWithConflictingHelpersTest < Test::Unit::TestCase
131
+ def setup
132
+ @klass = Class.new do
133
+ def can_ignite?
134
+ 0
135
+ end
136
+
137
+ def ignite_transition
138
+ 0
139
+ end
140
+
141
+ def ignite
142
+ 0
143
+ end
144
+
145
+ def ignite!
146
+ 0
147
+ end
148
+ end
149
+ @machine = StateMachine::Machine.new(@klass)
150
+ @state = StateMachine::Event.new(@machine, :ignite)
151
+ @object = @klass.new
152
+ end
153
+
154
+ def test_should_not_redefine_predicate
155
+ assert_equal 0, @object.can_ignite?
156
+ end
157
+
158
+ def test_should_not_redefine_transition_accessor
159
+ assert_equal 0, @object.ignite_transition
160
+ end
161
+
162
+ def test_should_not_redefine_action
163
+ assert_equal 0, @object.ignite
164
+ end
165
+
166
+ def test_should_not_redefine_bang_action
167
+ assert_equal 0, @object.ignite!
168
+ end
169
+
170
+ def test_should_allow_super_chaining
171
+ @klass.class_eval do
172
+ def can_ignite?
173
+ super ? 1 : 0
174
+ end
175
+
176
+ def ignite_transition
177
+ super ? 1 : 0
178
+ end
179
+
180
+ def ignite
181
+ super ? 1 : 0
182
+ end
183
+
184
+ def ignite!
185
+ begin
186
+ super
187
+ 1
188
+ rescue Exception => ex
189
+ 0
190
+ end
191
+ end
192
+ end
193
+
194
+ assert_equal 0, @object.can_ignite?
195
+ assert_equal 0, @object.ignite_transition
196
+ assert_equal 0, @object.ignite
197
+ assert_equal 1, @object.ignite!
198
+ end
199
+ end
200
+
201
+ class EventWithNamespaceTest < Test::Unit::TestCase
202
+ def setup
203
+ @klass = Class.new
204
+ @machine = StateMachine::Machine.new(@klass, :namespace => 'alarm')
205
+ @event = StateMachine::Event.new(@machine, :enable)
206
+ @object = @klass.new
207
+ end
208
+
209
+ def test_should_have_a_name
210
+ assert_equal :enable, @event.name
211
+ end
212
+
213
+ def test_should_have_a_qualified_name
214
+ assert_equal :enable_alarm, @event.qualified_name
215
+ end
216
+
217
+ def test_should_namespace_predicate
218
+ assert @object.respond_to?(:can_enable_alarm?)
219
+ end
220
+
221
+ def test_should_namespace_transition_accessor
222
+ assert @object.respond_to?(:enable_alarm_transition)
223
+ end
224
+
225
+ def test_should_namespace_action
226
+ assert @object.respond_to?(:enable_alarm)
227
+ end
228
+
229
+ def test_should_namespace_bang_action
230
+ assert @object.respond_to?(:enable_alarm!)
231
+ end
232
+ end
233
+
234
+ class EventTransitionsTest < Test::Unit::TestCase
235
+ def setup
236
+ @machine = StateMachine::Machine.new(Class.new)
237
+ @event = StateMachine::Event.new(@machine, :ignite)
238
+ end
239
+
240
+ def test_should_not_raise_exception_if_implicit_option_specified
241
+ assert_nothing_raised {@event.transition(:invalid => :valid)}
242
+ end
243
+
244
+ def test_should_not_allow_on_option
245
+ exception = assert_raise(ArgumentError) {@event.transition(:on => :ignite)}
246
+ assert_equal 'Invalid key(s): on', exception.message
247
+ end
248
+
249
+ def test_should_automatically_set_on_option
250
+ guard = @event.transition(:to => :idling)
251
+ assert_instance_of StateMachine::WhitelistMatcher, guard.event_requirement
252
+ assert_equal [:ignite], guard.event_requirement.values
253
+ end
254
+
255
+ def test_should_not_allow_except_to_option
256
+ exception = assert_raise(ArgumentError) {@event.transition(:except_to => :parked)}
257
+ assert_equal 'Invalid key(s): except_to', exception.message
258
+ end
259
+
260
+ def test_should_not_allow_except_on_option
261
+ exception = assert_raise(ArgumentError) {@event.transition(:except_on => :ignite)}
262
+ assert_equal 'Invalid key(s): except_on', exception.message
263
+ end
264
+
265
+ def test_should_allow_transitioning_without_a_to_state
266
+ assert_nothing_raised {@event.transition(:from => :parked)}
267
+ end
268
+
269
+ def test_should_allow_transitioning_without_a_from_state
270
+ assert_nothing_raised {@event.transition(:to => :idling)}
271
+ end
272
+
273
+ def test_should_allow_except_from_option
274
+ assert_nothing_raised {@event.transition(:except_from => :idling)}
275
+ end
276
+
277
+ def test_should_allow_transitioning_from_a_single_state
278
+ assert @event.transition(:parked => :idling)
279
+ end
280
+
281
+ def test_should_allow_transitioning_from_multiple_states
282
+ assert @event.transition([:parked, :idling] => :idling)
283
+ end
284
+
285
+ def test_should_have_transitions
286
+ guard = @event.transition(:to => :idling)
287
+ assert_equal [guard], @event.guards
288
+ end
289
+ end
290
+
291
+ class EventAfterBeingCopiedTest < Test::Unit::TestCase
292
+ def setup
293
+ @machine = StateMachine::Machine.new(Class.new)
294
+ @event = StateMachine::Event.new(@machine, :ignite)
295
+ @copied_event = @event.dup
296
+ end
297
+
298
+ def test_should_not_have_the_same_collection_of_guards
299
+ assert_not_same @event.guards, @copied_event.guards
300
+ end
301
+
302
+ def test_should_not_have_the_same_collection_of_known_states
303
+ assert_not_same @event.known_states, @copied_event.known_states
304
+ end
305
+ end
306
+
307
+ class EventWithoutTransitionsTest < Test::Unit::TestCase
308
+ def setup
309
+ @klass = Class.new
310
+ @machine = StateMachine::Machine.new(@klass)
311
+ @event = StateMachine::Event.new(@machine, :ignite)
312
+ @object = @klass.new
313
+ end
314
+
315
+ def test_should_not_be_able_to_fire
316
+ assert !@event.can_fire?(@object)
317
+ end
318
+
319
+ def test_should_not_have_a_transition
320
+ assert_nil @event.transition_for(@object)
321
+ end
322
+
323
+ def test_should_not_fire
324
+ assert !@event.fire(@object)
325
+ end
326
+
327
+ def test_should_not_change_the_current_state
328
+ @event.fire(@object)
329
+ assert_nil @object.state
330
+ end
331
+ end
332
+
333
+ class EventWithTransitionsTest < Test::Unit::TestCase
334
+ def setup
335
+ @klass = Class.new
336
+ @machine = StateMachine::Machine.new(@klass)
337
+ @event = StateMachine::Event.new(@machine, :ignite)
338
+ @event.transition(:parked => :idling)
339
+ @event.transition(:first_gear => :idling)
340
+ end
341
+
342
+ def test_should_include_all_transition_states_in_known_states
343
+ assert_equal [:parked, :idling, :first_gear], @event.known_states
344
+ end
345
+
346
+ def test_should_include_new_transition_states_after_calling_known_states
347
+ @event.known_states
348
+ @event.transition(:stalled => :idling)
349
+
350
+ assert_equal [:parked, :idling, :first_gear, :stalled], @event.known_states
351
+ end
352
+
353
+ def test_should_use_pretty_inspect
354
+ assert_match "#<StateMachine::Event name=:ignite transitions=[:parked => :idling, :first_gear => :idling]>", @event.inspect
355
+ end
356
+ end
357
+
358
+ class EventWithoutMatchingTransitionsTest < Test::Unit::TestCase
359
+ def setup
360
+ @klass = Class.new
361
+ @machine = StateMachine::Machine.new(@klass)
362
+ @machine.state :parked, :idling
363
+
364
+ @event = StateMachine::Event.new(@machine, :ignite)
365
+ @event.transition(:parked => :idling)
366
+
367
+ @object = @klass.new
368
+ @object.state = 'idling'
369
+ end
370
+
371
+ def test_should_not_be_able_to_fire
372
+ assert !@event.can_fire?(@object)
373
+ end
374
+
375
+ def test_should_not_have_a_transition
376
+ assert_nil @event.transition_for(@object)
377
+ end
378
+
379
+ def test_should_not_fire
380
+ assert !@event.fire(@object)
381
+ end
382
+
383
+ def test_should_not_change_the_current_state
384
+ @event.fire(@object)
385
+ assert_equal 'idling', @object.state
386
+ end
387
+ end
388
+
389
+ class EventWithMatchingDisabledTransitionsTest < Test::Unit::TestCase
390
+ def setup
391
+ StateMachine::Integrations.const_set('Custom', Module.new do
392
+ def invalidate(object, attribute, message, values = [])
393
+ (object.errors ||= []) << generate_message(message, values)
394
+ end
395
+
396
+ def reset(object)
397
+ object.errors = []
398
+ end
399
+ end)
400
+
401
+ @klass = Class.new do
402
+ attr_accessor :errors
403
+ end
404
+
405
+ @machine = StateMachine::Machine.new(@klass, :integration => :custom)
406
+ @machine.state :parked, :idling
407
+
408
+ @event = StateMachine::Event.new(@machine, :ignite)
409
+ @event.transition(:parked => :idling, :if => lambda {false})
410
+
411
+ @object = @klass.new
412
+ @object.state = 'parked'
413
+ end
414
+
415
+ def test_should_not_be_able_to_fire
416
+ assert !@event.can_fire?(@object)
417
+ end
418
+
419
+ def test_should_not_have_a_transition
420
+ assert_nil @event.transition_for(@object)
421
+ end
422
+
423
+ def test_should_not_fire
424
+ assert !@event.fire(@object)
425
+ end
426
+
427
+ def test_should_not_change_the_current_state
428
+ @event.fire(@object)
429
+ assert_equal 'parked', @object.state
430
+ end
431
+
432
+ def test_should_invalidate_the_state
433
+ @event.fire(@object)
434
+ assert_equal ['cannot transition via "ignite"'], @object.errors
435
+ end
436
+
437
+ def test_should_invalidate_with_human_event_name
438
+ @event.human_name = 'start'
439
+ @event.fire(@object)
440
+ assert_equal ['cannot transition via "start"'], @object.errors
441
+ end
442
+
443
+ def test_should_reset_existing_error
444
+ @object.errors = ['invalid']
445
+
446
+ @event.fire(@object)
447
+ assert_equal ['cannot transition via "ignite"'], @object.errors
448
+ end
449
+
450
+ def teardown
451
+ StateMachine::Integrations.send(:remove_const, 'Custom')
452
+ end
453
+ end
454
+
455
+ class EventWithMatchingEnabledTransitionsTest < Test::Unit::TestCase
456
+ def setup
457
+ StateMachine::Integrations.const_set('Custom', Module.new do
458
+ def invalidate(object, attribute, message, values = [])
459
+ (object.errors ||= []) << generate_message(message, values)
460
+ end
461
+
462
+ def reset(object)
463
+ object.errors = []
464
+ end
465
+ end)
466
+
467
+ @klass = Class.new do
468
+ attr_accessor :errors
469
+ end
470
+
471
+ @machine = StateMachine::Machine.new(@klass, :integration => :custom)
472
+ @machine.state :parked, :idling
473
+ @machine.event :ignite
474
+
475
+ @event = StateMachine::Event.new(@machine, :ignite)
476
+ @event.transition(:parked => :idling)
477
+
478
+ @object = @klass.new
479
+ @object.state = 'parked'
480
+ end
481
+
482
+ def test_should_be_able_to_fire
483
+ assert @event.can_fire?(@object)
484
+ end
485
+
486
+ def test_should_have_a_transition
487
+ transition = @event.transition_for(@object)
488
+ assert_not_nil transition
489
+ assert_equal 'parked', transition.from
490
+ assert_equal 'idling', transition.to
491
+ assert_equal :ignite, transition.event
492
+ end
493
+
494
+ def test_should_fire
495
+ assert @event.fire(@object)
496
+ end
497
+
498
+ def test_should_change_the_current_state
499
+ @event.fire(@object)
500
+ assert_equal 'idling', @object.state
501
+ end
502
+
503
+ def test_should_reset_existing_error
504
+ @object.errors = ['invalid']
505
+
506
+ @event.fire(@object)
507
+ assert_equal [], @object.errors
508
+ end
509
+
510
+ def test_should_not_invalidate_the_state
511
+ @event.fire(@object)
512
+ assert_equal [], @object.errors
513
+ end
514
+
515
+ def teardown
516
+ StateMachine::Integrations.send(:remove_const, 'Custom')
517
+ end
518
+ end
519
+
520
+ class EventWithTransitionWithoutToStateTest < Test::Unit::TestCase
521
+ def setup
522
+ @klass = Class.new
523
+ @machine = StateMachine::Machine.new(@klass)
524
+ @machine.state :parked
525
+ @machine.event :park
526
+
527
+ @event = StateMachine::Event.new(@machine, :park)
528
+ @event.transition(:from => :parked)
529
+
530
+ @object = @klass.new
531
+ @object.state = 'parked'
532
+ end
533
+
534
+ def test_should_be_able_to_fire
535
+ assert @event.can_fire?(@object)
536
+ end
537
+
538
+ def test_should_have_a_transition
539
+ transition = @event.transition_for(@object)
540
+ assert_not_nil transition
541
+ assert_equal 'parked', transition.from
542
+ assert_equal 'parked', transition.to
543
+ assert_equal :park, transition.event
544
+ end
545
+
546
+ def test_should_fire
547
+ assert @event.fire(@object)
548
+ end
549
+
550
+ def test_should_not_change_the_current_state
551
+ @event.fire(@object)
552
+ assert_equal 'parked', @object.state
553
+ end
554
+ end
555
+
556
+ class EventWithTransitionWithNilToStateTest < Test::Unit::TestCase
557
+ def setup
558
+ @klass = Class.new
559
+ @machine = StateMachine::Machine.new(@klass)
560
+ @machine.state nil, :idling
561
+ @machine.event :park
562
+
563
+ @event = StateMachine::Event.new(@machine, :park)
564
+ @event.transition(:idling => nil)
565
+
566
+ @object = @klass.new
567
+ @object.state = 'idling'
568
+ end
569
+
570
+ def test_should_be_able_to_fire
571
+ assert @event.can_fire?(@object)
572
+ end
573
+
574
+ def test_should_have_a_transition
575
+ transition = @event.transition_for(@object)
576
+ assert_not_nil transition
577
+ assert_equal 'idling', transition.from
578
+ assert_equal nil, transition.to
579
+ assert_equal :park, transition.event
580
+ end
581
+
582
+ def test_should_fire
583
+ assert @event.fire(@object)
584
+ end
585
+
586
+ def test_should_not_change_the_current_state
587
+ @event.fire(@object)
588
+ assert_equal nil, @object.state
589
+ end
590
+ end
591
+
592
+ class EventWithMultipleTransitionsTest < Test::Unit::TestCase
593
+ def setup
594
+ @klass = Class.new
595
+ @machine = StateMachine::Machine.new(@klass)
596
+ @machine.state :parked, :idling
597
+ @machine.event :ignite
598
+
599
+ @event = StateMachine::Event.new(@machine, :ignite)
600
+ @event.transition(:idling => :idling)
601
+ @event.transition(:parked => :idling) # This one should get used
602
+ @event.transition(:parked => :parked)
603
+
604
+ @object = @klass.new
605
+ @object.state = 'parked'
606
+ end
607
+
608
+ def test_should_be_able_to_fire
609
+ assert @event.can_fire?(@object)
610
+ end
611
+
612
+ def test_should_have_a_transition
613
+ transition = @event.transition_for(@object)
614
+ assert_not_nil transition
615
+ assert_equal 'parked', transition.from
616
+ assert_equal 'idling', transition.to
617
+ assert_equal :ignite, transition.event
618
+ end
619
+
620
+ def test_should_allow_specific_transition_selection_using_from
621
+ transition = @event.transition_for(@object, :from => :idling)
622
+
623
+ assert_not_nil transition
624
+ assert_equal 'idling', transition.from
625
+ assert_equal 'idling', transition.to
626
+ assert_equal :ignite, transition.event
627
+ end
628
+
629
+ def test_should_allow_specific_transition_selection_using_to
630
+ transition = @event.transition_for(@object, :from => :parked, :to => :parked)
631
+
632
+ assert_not_nil transition
633
+ assert_equal 'parked', transition.from
634
+ assert_equal 'parked', transition.to
635
+ assert_equal :ignite, transition.event
636
+ end
637
+
638
+ def test_should_allow_specific_transition_selection_using_on
639
+ transition = @event.transition_for(@object, :on => :park)
640
+ assert_nil transition
641
+
642
+ transition = @event.transition_for(@object, :on => :ignite)
643
+ assert_not_nil transition
644
+ end
645
+
646
+ def test_should_fire
647
+ assert @event.fire(@object)
648
+ end
649
+
650
+ def test_should_change_the_current_state
651
+ @event.fire(@object)
652
+ assert_equal 'idling', @object.state
653
+ end
654
+ end
655
+
656
+ class EventWithMachineActionTest < Test::Unit::TestCase
657
+ def setup
658
+ @klass = Class.new do
659
+ attr_reader :saved
660
+
661
+ def save
662
+ @saved = true
663
+ end
664
+ end
665
+
666
+ @machine = StateMachine::Machine.new(@klass, :action => :save)
667
+ @machine.state :parked, :idling
668
+
669
+ @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
670
+ @event.transition(:parked => :idling)
671
+
672
+ @object = @klass.new
673
+ @object.state = 'parked'
674
+ end
675
+
676
+ def test_should_run_action_on_fire
677
+ @event.fire(@object)
678
+ assert @object.saved
679
+ end
680
+
681
+ def test_should_not_run_action_if_configured_to_skip
682
+ @event.fire(@object, false)
683
+ assert !@object.saved
684
+ end
685
+ end
686
+
687
+ class EventWithInvalidCurrentStateTest < Test::Unit::TestCase
688
+ def setup
689
+ @klass = Class.new
690
+ @machine = StateMachine::Machine.new(@klass)
691
+ @machine.state :parked, :idling
692
+ @machine.event :ignite
693
+
694
+ @event = StateMachine::Event.new(@machine, :ignite)
695
+ @event.transition(:parked => :idling)
696
+
697
+ @object = @klass.new
698
+ @object.state = 'invalid'
699
+ end
700
+
701
+ def test_should_raise_exception_when_checking_availability
702
+ exception = assert_raise(ArgumentError) { @event.can_fire?(@object) }
703
+ assert_equal '"invalid" is not a known state value', exception.message
704
+ end
705
+
706
+ def test_should_raise_exception_when_finding_transition
707
+ exception = assert_raise(ArgumentError) { @event.transition_for(@object) }
708
+ assert_equal '"invalid" is not a known state value', exception.message
709
+ end
710
+
711
+ def test_should_raise_exception_when_firing
712
+ exception = assert_raise(ArgumentError) { @event.fire(@object) }
713
+ assert_equal '"invalid" is not a known state value', exception.message
714
+ end
715
+ end
716
+
717
+ class EventWithMarshallingTest < Test::Unit::TestCase
718
+ def setup
719
+ @klass = Class.new do
720
+ def save
721
+ true
722
+ end
723
+ end
724
+ self.class.const_set('Example', @klass)
725
+
726
+ @machine = StateMachine::Machine.new(@klass, :action => :save)
727
+ @machine.state :parked, :idling
728
+
729
+ @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
730
+ @event.transition(:parked => :idling)
731
+
732
+ @object = @klass.new
733
+ @object.state = 'parked'
734
+ end
735
+
736
+ def test_should_marshal_during_before_callbacks
737
+ @machine.before_transition {|object, transition| Marshal.dump(object)}
738
+ assert_nothing_raised { @event.fire(@object) }
739
+ end
740
+
741
+ def test_should_marshal_during_action
742
+ @klass.class_eval do
743
+ def save
744
+ Marshal.dump(self)
745
+ end
746
+ end
747
+
748
+ assert_nothing_raised { @event.fire(@object) }
749
+ end
750
+
751
+ def test_should_marshal_during_after_callbacks
752
+ @machine.after_transition {|object, transition| Marshal.dump(object)}
753
+ assert_nothing_raised { @event.fire(@object) }
754
+ end
755
+
756
+ def teardown
757
+ self.class.send(:remove_const, 'Example')
758
+ end
759
+ end
760
+
761
+ begin
762
+ # Load library
763
+ require 'rubygems'
764
+ gem 'ruby-graphviz', '>=0.9.0'
765
+ require 'graphviz'
766
+
767
+ class EventDrawingTest < Test::Unit::TestCase
768
+ def setup
769
+ states = [:parked, :idling, :first_gear]
770
+
771
+ @machine = StateMachine::Machine.new(Class.new, :initial => :parked)
772
+ @machine.other_states(*states)
773
+
774
+ graph = GraphViz.new('G')
775
+ states.each {|state| graph.add_node(state.to_s)}
776
+
777
+ @event = StateMachine::Event.new(@machine , :park)
778
+ @event.transition :parked => :idling
779
+ @event.transition :first_gear => :parked
780
+ @event.transition :except_from => :parked, :to => :parked
781
+
782
+ @edges = @event.draw(graph)
783
+ end
784
+
785
+ def test_should_generate_edges_for_each_transition
786
+ assert_equal 4, @edges.size
787
+ end
788
+
789
+ def test_should_use_event_name_for_edge_label
790
+ assert_equal 'park', @edges.first['label'].to_s.gsub('"', '')
791
+ end
792
+ end
793
+ rescue LoadError
794
+ $stderr.puts 'Skipping GraphViz StateMachine::Event tests. `gem install ruby-graphviz` >= v0.9.0 and try again.'
795
+ end