spree-state_machine 2.0.0.beta1

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 (140) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.travis.yml +12 -0
  4. data/.yardopts +5 -0
  5. data/CHANGELOG.md +502 -0
  6. data/Gemfile +3 -0
  7. data/LICENSE +20 -0
  8. data/README.md +1246 -0
  9. data/Rakefile +20 -0
  10. data/examples/AutoShop_state.png +0 -0
  11. data/examples/Car_state.png +0 -0
  12. data/examples/Gemfile +5 -0
  13. data/examples/Gemfile.lock +14 -0
  14. data/examples/TrafficLight_state.png +0 -0
  15. data/examples/Vehicle_state.png +0 -0
  16. data/examples/auto_shop.rb +13 -0
  17. data/examples/car.rb +21 -0
  18. data/examples/doc/AutoShop.html +2856 -0
  19. data/examples/doc/AutoShop_state.png +0 -0
  20. data/examples/doc/Car.html +919 -0
  21. data/examples/doc/Car_state.png +0 -0
  22. data/examples/doc/TrafficLight.html +2230 -0
  23. data/examples/doc/TrafficLight_state.png +0 -0
  24. data/examples/doc/Vehicle.html +7921 -0
  25. data/examples/doc/Vehicle_state.png +0 -0
  26. data/examples/doc/_index.html +136 -0
  27. data/examples/doc/class_list.html +47 -0
  28. data/examples/doc/css/common.css +1 -0
  29. data/examples/doc/css/full_list.css +55 -0
  30. data/examples/doc/css/style.css +322 -0
  31. data/examples/doc/file_list.html +46 -0
  32. data/examples/doc/frames.html +13 -0
  33. data/examples/doc/index.html +136 -0
  34. data/examples/doc/js/app.js +205 -0
  35. data/examples/doc/js/full_list.js +173 -0
  36. data/examples/doc/js/jquery.js +16 -0
  37. data/examples/doc/method_list.html +734 -0
  38. data/examples/doc/top-level-namespace.html +105 -0
  39. data/examples/merb-rest/controller.rb +51 -0
  40. data/examples/merb-rest/model.rb +28 -0
  41. data/examples/merb-rest/view_edit.html.erb +24 -0
  42. data/examples/merb-rest/view_index.html.erb +23 -0
  43. data/examples/merb-rest/view_new.html.erb +13 -0
  44. data/examples/merb-rest/view_show.html.erb +17 -0
  45. data/examples/rails-rest/controller.rb +43 -0
  46. data/examples/rails-rest/migration.rb +7 -0
  47. data/examples/rails-rest/model.rb +23 -0
  48. data/examples/rails-rest/view__form.html.erb +34 -0
  49. data/examples/rails-rest/view_edit.html.erb +6 -0
  50. data/examples/rails-rest/view_index.html.erb +25 -0
  51. data/examples/rails-rest/view_new.html.erb +5 -0
  52. data/examples/rails-rest/view_show.html.erb +19 -0
  53. data/examples/traffic_light.rb +9 -0
  54. data/examples/vehicle.rb +33 -0
  55. data/lib/state_machine/assertions.rb +36 -0
  56. data/lib/state_machine/branch.rb +225 -0
  57. data/lib/state_machine/callback.rb +236 -0
  58. data/lib/state_machine/core.rb +7 -0
  59. data/lib/state_machine/core_ext/class/state_machine.rb +5 -0
  60. data/lib/state_machine/core_ext.rb +2 -0
  61. data/lib/state_machine/error.rb +13 -0
  62. data/lib/state_machine/eval_helpers.rb +87 -0
  63. data/lib/state_machine/event.rb +257 -0
  64. data/lib/state_machine/event_collection.rb +141 -0
  65. data/lib/state_machine/extensions.rb +149 -0
  66. data/lib/state_machine/graph.rb +92 -0
  67. data/lib/state_machine/helper_module.rb +17 -0
  68. data/lib/state_machine/initializers/rails.rb +25 -0
  69. data/lib/state_machine/initializers.rb +4 -0
  70. data/lib/state_machine/integrations/active_model/locale.rb +11 -0
  71. data/lib/state_machine/integrations/active_model/observer.rb +33 -0
  72. data/lib/state_machine/integrations/active_model/observer_update.rb +42 -0
  73. data/lib/state_machine/integrations/active_model/versions.rb +31 -0
  74. data/lib/state_machine/integrations/active_model.rb +585 -0
  75. data/lib/state_machine/integrations/active_record/locale.rb +20 -0
  76. data/lib/state_machine/integrations/active_record/versions.rb +123 -0
  77. data/lib/state_machine/integrations/active_record.rb +525 -0
  78. data/lib/state_machine/integrations/base.rb +100 -0
  79. data/lib/state_machine/integrations.rb +121 -0
  80. data/lib/state_machine/machine.rb +2287 -0
  81. data/lib/state_machine/machine_collection.rb +74 -0
  82. data/lib/state_machine/macro_methods.rb +522 -0
  83. data/lib/state_machine/matcher.rb +123 -0
  84. data/lib/state_machine/matcher_helpers.rb +54 -0
  85. data/lib/state_machine/node_collection.rb +222 -0
  86. data/lib/state_machine/path.rb +120 -0
  87. data/lib/state_machine/path_collection.rb +90 -0
  88. data/lib/state_machine/state.rb +297 -0
  89. data/lib/state_machine/state_collection.rb +112 -0
  90. data/lib/state_machine/state_context.rb +138 -0
  91. data/lib/state_machine/transition.rb +470 -0
  92. data/lib/state_machine/transition_collection.rb +245 -0
  93. data/lib/state_machine/version.rb +3 -0
  94. data/lib/state_machine/yard/handlers/base.rb +32 -0
  95. data/lib/state_machine/yard/handlers/event.rb +25 -0
  96. data/lib/state_machine/yard/handlers/machine.rb +344 -0
  97. data/lib/state_machine/yard/handlers/state.rb +25 -0
  98. data/lib/state_machine/yard/handlers/transition.rb +47 -0
  99. data/lib/state_machine/yard/handlers.rb +12 -0
  100. data/lib/state_machine/yard/templates/default/class/html/setup.rb +30 -0
  101. data/lib/state_machine/yard/templates/default/class/html/state_machines.erb +12 -0
  102. data/lib/state_machine/yard/templates.rb +3 -0
  103. data/lib/state_machine/yard.rb +8 -0
  104. data/lib/state_machine.rb +8 -0
  105. data/lib/yard-state_machine.rb +2 -0
  106. data/state_machine.gemspec +22 -0
  107. data/test/files/en.yml +17 -0
  108. data/test/files/switch.rb +15 -0
  109. data/test/functional/state_machine_test.rb +1066 -0
  110. data/test/test_helper.rb +7 -0
  111. data/test/unit/assertions_test.rb +40 -0
  112. data/test/unit/branch_test.rb +969 -0
  113. data/test/unit/callback_test.rb +704 -0
  114. data/test/unit/error_test.rb +43 -0
  115. data/test/unit/eval_helpers_test.rb +270 -0
  116. data/test/unit/event_collection_test.rb +398 -0
  117. data/test/unit/event_test.rb +1196 -0
  118. data/test/unit/graph_test.rb +98 -0
  119. data/test/unit/helper_module_test.rb +17 -0
  120. data/test/unit/integrations/active_model_test.rb +1245 -0
  121. data/test/unit/integrations/active_record_test.rb +2551 -0
  122. data/test/unit/integrations/base_test.rb +104 -0
  123. data/test/unit/integrations_test.rb +71 -0
  124. data/test/unit/invalid_event_test.rb +20 -0
  125. data/test/unit/invalid_parallel_transition_test.rb +18 -0
  126. data/test/unit/invalid_transition_test.rb +115 -0
  127. data/test/unit/machine_collection_test.rb +603 -0
  128. data/test/unit/machine_test.rb +3395 -0
  129. data/test/unit/matcher_helpers_test.rb +37 -0
  130. data/test/unit/matcher_test.rb +155 -0
  131. data/test/unit/node_collection_test.rb +362 -0
  132. data/test/unit/path_collection_test.rb +266 -0
  133. data/test/unit/path_test.rb +485 -0
  134. data/test/unit/state_collection_test.rb +352 -0
  135. data/test/unit/state_context_test.rb +441 -0
  136. data/test/unit/state_machine_test.rb +31 -0
  137. data/test/unit/state_test.rb +1101 -0
  138. data/test/unit/transition_collection_test.rb +2168 -0
  139. data/test/unit/transition_test.rb +1558 -0
  140. metadata +264 -0
@@ -0,0 +1,1066 @@
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, :time_elapsed, :last_transition_args
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 {|vehicle, transition| vehicle.last_transition_args = transition.args}
62
+ before_transition :parked => any, :do => :put_on_seatbelt
63
+ before_transition any => :stalled, :do => :increase_insurance_premium
64
+ after_transition any => :parked, :do => lambda {|vehicle| vehicle.seatbelt_on = false}
65
+ after_transition :on => :crash, :do => :tow
66
+ after_transition :on => :repair, :do => :fix
67
+
68
+ # Callback tracking for initial state callbacks
69
+ after_transition any => :parked, :do => lambda {|vehicle| vehicle.callbacks << 'before_enter_parked'}
70
+ before_transition any => :idling, :do => lambda {|vehicle| vehicle.callbacks << 'before_enter_idling'}
71
+
72
+ around_transition do |vehicle, transition, block|
73
+ time = Time.now
74
+ block.call
75
+ vehicle.time_elapsed = Time.now - time
76
+ end
77
+
78
+ event all do
79
+ transition :locked => :parked
80
+ end
81
+
82
+ event :park do
83
+ transition [:idling, :first_gear] => :parked
84
+ end
85
+
86
+ event :ignite do
87
+ transition :stalled => :stalled
88
+ transition :parked => :idling
89
+ end
90
+
91
+ event :idle do
92
+ transition :first_gear => :idling
93
+ end
94
+
95
+ event :shift_up do
96
+ transition :idling => :first_gear, :first_gear => :second_gear, :second_gear => :third_gear
97
+ end
98
+
99
+ event :shift_down do
100
+ transition :third_gear => :second_gear
101
+ transition :second_gear => :first_gear
102
+ end
103
+
104
+ event :crash do
105
+ transition [:first_gear, :second_gear, :third_gear] => :stalled, :if => lambda {|vehicle| vehicle.auto_shop.available?}
106
+ end
107
+
108
+ event :repair do
109
+ transition :stalled => :parked, :if => :auto_shop_busy?
110
+ end
111
+ end
112
+
113
+ state_machine :insurance_state, :initial => :inactive, :namespace => 'insurance' do
114
+ event :buy do
115
+ transition :inactive => :active
116
+ end
117
+
118
+ event :cancel do
119
+ transition :active => :inactive
120
+ end
121
+ end
122
+
123
+ def save
124
+ super
125
+ end
126
+
127
+ def new_record?
128
+ @saved == false
129
+ end
130
+
131
+ def park
132
+ super
133
+ end
134
+
135
+ # Tows the vehicle to the auto shop
136
+ def tow
137
+ auto_shop.tow_vehicle
138
+ end
139
+
140
+ # Fixes the vehicle; it will no longer be in the auto shop
141
+ def fix
142
+ auto_shop.fix_vehicle
143
+ end
144
+
145
+ def decibels
146
+ 0.0
147
+ end
148
+
149
+ private
150
+ # Safety first! Puts on our seatbelt
151
+ def put_on_seatbelt
152
+ self.seatbelt_on = true
153
+ end
154
+
155
+ # We crashed! Increase the insurance premium on the vehicle
156
+ def increase_insurance_premium
157
+ self.insurance_premium += 100
158
+ end
159
+
160
+ # Is the auto shop currently servicing another customer?
161
+ def auto_shop_busy?
162
+ auto_shop.busy?
163
+ end
164
+ end
165
+
166
+ class Car < Vehicle
167
+ state_machine do
168
+ event :reverse do
169
+ transition [:parked, :idling, :first_gear] => :backing_up
170
+ end
171
+
172
+ event :park do
173
+ transition :backing_up => :parked
174
+ end
175
+
176
+ event :idle do
177
+ transition :backing_up => :idling
178
+ end
179
+
180
+ event :shift_up do
181
+ transition :backing_up => :first_gear
182
+ end
183
+ end
184
+ end
185
+
186
+ class Motorcycle < Vehicle
187
+ state_machine :initial => :idling do
188
+ state :first_gear do
189
+ def decibels
190
+ 1.0
191
+ end
192
+ end
193
+ end
194
+ end
195
+
196
+ class TrafficLight
197
+ state_machine :initial => :stop do
198
+ event :cycle do
199
+ transition :stop => :proceed, :proceed => :caution, :caution => :stop
200
+ end
201
+
202
+ state :stop do
203
+ def color(transform)
204
+ value = 'red'
205
+
206
+ if block_given?
207
+ yield value
208
+ else
209
+ value.send(transform)
210
+ end
211
+
212
+ value
213
+ end
214
+ end
215
+
216
+ state all - :proceed do
217
+ def capture_violations?
218
+ true
219
+ end
220
+ end
221
+
222
+ state :proceed do
223
+ def color(transform)
224
+ 'green'
225
+ end
226
+
227
+ def capture_violations?
228
+ false
229
+ end
230
+ end
231
+
232
+ state :caution do
233
+ def color(transform)
234
+ 'yellow'
235
+ end
236
+ end
237
+ end
238
+
239
+ def color(transform = :to_s)
240
+ super
241
+ end
242
+ end
243
+
244
+ class VehicleTest < Test::Unit::TestCase
245
+ def setup
246
+ @vehicle = Vehicle.new
247
+ end
248
+
249
+ def test_should_not_allow_access_to_subclass_events
250
+ assert !@vehicle.respond_to?(:reverse)
251
+ end
252
+
253
+ def test_should_have_human_state_names
254
+ assert_equal 'parked', Vehicle.human_state_name(:parked)
255
+ end
256
+
257
+ def test_should_have_human_state_event_names
258
+ assert_equal 'park', Vehicle.human_state_event_name(:park)
259
+ end
260
+ end
261
+
262
+ class VehicleUnsavedTest < Test::Unit::TestCase
263
+ def setup
264
+ @vehicle = Vehicle.new
265
+ end
266
+
267
+ def test_should_be_in_parked_state
268
+ assert_equal 'parked', @vehicle.state
269
+ end
270
+
271
+ def test_should_raise_exception_if_checking_invalid_state
272
+ assert_raise(IndexError) { @vehicle.state?(:invalid) }
273
+ end
274
+
275
+ def test_should_raise_exception_if_getting_name_of_invalid_state
276
+ @vehicle.state = 'invalid'
277
+ assert_raise(ArgumentError) { @vehicle.state_name }
278
+ end
279
+
280
+ def test_should_be_parked
281
+ assert @vehicle.parked?
282
+ assert @vehicle.state?(:parked)
283
+ assert_equal :parked, @vehicle.state_name
284
+ assert_equal 'parked', @vehicle.human_state_name
285
+ end
286
+
287
+ def test_should_not_be_idling
288
+ assert !@vehicle.idling?
289
+ end
290
+
291
+ def test_should_not_be_first_gear
292
+ assert !@vehicle.first_gear?
293
+ end
294
+
295
+ def test_should_not_be_second_gear
296
+ assert !@vehicle.second_gear?
297
+ end
298
+
299
+ def test_should_not_be_stalled
300
+ assert !@vehicle.stalled?
301
+ end
302
+
303
+ def test_should_not_be_able_to_park
304
+ assert !@vehicle.can_park?
305
+ end
306
+
307
+ def test_should_not_have_a_transition_for_park
308
+ assert_nil @vehicle.park_transition
309
+ end
310
+
311
+ def test_should_not_allow_park
312
+ assert !@vehicle.park
313
+ end
314
+
315
+ def test_should_be_able_to_ignite
316
+ assert @vehicle.can_ignite?
317
+ end
318
+
319
+ def test_should_have_a_transition_for_ignite
320
+ transition = @vehicle.ignite_transition
321
+ assert_not_nil transition
322
+ assert_equal 'parked', transition.from
323
+ assert_equal 'idling', transition.to
324
+ assert_equal :ignite, transition.event
325
+ assert_equal :state, transition.attribute
326
+ assert_equal @vehicle, transition.object
327
+ end
328
+
329
+ def test_should_have_a_list_of_possible_events
330
+ assert_equal [:ignite], @vehicle.state_events
331
+ end
332
+
333
+ def test_should_have_a_list_of_possible_transitions
334
+ assert_equal [{:object => @vehicle, :attribute => :state, :event => :ignite, :from => 'parked', :to => 'idling'}], @vehicle.state_transitions.map {|transition| transition.attributes}
335
+ end
336
+
337
+ def test_should_have_a_list_of_possible_paths
338
+ assert_equal [[
339
+ StateMachine::Transition.new(@vehicle, Vehicle.state_machine, :ignite, :parked, :idling),
340
+ StateMachine::Transition.new(@vehicle, Vehicle.state_machine, :shift_up, :idling, :first_gear)
341
+ ]], @vehicle.state_paths(:to => :first_gear)
342
+ end
343
+
344
+ def test_should_allow_generic_event_to_fire
345
+ assert @vehicle.fire_state_event(:ignite)
346
+ assert_equal 'idling', @vehicle.state
347
+ end
348
+
349
+ def test_should_pass_arguments_through_to_generic_event_runner
350
+ @vehicle.fire_state_event(:ignite, 1, 2, 3)
351
+ assert_equal [1, 2, 3], @vehicle.last_transition_args
352
+ end
353
+
354
+ def test_should_allow_skipping_action_through_generic_event_runner
355
+ @vehicle.fire_state_event(:ignite, false)
356
+ assert_equal false, @vehicle.saved
357
+ end
358
+
359
+ def test_should_raise_error_with_invalid_event_through_generic_event_runer
360
+ assert_raise(IndexError) { @vehicle.fire_state_event(:invalid) }
361
+ end
362
+
363
+ def test_should_allow_ignite
364
+ assert @vehicle.ignite
365
+ assert_equal 'idling', @vehicle.state
366
+ end
367
+
368
+ def test_should_allow_ignite_with_skipped_action
369
+ assert @vehicle.ignite(false)
370
+ assert @vehicle.new_record?
371
+ end
372
+
373
+ def test_should_allow_ignite_bang
374
+ assert @vehicle.ignite!
375
+ end
376
+
377
+ def test_should_allow_ignite_bang_with_skipped_action
378
+ assert @vehicle.ignite!(false)
379
+ assert @vehicle.new_record?
380
+ end
381
+
382
+ def test_should_be_saved_after_successful_event
383
+ @vehicle.ignite
384
+ assert !@vehicle.new_record?
385
+ end
386
+
387
+ def test_should_not_allow_idle
388
+ assert !@vehicle.idle
389
+ end
390
+
391
+ def test_should_not_allow_shift_up
392
+ assert !@vehicle.shift_up
393
+ end
394
+
395
+ def test_should_not_allow_shift_down
396
+ assert !@vehicle.shift_down
397
+ end
398
+
399
+ def test_should_not_allow_crash
400
+ assert !@vehicle.crash
401
+ end
402
+
403
+ def test_should_not_allow_repair
404
+ assert !@vehicle.repair
405
+ end
406
+
407
+ def test_should_be_insurance_inactive
408
+ assert @vehicle.insurance_inactive?
409
+ end
410
+
411
+ def test_should_be_able_to_buy
412
+ assert @vehicle.can_buy_insurance?
413
+ end
414
+
415
+ def test_should_allow_buying_insurance
416
+ assert @vehicle.buy_insurance
417
+ end
418
+
419
+ def test_should_allow_buying_insurance_bang
420
+ assert @vehicle.buy_insurance!
421
+ end
422
+
423
+ def test_should_allow_ignite_buying_insurance_with_skipped_action
424
+ assert @vehicle.buy_insurance!(false)
425
+ assert @vehicle.new_record?
426
+ end
427
+
428
+ def test_should_not_be_insurance_active
429
+ assert !@vehicle.insurance_active?
430
+ end
431
+
432
+ def test_should_not_be_able_to_cancel
433
+ assert !@vehicle.can_cancel_insurance?
434
+ end
435
+
436
+ def test_should_not_allow_cancelling_insurance
437
+ assert !@vehicle.cancel_insurance
438
+ end
439
+ end
440
+
441
+ class VehicleParkedTest < Test::Unit::TestCase
442
+ def setup
443
+ @vehicle = Vehicle.new
444
+ end
445
+
446
+ def test_should_be_in_parked_state
447
+ assert_equal 'parked', @vehicle.state
448
+ end
449
+
450
+ def test_should_not_have_the_seatbelt_on
451
+ assert !@vehicle.seatbelt_on
452
+ end
453
+
454
+ def test_should_not_allow_park
455
+ assert !@vehicle.park
456
+ end
457
+
458
+ def test_should_allow_ignite
459
+ assert @vehicle.ignite
460
+ assert_equal 'idling', @vehicle.state
461
+ end
462
+
463
+ def test_should_not_allow_idle
464
+ assert !@vehicle.idle
465
+ end
466
+
467
+ def test_should_not_allow_shift_up
468
+ assert !@vehicle.shift_up
469
+ end
470
+
471
+ def test_should_not_allow_shift_down
472
+ assert !@vehicle.shift_down
473
+ end
474
+
475
+ def test_should_not_allow_crash
476
+ assert !@vehicle.crash
477
+ end
478
+
479
+ def test_should_not_allow_repair
480
+ assert !@vehicle.repair
481
+ end
482
+
483
+ def test_should_raise_exception_if_repair_not_allowed!
484
+ exception = assert_raise(StateMachine::InvalidTransition) {@vehicle.repair!}
485
+ assert_equal @vehicle, exception.object
486
+ assert_equal Vehicle.state_machine(:state), exception.machine
487
+ assert_equal :repair, exception.event
488
+ assert_equal 'parked', exception.from
489
+ end
490
+ end
491
+
492
+ class VehicleIdlingTest < Test::Unit::TestCase
493
+ def setup
494
+ @vehicle = Vehicle.new
495
+ @vehicle.ignite
496
+ end
497
+
498
+ def test_should_be_in_idling_state
499
+ assert_equal 'idling', @vehicle.state
500
+ end
501
+
502
+ def test_should_be_idling
503
+ assert @vehicle.idling?
504
+ end
505
+
506
+ def test_should_have_seatbelt_on
507
+ assert @vehicle.seatbelt_on
508
+ end
509
+
510
+ def test_should_track_time_elapsed
511
+ assert_not_nil @vehicle.time_elapsed
512
+ end
513
+
514
+ def test_should_allow_park
515
+ assert @vehicle.park
516
+ end
517
+
518
+ def test_should_call_park_with_bang_action
519
+ class << @vehicle
520
+ def park
521
+ super && 1
522
+ end
523
+ end
524
+
525
+ assert_equal 1, @vehicle.park!
526
+ end
527
+
528
+ def test_should_not_allow_idle
529
+ assert !@vehicle.idle
530
+ end
531
+
532
+ def test_should_allow_shift_up
533
+ assert @vehicle.shift_up
534
+ end
535
+
536
+ def test_should_not_allow_shift_down
537
+ assert !@vehicle.shift_down
538
+ end
539
+
540
+ def test_should_not_allow_crash
541
+ assert !@vehicle.crash
542
+ end
543
+
544
+ def test_should_not_allow_repair
545
+ assert !@vehicle.repair
546
+ end
547
+ end
548
+
549
+ class VehicleFirstGearTest < Test::Unit::TestCase
550
+ def setup
551
+ @vehicle = Vehicle.new
552
+ @vehicle.ignite
553
+ @vehicle.shift_up
554
+ end
555
+
556
+ def test_should_be_in_first_gear_state
557
+ assert_equal 'first_gear', @vehicle.state
558
+ end
559
+
560
+ def test_should_be_first_gear
561
+ assert @vehicle.first_gear?
562
+ end
563
+
564
+ def test_should_allow_park
565
+ assert @vehicle.park
566
+ end
567
+
568
+ def test_should_allow_idle
569
+ assert @vehicle.idle
570
+ end
571
+
572
+ def test_should_allow_shift_up
573
+ assert @vehicle.shift_up
574
+ end
575
+
576
+ def test_should_not_allow_shift_down
577
+ assert !@vehicle.shift_down
578
+ end
579
+
580
+ def test_should_allow_crash
581
+ assert @vehicle.crash
582
+ end
583
+
584
+ def test_should_not_allow_repair
585
+ assert !@vehicle.repair
586
+ end
587
+ end
588
+
589
+ class VehicleSecondGearTest < Test::Unit::TestCase
590
+ def setup
591
+ @vehicle = Vehicle.new
592
+ @vehicle.ignite
593
+ 2.times {@vehicle.shift_up}
594
+ end
595
+
596
+ def test_should_be_in_second_gear_state
597
+ assert_equal 'second_gear', @vehicle.state
598
+ end
599
+
600
+ def test_should_be_second_gear
601
+ assert @vehicle.second_gear?
602
+ end
603
+
604
+ def test_should_not_allow_park
605
+ assert !@vehicle.park
606
+ end
607
+
608
+ def test_should_not_allow_idle
609
+ assert !@vehicle.idle
610
+ end
611
+
612
+ def test_should_allow_shift_up
613
+ assert @vehicle.shift_up
614
+ end
615
+
616
+ def test_should_allow_shift_down
617
+ assert @vehicle.shift_down
618
+ end
619
+
620
+ def test_should_allow_crash
621
+ assert @vehicle.crash
622
+ end
623
+
624
+ def test_should_not_allow_repair
625
+ assert !@vehicle.repair
626
+ end
627
+ end
628
+
629
+ class VehicleThirdGearTest < Test::Unit::TestCase
630
+ def setup
631
+ @vehicle = Vehicle.new
632
+ @vehicle.ignite
633
+ 3.times {@vehicle.shift_up}
634
+ end
635
+
636
+ def test_should_be_in_third_gear_state
637
+ assert_equal 'third_gear', @vehicle.state
638
+ end
639
+
640
+ def test_should_be_third_gear
641
+ assert @vehicle.third_gear?
642
+ end
643
+
644
+ def test_should_not_allow_park
645
+ assert !@vehicle.park
646
+ end
647
+
648
+ def test_should_not_allow_idle
649
+ assert !@vehicle.idle
650
+ end
651
+
652
+ def test_should_not_allow_shift_up
653
+ assert !@vehicle.shift_up
654
+ end
655
+
656
+ def test_should_allow_shift_down
657
+ assert @vehicle.shift_down
658
+ end
659
+
660
+ def test_should_allow_crash
661
+ assert @vehicle.crash
662
+ end
663
+
664
+ def test_should_not_allow_repair
665
+ assert !@vehicle.repair
666
+ end
667
+ end
668
+
669
+ class VehicleStalledTest < Test::Unit::TestCase
670
+ def setup
671
+ @vehicle = Vehicle.new
672
+ @vehicle.ignite
673
+ @vehicle.shift_up
674
+ @vehicle.crash
675
+ end
676
+
677
+ def test_should_be_in_stalled_state
678
+ assert_equal 'stalled', @vehicle.state
679
+ end
680
+
681
+ def test_should_be_stalled
682
+ assert @vehicle.stalled?
683
+ end
684
+
685
+ def test_should_be_towed
686
+ assert @vehicle.auto_shop.busy?
687
+ assert_equal 1, @vehicle.auto_shop.num_customers
688
+ end
689
+
690
+ def test_should_have_an_increased_insurance_premium
691
+ assert_equal 150, @vehicle.insurance_premium
692
+ end
693
+
694
+ def test_should_not_allow_park
695
+ assert !@vehicle.park
696
+ end
697
+
698
+ def test_should_allow_ignite
699
+ assert @vehicle.ignite
700
+ end
701
+
702
+ def test_should_not_change_state_when_ignited
703
+ assert_equal 'stalled', @vehicle.state
704
+ end
705
+
706
+ def test_should_not_allow_idle
707
+ assert !@vehicle.idle
708
+ end
709
+
710
+ def test_should_now_allow_shift_up
711
+ assert !@vehicle.shift_up
712
+ end
713
+
714
+ def test_should_not_allow_shift_down
715
+ assert !@vehicle.shift_down
716
+ end
717
+
718
+ def test_should_not_allow_crash
719
+ assert !@vehicle.crash
720
+ end
721
+
722
+ def test_should_allow_repair_if_auto_shop_is_busy
723
+ assert @vehicle.repair
724
+ end
725
+
726
+ def test_should_not_allow_repair_if_auto_shop_is_available
727
+ @vehicle.auto_shop.fix_vehicle
728
+ assert !@vehicle.repair
729
+ end
730
+ end
731
+
732
+ class VehicleRepairedTest < Test::Unit::TestCase
733
+ def setup
734
+ @vehicle = Vehicle.new
735
+ @vehicle.ignite
736
+ @vehicle.shift_up
737
+ @vehicle.crash
738
+ @vehicle.repair
739
+ end
740
+
741
+ def test_should_be_in_parked_state
742
+ assert_equal 'parked', @vehicle.state
743
+ end
744
+
745
+ def test_should_not_have_a_busy_auto_shop
746
+ assert @vehicle.auto_shop.available?
747
+ end
748
+ end
749
+
750
+ class VehicleLockedTest < Test::Unit::TestCase
751
+ def setup
752
+ @vehicle = Vehicle.new
753
+ @vehicle.state = 'locked'
754
+ end
755
+
756
+ def test_should_be_parked_after_park
757
+ @vehicle.park
758
+ assert @vehicle.parked?
759
+ end
760
+
761
+ def test_should_be_parked_after_ignite
762
+ @vehicle.ignite
763
+ assert @vehicle.parked?
764
+ end
765
+
766
+ def test_should_be_parked_after_shift_up
767
+ @vehicle.shift_up
768
+ assert @vehicle.parked?
769
+ end
770
+
771
+ def test_should_be_parked_after_shift_down
772
+ @vehicle.shift_down
773
+ assert @vehicle.parked?
774
+ end
775
+ end
776
+
777
+ class VehicleWithParallelEventsTest < Test::Unit::TestCase
778
+ def setup
779
+ @vehicle = Vehicle.new
780
+ end
781
+
782
+ def test_should_fail_if_any_event_cannot_transition
783
+ assert !@vehicle.fire_events(:ignite, :cancel_insurance)
784
+ end
785
+
786
+ def test_should_be_successful_if_all_events_transition
787
+ assert @vehicle.fire_events(:ignite, :buy_insurance)
788
+ end
789
+
790
+ def test_should_not_save_if_skipping_action
791
+ assert @vehicle.fire_events(:ignite, :buy_insurance, false)
792
+ assert !@vehicle.saved
793
+ end
794
+
795
+ def test_should_raise_exception_if_any_event_cannot_transition_on_bang
796
+ exception = assert_raise(StateMachine::InvalidParallelTransition) { @vehicle.fire_events!(:ignite, :cancel_insurance) }
797
+ assert_equal @vehicle, exception.object
798
+ assert_equal [:ignite, :cancel_insurance], exception.events
799
+ end
800
+
801
+ def test_should_not_raise_exception_if_all_events_transition_on_bang
802
+ assert @vehicle.fire_events!(:ignite, :buy_insurance)
803
+ end
804
+
805
+ def test_should_not_save_if_skipping_action_on_bang
806
+ assert @vehicle.fire_events!(:ignite, :buy_insurance, false)
807
+ assert !@vehicle.saved
808
+ end
809
+ end
810
+
811
+ class VehicleWithEventAttributesTest < Test::Unit::TestCase
812
+ def setup
813
+ @vehicle = Vehicle.new
814
+ @vehicle.state_event = 'ignite'
815
+ end
816
+
817
+ def test_should_fail_if_event_is_invalid
818
+ @vehicle.state_event = 'invalid'
819
+ assert !@vehicle.save
820
+ assert_equal 'parked', @vehicle.state
821
+ end
822
+
823
+ def test_should_fail_if_event_has_no_transition
824
+ @vehicle.state_event = 'park'
825
+ assert !@vehicle.save
826
+ assert_equal 'parked', @vehicle.state
827
+ end
828
+
829
+ def test_should_return_original_action_value_on_success
830
+ assert_equal @vehicle, @vehicle.save
831
+ end
832
+
833
+ def test_should_transition_state_on_success
834
+ @vehicle.save
835
+ assert_equal 'idling', @vehicle.state
836
+ end
837
+ end
838
+
839
+ class MotorcycleTest < Test::Unit::TestCase
840
+ def setup
841
+ @motorcycle = Motorcycle.new
842
+ end
843
+
844
+ def test_should_be_in_idling_state
845
+ assert_equal 'idling', @motorcycle.state
846
+ end
847
+
848
+ def test_should_allow_park
849
+ assert @motorcycle.park
850
+ end
851
+
852
+ def test_should_not_allow_ignite
853
+ assert !@motorcycle.ignite
854
+ end
855
+
856
+ def test_should_allow_shift_up
857
+ assert @motorcycle.shift_up
858
+ end
859
+
860
+ def test_should_not_allow_shift_down
861
+ assert !@motorcycle.shift_down
862
+ end
863
+
864
+ def test_should_not_allow_crash
865
+ assert !@motorcycle.crash
866
+ end
867
+
868
+ def test_should_not_allow_repair
869
+ assert !@motorcycle.repair
870
+ end
871
+
872
+ def test_should_inherit_decibels_from_superclass
873
+ @motorcycle.park
874
+ assert_equal 0.0, @motorcycle.decibels
875
+ end
876
+
877
+ def test_should_use_decibels_defined_in_state
878
+ @motorcycle.shift_up
879
+ assert_equal 1.0, @motorcycle.decibels
880
+ end
881
+ end
882
+
883
+ class CarTest < Test::Unit::TestCase
884
+ def setup
885
+ @car = Car.new
886
+ end
887
+
888
+ def test_should_be_in_parked_state
889
+ assert_equal 'parked', @car.state
890
+ end
891
+
892
+ def test_should_not_have_the_seatbelt_on
893
+ assert !@car.seatbelt_on
894
+ end
895
+
896
+ def test_should_not_allow_park
897
+ assert !@car.park
898
+ end
899
+
900
+ def test_should_allow_ignite
901
+ assert @car.ignite
902
+ assert_equal 'idling', @car.state
903
+ end
904
+
905
+ def test_should_not_allow_idle
906
+ assert !@car.idle
907
+ end
908
+
909
+ def test_should_not_allow_shift_up
910
+ assert !@car.shift_up
911
+ end
912
+
913
+ def test_should_not_allow_shift_down
914
+ assert !@car.shift_down
915
+ end
916
+
917
+ def test_should_not_allow_crash
918
+ assert !@car.crash
919
+ end
920
+
921
+ def test_should_not_allow_repair
922
+ assert !@car.repair
923
+ end
924
+
925
+ def test_should_allow_reverse
926
+ assert @car.reverse
927
+ end
928
+ end
929
+
930
+ class CarBackingUpTest < Test::Unit::TestCase
931
+ def setup
932
+ @car = Car.new
933
+ @car.reverse
934
+ end
935
+
936
+ def test_should_be_in_backing_up_state
937
+ assert_equal 'backing_up', @car.state
938
+ end
939
+
940
+ def test_should_allow_park
941
+ assert @car.park
942
+ end
943
+
944
+ def test_should_not_allow_ignite
945
+ assert !@car.ignite
946
+ end
947
+
948
+ def test_should_allow_idle
949
+ assert @car.idle
950
+ end
951
+
952
+ def test_should_allow_shift_up
953
+ assert @car.shift_up
954
+ end
955
+
956
+ def test_should_not_allow_shift_down
957
+ assert !@car.shift_down
958
+ end
959
+
960
+ def test_should_not_allow_crash
961
+ assert !@car.crash
962
+ end
963
+
964
+ def test_should_not_allow_repair
965
+ assert !@car.repair
966
+ end
967
+
968
+ def test_should_not_allow_reverse
969
+ assert !@car.reverse
970
+ end
971
+ end
972
+
973
+ class AutoShopAvailableTest < Test::Unit::TestCase
974
+ def setup
975
+ @auto_shop = AutoShop.new
976
+ end
977
+
978
+ def test_should_be_in_available_state
979
+ assert_equal 'available', @auto_shop.state
980
+ end
981
+
982
+ def test_should_allow_tow_vehicle
983
+ assert @auto_shop.tow_vehicle
984
+ end
985
+
986
+ def test_should_not_allow_fix_vehicle
987
+ assert !@auto_shop.fix_vehicle
988
+ end
989
+ end
990
+
991
+ class AutoShopBusyTest < Test::Unit::TestCase
992
+ def setup
993
+ @auto_shop = AutoShop.new
994
+ @auto_shop.tow_vehicle
995
+ end
996
+
997
+ def test_should_be_in_busy_state
998
+ assert_equal 'busy', @auto_shop.state
999
+ end
1000
+
1001
+ def test_should_have_incremented_number_of_customers
1002
+ assert_equal 1, @auto_shop.num_customers
1003
+ end
1004
+
1005
+ def test_should_not_allow_tow_vehicle
1006
+ assert !@auto_shop.tow_vehicle
1007
+ end
1008
+
1009
+ def test_should_allow_fix_vehicle
1010
+ assert @auto_shop.fix_vehicle
1011
+ end
1012
+ end
1013
+
1014
+ class TrafficLightStopTest < Test::Unit::TestCase
1015
+ def setup
1016
+ @light = TrafficLight.new
1017
+ @light.state = 'stop'
1018
+ end
1019
+
1020
+ def test_should_use_stop_color
1021
+ assert_equal 'red', @light.color
1022
+ end
1023
+
1024
+ def test_should_pass_arguments_through
1025
+ assert_equal 'RED', @light.color(:upcase!)
1026
+ end
1027
+
1028
+ def test_should_pass_block_through
1029
+ color = @light.color {|value| value.upcase!}
1030
+ assert_equal 'RED', color
1031
+ end
1032
+
1033
+ def test_should_use_stop_capture_violations
1034
+ assert_equal true, @light.capture_violations?
1035
+ end
1036
+ end
1037
+
1038
+ class TrafficLightProceedTest < Test::Unit::TestCase
1039
+ def setup
1040
+ @light = TrafficLight.new
1041
+ @light.state = 'proceed'
1042
+ end
1043
+
1044
+ def test_should_use_proceed_color
1045
+ assert_equal 'green', @light.color
1046
+ end
1047
+
1048
+ def test_should_use_proceed_capture_violations
1049
+ assert_equal false, @light.capture_violations?
1050
+ end
1051
+ end
1052
+
1053
+ class TrafficLightCautionTest < Test::Unit::TestCase
1054
+ def setup
1055
+ @light = TrafficLight.new
1056
+ @light.state = 'caution'
1057
+ end
1058
+
1059
+ def test_should_use_caution_color
1060
+ assert_equal 'yellow', @light.color
1061
+ end
1062
+
1063
+ def test_should_use_caution_capture_violations
1064
+ assert_equal true, @light.capture_violations?
1065
+ end
1066
+ end