joelind-state_machine 0.8.1

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