pluginaweek-state_machine 0.7.6

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 +273 -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 +429 -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 +251 -0
  33. data/lib/state_machine/event_collection.rb +113 -0
  34. data/lib/state_machine/extensions.rb +158 -0
  35. data/lib/state_machine/guard.rb +219 -0
  36. data/lib/state_machine/integrations.rb +68 -0
  37. data/lib/state_machine/integrations/active_record.rb +444 -0
  38. data/lib/state_machine/integrations/active_record/locale.rb +10 -0
  39. data/lib/state_machine/integrations/active_record/observer.rb +41 -0
  40. data/lib/state_machine/integrations/data_mapper.rb +325 -0
  41. data/lib/state_machine/integrations/data_mapper/observer.rb +139 -0
  42. data/lib/state_machine/integrations/sequel.rb +292 -0
  43. data/lib/state_machine/machine.rb +1431 -0
  44. data/lib/state_machine/machine_collection.rb +146 -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 +367 -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 +129 -0
  60. data/test/unit/event_collection_test.rb +293 -0
  61. data/test/unit/event_test.rb +605 -0
  62. data/test/unit/guard_test.rb +862 -0
  63. data/test/unit/integrations/active_record_test.rb +1001 -0
  64. data/test/unit/integrations/data_mapper_test.rb +694 -0
  65. data/test/unit/integrations/sequel_test.rb +486 -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 +710 -0
  70. data/test/unit/machine_test.rb +1910 -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 +1113 -0
  78. metadata +161 -0
@@ -0,0 +1 @@
1
+ require File.join("#{File.dirname(__FILE__)}/state_machine")
@@ -0,0 +1,30 @@
1
+ namespace :state_machine do
2
+ desc 'Draws a set of state machines using GraphViz. Target files to load with FILE=x,y,z; Machine class with CLASS=x,y,z; Font name with FONT=x; Image format with FORMAT=x; Orientation with ORIENTATION=x'
3
+ task :draw do
4
+ # Load the library
5
+ $:.unshift(File.dirname(__FILE__) + '/lib')
6
+ require 'state_machine'
7
+
8
+ # Build drawing options
9
+ options = {}
10
+ options[:file] = ENV['FILE'] if ENV['FILE']
11
+ options[:path] = ENV['TARGET'] if ENV['TARGET']
12
+ options[:format] = ENV['FORMAT'] if ENV['FORMAT']
13
+ options[:font] = ENV['FONT'] if ENV['FONT']
14
+ options[:orientation] = ENV['ORIENTATION'] if ENV['ORIENTATION']
15
+
16
+ StateMachine::Machine.draw(ENV['CLASS'], options)
17
+ end
18
+
19
+ namespace :draw do
20
+ desc 'Draws a set of state machines using GraphViz for a Ruby on Rails application. Target class with CLASS=x,y,z; Font name with FONT=x; Image format with FORMAT=x; Orientation with ORIENTATION=x'
21
+ task :rails => [:environment, 'state_machine:draw']
22
+
23
+ desc 'Draws a set of state machines using GraphViz for a Merb application. Target class with CLASS=x,y,z; Font name with FONT=x; Image format with FORMAT=x; Orientation with ORIENTATION=x'
24
+ task :merb => [:merb_env] do
25
+ # Fix ruby-graphviz being incompatible with Merb's process title
26
+ $0 = 'rake'
27
+ Rake::Task['state_machine:draw'].invoke
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,11 @@
1
+ class Switch
2
+ state_machine do
3
+ event :turn_on do
4
+ transition all => :on
5
+ end
6
+
7
+ event :turn_off do
8
+ transition all => :off
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,941 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ class AutoShop
4
+ attr_accessor :num_customers
5
+
6
+ def initialize
7
+ @num_customers = 0
8
+ super
9
+ end
10
+
11
+ state_machine :initial => :available do
12
+ after_transition :available => any, :do => :increment_customers
13
+ after_transition :busy => any, :do => :decrement_customers
14
+
15
+ event :tow_vehicle do
16
+ transition :available => :busy
17
+ end
18
+
19
+ event :fix_vehicle do
20
+ transition :busy => :available
21
+ end
22
+ end
23
+
24
+ # Increments the number of customers in service
25
+ def increment_customers
26
+ self.num_customers += 1
27
+ end
28
+
29
+ # Decrements the number of customers in service
30
+ def decrement_customers
31
+ self.num_customers -= 1
32
+ end
33
+ end
34
+
35
+ class ModelBase
36
+ def save
37
+ @saved = true
38
+ self
39
+ end
40
+ end
41
+
42
+ class Vehicle < ModelBase
43
+ attr_accessor :auto_shop, :seatbelt_on, :insurance_premium, :force_idle, :callbacks, :saved
44
+
45
+ def initialize(attributes = {})
46
+ attributes = {
47
+ :auto_shop => AutoShop.new,
48
+ :seatbelt_on => false,
49
+ :insurance_premium => 50,
50
+ :force_idle => false,
51
+ :callbacks => [],
52
+ :saved => false
53
+ }.merge(attributes)
54
+
55
+ attributes.each {|attr, value| send("#{attr}=", value)}
56
+ super()
57
+ end
58
+
59
+ # Defines the state machine for the state of the vehicled
60
+ state_machine :initial => lambda {|vehicle| vehicle.force_idle ? :idling : :parked}, :action => :save do
61
+ before_transition :parked => any, :do => :put_on_seatbelt
62
+ before_transition any => :stalled, :do => :increase_insurance_premium
63
+ after_transition any => :parked, :do => lambda {|vehicle| vehicle.seatbelt_on = false}
64
+ after_transition :on => :crash, :do => :tow
65
+ after_transition :on => :repair, :do => :fix
66
+
67
+ # Callback tracking for initial state callbacks
68
+ after_transition any => :parked, :do => lambda {|vehicle| vehicle.callbacks << 'before_enter_parked'}
69
+ before_transition any => :idling, :do => lambda {|vehicle| vehicle.callbacks << 'before_enter_idling'}
70
+
71
+ event :park do
72
+ transition [:idling, :first_gear] => :parked
73
+ end
74
+
75
+ event :ignite do
76
+ transition :stalled => :stalled
77
+ transition :parked => :idling
78
+ end
79
+
80
+ event :idle do
81
+ transition :first_gear => :idling
82
+ end
83
+
84
+ event :shift_up do
85
+ transition :idling => :first_gear, :first_gear => :second_gear, :second_gear => :third_gear
86
+ end
87
+
88
+ event :shift_down do
89
+ transition :third_gear => :second_gear
90
+ transition :second_gear => :first_gear
91
+ end
92
+
93
+ event :crash do
94
+ transition [:first_gear, :second_gear, :third_gear] => :stalled, :if => lambda {|vehicle| vehicle.auto_shop.available?}
95
+ end
96
+
97
+ event :repair do
98
+ transition :stalled => :parked, :if => :auto_shop_busy?
99
+ end
100
+ end
101
+
102
+ state_machine :insurance_state, :initial => :inactive, :namespace => 'insurance' do
103
+ event :buy do
104
+ transition :inactive => :active
105
+ end
106
+
107
+ event :cancel do
108
+ transition :active => :inactive
109
+ end
110
+ end
111
+
112
+ def save
113
+ super
114
+ end
115
+
116
+ def new_record?
117
+ @saved == false
118
+ end
119
+
120
+ def park
121
+ super
122
+ end
123
+
124
+ # Tows the vehicle to the auto shop
125
+ def tow
126
+ auto_shop.tow_vehicle
127
+ end
128
+
129
+ # Fixes the vehicle; it will no longer be in the auto shop
130
+ def fix
131
+ auto_shop.fix_vehicle
132
+ end
133
+
134
+ private
135
+ # Safety first! Puts on our seatbelt
136
+ def put_on_seatbelt
137
+ self.seatbelt_on = true
138
+ end
139
+
140
+ # We crashed! Increase the insurance premium on the vehicle
141
+ def increase_insurance_premium
142
+ self.insurance_premium += 100
143
+ end
144
+
145
+ # Is the auto shop currently servicing another customer?
146
+ def auto_shop_busy?
147
+ auto_shop.busy?
148
+ end
149
+ end
150
+
151
+ class Car < Vehicle
152
+ state_machine do
153
+ event :reverse do
154
+ transition [:parked, :idling, :first_gear] => :backing_up
155
+ end
156
+
157
+ event :park do
158
+ transition :backing_up => :parked
159
+ end
160
+
161
+ event :idle do
162
+ transition :backing_up => :idling
163
+ end
164
+
165
+ event :shift_up do
166
+ transition :backing_up => :first_gear
167
+ end
168
+ end
169
+ end
170
+
171
+ class Motorcycle < Vehicle
172
+ state_machine :initial => :idling
173
+ end
174
+
175
+ class TrafficLight
176
+ state_machine :initial => :stop do
177
+ event :cycle do
178
+ transition :stop => :proceed, :proceed=> :caution, :caution => :stop
179
+ end
180
+
181
+ state :stop do
182
+ def color(transform)
183
+ value = 'red'
184
+
185
+ if block_given?
186
+ yield value
187
+ else
188
+ value.send(transform)
189
+ end
190
+
191
+ value
192
+ end
193
+ end
194
+
195
+ state :proceed do
196
+ def color(transform)
197
+ 'green'
198
+ end
199
+ end
200
+
201
+ state :caution do
202
+ def color(transform)
203
+ 'yellow'
204
+ end
205
+ end
206
+ end
207
+
208
+ def color(transform = :to_s)
209
+ super
210
+ end
211
+ end
212
+
213
+ class VehicleTest < Test::Unit::TestCase
214
+ def setup
215
+ @vehicle = Vehicle.new
216
+ end
217
+
218
+ def test_should_not_allow_access_to_subclass_events
219
+ assert !@vehicle.respond_to?(:reverse)
220
+ end
221
+ end
222
+
223
+ class VehicleUnsavedTest < Test::Unit::TestCase
224
+ def setup
225
+ @vehicle = Vehicle.new
226
+ end
227
+
228
+ def test_should_be_in_parked_state
229
+ assert_equal 'parked', @vehicle.state
230
+ end
231
+
232
+ def test_should_raise_exception_if_checking_invalid_state
233
+ assert_raise(IndexError) { @vehicle.state?(:invalid) }
234
+ end
235
+
236
+ def test_should_raise_exception_if_getting_name_of_invalid_state
237
+ @vehicle.state = 'invalid'
238
+ assert_raise(ArgumentError) { @vehicle.state_name }
239
+ end
240
+
241
+ def test_should_be_parked
242
+ assert @vehicle.parked?
243
+ assert @vehicle.state?(:parked)
244
+ assert_equal :parked, @vehicle.state_name
245
+ end
246
+
247
+ def test_should_not_be_idling
248
+ assert !@vehicle.idling?
249
+ end
250
+
251
+ def test_should_not_be_first_gear
252
+ assert !@vehicle.first_gear?
253
+ end
254
+
255
+ def test_should_not_be_second_gear
256
+ assert !@vehicle.second_gear?
257
+ end
258
+
259
+ def test_should_not_be_stalled
260
+ assert !@vehicle.stalled?
261
+ end
262
+
263
+ def test_should_not_be_able_to_park
264
+ assert !@vehicle.can_park?
265
+ end
266
+
267
+ def test_should_not_have_a_transition_for_park
268
+ assert_nil @vehicle.park_transition
269
+ end
270
+
271
+ def test_should_not_allow_park
272
+ assert !@vehicle.park
273
+ end
274
+
275
+ def test_should_be_able_to_ignite
276
+ assert @vehicle.can_ignite?
277
+ end
278
+
279
+ def test_should_have_a_transition_for_ignite
280
+ transition = @vehicle.ignite_transition
281
+ assert_not_nil transition
282
+ assert_equal 'parked', transition.from
283
+ assert_equal 'idling', transition.to
284
+ assert_equal :ignite, transition.event
285
+ assert_equal :state, transition.attribute
286
+ assert_equal @vehicle, transition.object
287
+ end
288
+
289
+ def test_should_have_a_list_of_possible_events
290
+ assert_equal [:ignite], @vehicle.state_events
291
+ end
292
+
293
+ def test_should_have_a_list_of_possible_transitions
294
+ assert_equal [{:object => @vehicle, :attribute => :state, :event => :ignite, :from => 'parked', :to => 'idling'}], @vehicle.state_transitions.map {|transition| transition.attributes}
295
+ end
296
+
297
+ def test_should_allow_ignite
298
+ assert @vehicle.ignite
299
+ assert_equal 'idling', @vehicle.state
300
+ end
301
+
302
+ def test_should_allow_ignite_with_skipped_action
303
+ assert @vehicle.ignite(false)
304
+ assert @vehicle.new_record?
305
+ end
306
+
307
+ def test_should_allow_ignite_bang
308
+ assert @vehicle.ignite!
309
+ end
310
+
311
+ def test_should_allow_ignite_bang_with_skipped_action
312
+ assert @vehicle.ignite!(false)
313
+ assert @vehicle.new_record?
314
+ end
315
+
316
+ def test_should_be_saved_after_successful_event
317
+ @vehicle.ignite
318
+ assert !@vehicle.new_record?
319
+ end
320
+
321
+ def test_should_not_allow_idle
322
+ assert !@vehicle.idle
323
+ end
324
+
325
+ def test_should_not_allow_shift_up
326
+ assert !@vehicle.shift_up
327
+ end
328
+
329
+ def test_should_not_allow_shift_down
330
+ assert !@vehicle.shift_down
331
+ end
332
+
333
+ def test_should_not_allow_crash
334
+ assert !@vehicle.crash
335
+ end
336
+
337
+ def test_should_not_allow_repair
338
+ assert !@vehicle.repair
339
+ end
340
+
341
+ def test_should_be_insurance_inactive
342
+ assert @vehicle.insurance_inactive?
343
+ end
344
+
345
+ def test_should_be_able_to_buy
346
+ assert @vehicle.can_buy_insurance?
347
+ end
348
+
349
+ def test_should_allow_buying_insurance
350
+ assert @vehicle.buy_insurance
351
+ end
352
+
353
+ def test_should_allow_buying_insurance_bang
354
+ assert @vehicle.buy_insurance!
355
+ end
356
+
357
+ def test_should_allow_ignite_buying_insurance_with_skipped_action
358
+ assert @vehicle.buy_insurance!(false)
359
+ assert @vehicle.new_record?
360
+ end
361
+
362
+ def test_should_not_be_insurance_active
363
+ assert !@vehicle.insurance_active?
364
+ end
365
+
366
+ def test_should_not_be_able_to_cancel
367
+ assert !@vehicle.can_cancel_insurance?
368
+ end
369
+
370
+ def test_should_not_allow_cancelling_insurance
371
+ assert !@vehicle.cancel_insurance
372
+ end
373
+ end
374
+
375
+ class VehicleParkedTest < Test::Unit::TestCase
376
+ def setup
377
+ @vehicle = Vehicle.new
378
+ end
379
+
380
+ def test_should_be_in_parked_state
381
+ assert_equal 'parked', @vehicle.state
382
+ end
383
+
384
+ def test_should_not_have_the_seatbelt_on
385
+ assert !@vehicle.seatbelt_on
386
+ end
387
+
388
+ def test_should_not_allow_park
389
+ assert !@vehicle.park
390
+ end
391
+
392
+ def test_should_allow_ignite
393
+ assert @vehicle.ignite
394
+ assert_equal 'idling', @vehicle.state
395
+ end
396
+
397
+ def test_should_not_allow_idle
398
+ assert !@vehicle.idle
399
+ end
400
+
401
+ def test_should_not_allow_shift_up
402
+ assert !@vehicle.shift_up
403
+ end
404
+
405
+ def test_should_not_allow_shift_down
406
+ assert !@vehicle.shift_down
407
+ end
408
+
409
+ def test_should_not_allow_crash
410
+ assert !@vehicle.crash
411
+ end
412
+
413
+ def test_should_not_allow_repair
414
+ assert !@vehicle.repair
415
+ end
416
+
417
+ def test_should_raise_exception_if_repair_not_allowed!
418
+ assert_raise(StateMachine::InvalidTransition) {@vehicle.repair!}
419
+ end
420
+ end
421
+
422
+ class VehicleIdlingTest < Test::Unit::TestCase
423
+ def setup
424
+ @vehicle = Vehicle.new
425
+ @vehicle.ignite
426
+ end
427
+
428
+ def test_should_be_in_idling_state
429
+ assert_equal 'idling', @vehicle.state
430
+ end
431
+
432
+ def test_should_be_idling
433
+ assert @vehicle.idling?
434
+ end
435
+
436
+ def test_should_have_seatbelt_on
437
+ assert @vehicle.seatbelt_on
438
+ end
439
+
440
+ def test_should_allow_park
441
+ assert @vehicle.park
442
+ end
443
+
444
+ def test_should_call_park_with_bang_action
445
+ class << @vehicle
446
+ def park
447
+ super && 1
448
+ end
449
+ end
450
+
451
+ assert_equal 1, @vehicle.park!
452
+ end
453
+
454
+ def test_should_not_allow_idle
455
+ assert !@vehicle.idle
456
+ end
457
+
458
+ def test_should_allow_shift_up
459
+ assert @vehicle.shift_up
460
+ end
461
+
462
+ def test_should_not_allow_shift_down
463
+ assert !@vehicle.shift_down
464
+ end
465
+
466
+ def test_should_not_allow_crash
467
+ assert !@vehicle.crash
468
+ end
469
+
470
+ def test_should_not_allow_repair
471
+ assert !@vehicle.repair
472
+ end
473
+ end
474
+
475
+ class VehicleFirstGearTest < Test::Unit::TestCase
476
+ def setup
477
+ @vehicle = Vehicle.new
478
+ @vehicle.ignite
479
+ @vehicle.shift_up
480
+ end
481
+
482
+ def test_should_be_in_first_gear_state
483
+ assert_equal 'first_gear', @vehicle.state
484
+ end
485
+
486
+ def test_should_be_first_gear
487
+ assert @vehicle.first_gear?
488
+ end
489
+
490
+ def test_should_allow_park
491
+ assert @vehicle.park
492
+ end
493
+
494
+ def test_should_allow_idle
495
+ assert @vehicle.idle
496
+ end
497
+
498
+ def test_should_allow_shift_up
499
+ assert @vehicle.shift_up
500
+ end
501
+
502
+ def test_should_not_allow_shift_down
503
+ assert !@vehicle.shift_down
504
+ end
505
+
506
+ def test_should_allow_crash
507
+ assert @vehicle.crash
508
+ end
509
+
510
+ def test_should_not_allow_repair
511
+ assert !@vehicle.repair
512
+ end
513
+ end
514
+
515
+ class VehicleSecondGearTest < Test::Unit::TestCase
516
+ def setup
517
+ @vehicle = Vehicle.new
518
+ @vehicle.ignite
519
+ 2.times {@vehicle.shift_up}
520
+ end
521
+
522
+ def test_should_be_in_second_gear_state
523
+ assert_equal 'second_gear', @vehicle.state
524
+ end
525
+
526
+ def test_should_be_second_gear
527
+ assert @vehicle.second_gear?
528
+ end
529
+
530
+ def test_should_not_allow_park
531
+ assert !@vehicle.park
532
+ end
533
+
534
+ def test_should_not_allow_idle
535
+ assert !@vehicle.idle
536
+ end
537
+
538
+ def test_should_allow_shift_up
539
+ assert @vehicle.shift_up
540
+ end
541
+
542
+ def test_should_allow_shift_down
543
+ assert @vehicle.shift_down
544
+ end
545
+
546
+ def test_should_allow_crash
547
+ assert @vehicle.crash
548
+ end
549
+
550
+ def test_should_not_allow_repair
551
+ assert !@vehicle.repair
552
+ end
553
+ end
554
+
555
+ class VehicleThirdGearTest < Test::Unit::TestCase
556
+ def setup
557
+ @vehicle = Vehicle.new
558
+ @vehicle.ignite
559
+ 3.times {@vehicle.shift_up}
560
+ end
561
+
562
+ def test_should_be_in_third_gear_state
563
+ assert_equal 'third_gear', @vehicle.state
564
+ end
565
+
566
+ def test_should_be_third_gear
567
+ assert @vehicle.third_gear?
568
+ end
569
+
570
+ def test_should_not_allow_park
571
+ assert !@vehicle.park
572
+ end
573
+
574
+ def test_should_not_allow_idle
575
+ assert !@vehicle.idle
576
+ end
577
+
578
+ def test_should_not_allow_shift_up
579
+ assert !@vehicle.shift_up
580
+ end
581
+
582
+ def test_should_allow_shift_down
583
+ assert @vehicle.shift_down
584
+ end
585
+
586
+ def test_should_allow_crash
587
+ assert @vehicle.crash
588
+ end
589
+
590
+ def test_should_not_allow_repair
591
+ assert !@vehicle.repair
592
+ end
593
+ end
594
+
595
+ class VehicleStalledTest < Test::Unit::TestCase
596
+ def setup
597
+ @vehicle = Vehicle.new
598
+ @vehicle.ignite
599
+ @vehicle.shift_up
600
+ @vehicle.crash
601
+ end
602
+
603
+ def test_should_be_in_stalled_state
604
+ assert_equal 'stalled', @vehicle.state
605
+ end
606
+
607
+ def test_should_be_stalled
608
+ assert @vehicle.stalled?
609
+ end
610
+
611
+ def test_should_be_towed
612
+ assert @vehicle.auto_shop.busy?
613
+ assert_equal 1, @vehicle.auto_shop.num_customers
614
+ end
615
+
616
+ def test_should_have_an_increased_insurance_premium
617
+ assert_equal 150, @vehicle.insurance_premium
618
+ end
619
+
620
+ def test_should_not_allow_park
621
+ assert !@vehicle.park
622
+ end
623
+
624
+ def test_should_allow_ignite
625
+ assert @vehicle.ignite
626
+ end
627
+
628
+ def test_should_not_change_state_when_ignited
629
+ assert_equal 'stalled', @vehicle.state
630
+ end
631
+
632
+ def test_should_not_allow_idle
633
+ assert !@vehicle.idle
634
+ end
635
+
636
+ def test_should_now_allow_shift_up
637
+ assert !@vehicle.shift_up
638
+ end
639
+
640
+ def test_should_not_allow_shift_down
641
+ assert !@vehicle.shift_down
642
+ end
643
+
644
+ def test_should_not_allow_crash
645
+ assert !@vehicle.crash
646
+ end
647
+
648
+ def test_should_allow_repair_if_auto_shop_is_busy
649
+ assert @vehicle.repair
650
+ end
651
+
652
+ def test_should_not_allow_repair_if_auto_shop_is_available
653
+ @vehicle.auto_shop.fix_vehicle
654
+ assert !@vehicle.repair
655
+ end
656
+ end
657
+
658
+ class VehicleRepairedTest < Test::Unit::TestCase
659
+ def setup
660
+ @vehicle = Vehicle.new
661
+ @vehicle.ignite
662
+ @vehicle.shift_up
663
+ @vehicle.crash
664
+ @vehicle.repair
665
+ end
666
+
667
+ def test_should_be_in_parked_state
668
+ assert_equal 'parked', @vehicle.state
669
+ end
670
+
671
+ def test_should_not_have_a_busy_auto_shop
672
+ assert @vehicle.auto_shop.available?
673
+ end
674
+ end
675
+
676
+ class VehicleWithParallelEventsTest < Test::Unit::TestCase
677
+ def setup
678
+ @vehicle = Vehicle.new
679
+ end
680
+
681
+ def test_should_fail_if_any_event_cannot_transition
682
+ assert !@vehicle.fire_events(:ignite, :cancel_insurance)
683
+ end
684
+
685
+ def test_should_be_successful_if_all_events_transition
686
+ assert @vehicle.fire_events(:ignite, :buy_insurance)
687
+ end
688
+
689
+ def test_should_not_save_if_skipping_action
690
+ assert @vehicle.fire_events(:ignite, :buy_insurance, false)
691
+ assert !@vehicle.saved
692
+ end
693
+
694
+ def test_should_raise_exception_if_any_event_cannot_transition_on_bang
695
+ assert_raise(StateMachine::InvalidTransition) { @vehicle.fire_events!(:ignite, :cancel_insurance) }
696
+ end
697
+
698
+ def test_should_not_raise_exception_if_all_events_transition_on_bang
699
+ assert @vehicle.fire_events!(:ignite, :buy_insurance)
700
+ end
701
+
702
+ def test_should_not_save_if_skipping_action_on_bang
703
+ assert @vehicle.fire_events!(:ignite, :buy_insurance, false)
704
+ assert !@vehicle.saved
705
+ end
706
+ end
707
+
708
+ class VehicleWithEventAttributesTest < Test::Unit::TestCase
709
+ def setup
710
+ @vehicle = Vehicle.new
711
+ @vehicle.state_event = 'ignite'
712
+ end
713
+
714
+ def test_should_fail_if_event_is_invalid
715
+ @vehicle.state_event = 'invalid'
716
+ assert !@vehicle.save
717
+ assert_equal 'parked', @vehicle.state
718
+ end
719
+
720
+ def test_should_fail_if_event_has_no_transition
721
+ @vehicle.state_event = 'park'
722
+ assert !@vehicle.save
723
+ assert_equal 'parked', @vehicle.state
724
+ end
725
+
726
+ def test_should_return_original_action_value_on_success
727
+ assert_equal @vehicle, @vehicle.save
728
+ end
729
+
730
+ def test_should_transition_state_on_success
731
+ @vehicle.save
732
+ assert_equal 'idling', @vehicle.state
733
+ end
734
+ end
735
+
736
+ class MotorcycleTest < Test::Unit::TestCase
737
+ def setup
738
+ @motorcycle = Motorcycle.new
739
+ end
740
+
741
+ def test_should_be_in_idling_state
742
+ assert_equal 'idling', @motorcycle.state
743
+ end
744
+
745
+ def test_should_allow_park
746
+ assert @motorcycle.park
747
+ end
748
+
749
+ def test_should_not_allow_ignite
750
+ assert !@motorcycle.ignite
751
+ end
752
+
753
+ def test_should_allow_shift_up
754
+ assert @motorcycle.shift_up
755
+ end
756
+
757
+ def test_should_not_allow_shift_down
758
+ assert !@motorcycle.shift_down
759
+ end
760
+
761
+ def test_should_not_allow_crash
762
+ assert !@motorcycle.crash
763
+ end
764
+
765
+ def test_should_not_allow_repair
766
+ assert !@motorcycle.repair
767
+ end
768
+ end
769
+
770
+ class CarTest < Test::Unit::TestCase
771
+ def setup
772
+ @car = Car.new
773
+ end
774
+
775
+ def test_should_be_in_parked_state
776
+ assert_equal 'parked', @car.state
777
+ end
778
+
779
+ def test_should_not_have_the_seatbelt_on
780
+ assert !@car.seatbelt_on
781
+ end
782
+
783
+ def test_should_not_allow_park
784
+ assert !@car.park
785
+ end
786
+
787
+ def test_should_allow_ignite
788
+ assert @car.ignite
789
+ assert_equal 'idling', @car.state
790
+ end
791
+
792
+ def test_should_not_allow_idle
793
+ assert !@car.idle
794
+ end
795
+
796
+ def test_should_not_allow_shift_up
797
+ assert !@car.shift_up
798
+ end
799
+
800
+ def test_should_not_allow_shift_down
801
+ assert !@car.shift_down
802
+ end
803
+
804
+ def test_should_not_allow_crash
805
+ assert !@car.crash
806
+ end
807
+
808
+ def test_should_not_allow_repair
809
+ assert !@car.repair
810
+ end
811
+
812
+ def test_should_allow_reverse
813
+ assert @car.reverse
814
+ end
815
+ end
816
+
817
+ class CarBackingUpTest < Test::Unit::TestCase
818
+ def setup
819
+ @car = Car.new
820
+ @car.reverse
821
+ end
822
+
823
+ def test_should_be_in_backing_up_state
824
+ assert_equal 'backing_up', @car.state
825
+ end
826
+
827
+ def test_should_allow_park
828
+ assert @car.park
829
+ end
830
+
831
+ def test_should_not_allow_ignite
832
+ assert !@car.ignite
833
+ end
834
+
835
+ def test_should_allow_idle
836
+ assert @car.idle
837
+ end
838
+
839
+ def test_should_allow_shift_up
840
+ assert @car.shift_up
841
+ end
842
+
843
+ def test_should_not_allow_shift_down
844
+ assert !@car.shift_down
845
+ end
846
+
847
+ def test_should_not_allow_crash
848
+ assert !@car.crash
849
+ end
850
+
851
+ def test_should_not_allow_repair
852
+ assert !@car.repair
853
+ end
854
+
855
+ def test_should_not_allow_reverse
856
+ assert !@car.reverse
857
+ end
858
+ end
859
+
860
+ class AutoShopAvailableTest < Test::Unit::TestCase
861
+ def setup
862
+ @auto_shop = AutoShop.new
863
+ end
864
+
865
+ def test_should_be_in_available_state
866
+ assert_equal 'available', @auto_shop.state
867
+ end
868
+
869
+ def test_should_allow_tow_vehicle
870
+ assert @auto_shop.tow_vehicle
871
+ end
872
+
873
+ def test_should_not_allow_fix_vehicle
874
+ assert !@auto_shop.fix_vehicle
875
+ end
876
+ end
877
+
878
+ class AutoShopBusyTest < Test::Unit::TestCase
879
+ def setup
880
+ @auto_shop = AutoShop.new
881
+ @auto_shop.tow_vehicle
882
+ end
883
+
884
+ def test_should_be_in_busy_state
885
+ assert_equal 'busy', @auto_shop.state
886
+ end
887
+
888
+ def test_should_have_incremented_number_of_customers
889
+ assert_equal 1, @auto_shop.num_customers
890
+ end
891
+
892
+ def test_should_not_allow_tow_vehicle
893
+ assert !@auto_shop.tow_vehicle
894
+ end
895
+
896
+ def test_should_allow_fix_vehicle
897
+ assert @auto_shop.fix_vehicle
898
+ end
899
+ end
900
+
901
+ class TrafficLightStopTest < Test::Unit::TestCase
902
+ def setup
903
+ @light = TrafficLight.new
904
+ @light.state = 'stop'
905
+ end
906
+
907
+ def test_should_use_stop_color
908
+ assert_equal 'red', @light.color
909
+ end
910
+
911
+ def test_should_pass_arguments_through
912
+ assert_equal 'RED', @light.color(:upcase!)
913
+ end
914
+
915
+ def test_should_pass_block_through
916
+ color = @light.color {|value| value.upcase!}
917
+ assert_equal 'RED', color
918
+ end
919
+ end
920
+
921
+ class TrafficLightProceedTest < Test::Unit::TestCase
922
+ def setup
923
+ @light = TrafficLight.new
924
+ @light.state = 'proceed'
925
+ end
926
+
927
+ def test_should_use_proceed_color
928
+ assert_equal 'green', @light.color
929
+ end
930
+ end
931
+
932
+ class TrafficLightCautionTest < Test::Unit::TestCase
933
+ def setup
934
+ @light = TrafficLight.new
935
+ @light.state = 'caution'
936
+ end
937
+
938
+ def test_should_use_caution_color
939
+ assert_equal 'yellow', @light.color
940
+ end
941
+ end