verborghs-state_machine 0.9.4

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 (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