mattscilipoti-state_machine 0.8.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. data/CHANGELOG.rdoc +298 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +474 -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/tasks.rb +30 -0
  51. data/lib/state_machine/transition.rb +394 -0
  52. data/tasks/state_machine.rake +1 -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 +1367 -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 +155 -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