joelind-state_machine 0.8.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 +297 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +466 -0
  4. data/Rakefile +98 -0
  5. data/examples/AutoShop_state.png +0 -0
  6. data/examples/Car_state.png +0 -0
  7. data/examples/TrafficLight_state.png +0 -0
  8. data/examples/Vehicle_state.png +0 -0
  9. data/examples/auto_shop.rb +11 -0
  10. data/examples/car.rb +19 -0
  11. data/examples/merb-rest/controller.rb +51 -0
  12. data/examples/merb-rest/model.rb +28 -0
  13. data/examples/merb-rest/view_edit.html.erb +24 -0
  14. data/examples/merb-rest/view_index.html.erb +23 -0
  15. data/examples/merb-rest/view_new.html.erb +13 -0
  16. data/examples/merb-rest/view_show.html.erb +17 -0
  17. data/examples/rails-rest/controller.rb +43 -0
  18. data/examples/rails-rest/migration.rb +11 -0
  19. data/examples/rails-rest/model.rb +23 -0
  20. data/examples/rails-rest/view_edit.html.erb +25 -0
  21. data/examples/rails-rest/view_index.html.erb +23 -0
  22. data/examples/rails-rest/view_new.html.erb +14 -0
  23. data/examples/rails-rest/view_show.html.erb +17 -0
  24. data/examples/traffic_light.rb +7 -0
  25. data/examples/vehicle.rb +31 -0
  26. data/init.rb +1 -0
  27. data/lib/state_machine.rb +388 -0
  28. data/lib/state_machine/assertions.rb +36 -0
  29. data/lib/state_machine/callback.rb +189 -0
  30. data/lib/state_machine/condition_proxy.rb +94 -0
  31. data/lib/state_machine/eval_helpers.rb +67 -0
  32. data/lib/state_machine/event.rb +252 -0
  33. data/lib/state_machine/event_collection.rb +122 -0
  34. data/lib/state_machine/extensions.rb +149 -0
  35. data/lib/state_machine/guard.rb +230 -0
  36. data/lib/state_machine/integrations.rb +68 -0
  37. data/lib/state_machine/integrations/active_record.rb +492 -0
  38. data/lib/state_machine/integrations/active_record/locale.rb +11 -0
  39. data/lib/state_machine/integrations/active_record/observer.rb +41 -0
  40. data/lib/state_machine/integrations/data_mapper.rb +351 -0
  41. data/lib/state_machine/integrations/data_mapper/observer.rb +139 -0
  42. data/lib/state_machine/integrations/sequel.rb +322 -0
  43. data/lib/state_machine/machine.rb +1467 -0
  44. data/lib/state_machine/machine_collection.rb +155 -0
  45. data/lib/state_machine/matcher.rb +123 -0
  46. data/lib/state_machine/matcher_helpers.rb +54 -0
  47. data/lib/state_machine/node_collection.rb +152 -0
  48. data/lib/state_machine/state.rb +249 -0
  49. data/lib/state_machine/state_collection.rb +112 -0
  50. data/lib/state_machine/transition.rb +394 -0
  51. data/tasks/state_machine.rake +1 -0
  52. data/tasks/state_machine.rb +30 -0
  53. data/test/classes/switch.rb +11 -0
  54. data/test/functional/state_machine_test.rb +941 -0
  55. data/test/test_helper.rb +4 -0
  56. data/test/unit/assertions_test.rb +40 -0
  57. data/test/unit/callback_test.rb +455 -0
  58. data/test/unit/condition_proxy_test.rb +328 -0
  59. data/test/unit/eval_helpers_test.rb +120 -0
  60. data/test/unit/event_collection_test.rb +326 -0
  61. data/test/unit/event_test.rb +743 -0
  62. data/test/unit/guard_test.rb +908 -0
  63. data/test/unit/integrations/active_record_test.rb +1374 -0
  64. data/test/unit/integrations/data_mapper_test.rb +962 -0
  65. data/test/unit/integrations/sequel_test.rb +859 -0
  66. data/test/unit/integrations_test.rb +42 -0
  67. data/test/unit/invalid_event_test.rb +7 -0
  68. data/test/unit/invalid_transition_test.rb +7 -0
  69. data/test/unit/machine_collection_test.rb +938 -0
  70. data/test/unit/machine_test.rb +2004 -0
  71. data/test/unit/matcher_helpers_test.rb +37 -0
  72. data/test/unit/matcher_test.rb +155 -0
  73. data/test/unit/node_collection_test.rb +207 -0
  74. data/test/unit/state_collection_test.rb +280 -0
  75. data/test/unit/state_machine_test.rb +31 -0
  76. data/test/unit/state_test.rb +795 -0
  77. data/test/unit/transition_test.rb +1212 -0
  78. metadata +163 -0
@@ -0,0 +1,42 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ class IntegrationMatcherTest < Test::Unit::TestCase
4
+ def setup
5
+ @klass = Class.new
6
+ end
7
+
8
+ def test_should_return_nil_if_no_match_found
9
+ assert_nil StateMachine::Integrations.match(@klass)
10
+ end
11
+
12
+ def test_should_return_integration_class_if_match_found
13
+ integration = Module.new do
14
+ def self.matches?(klass)
15
+ true
16
+ end
17
+ end
18
+ StateMachine::Integrations.const_set('Custom', integration)
19
+
20
+ assert_equal integration, StateMachine::Integrations.match(@klass)
21
+ ensure
22
+ StateMachine::Integrations.send(:remove_const, 'Custom')
23
+ end
24
+ end
25
+
26
+ class IntegrationFinderTest < Test::Unit::TestCase
27
+ def test_should_find_active_record
28
+ assert_equal StateMachine::Integrations::ActiveRecord, StateMachine::Integrations.find(:active_record)
29
+ end
30
+
31
+ def test_should_find_data_mapper
32
+ assert_equal StateMachine::Integrations::DataMapper, StateMachine::Integrations.find(:data_mapper)
33
+ end
34
+
35
+ def test_should_find_sequel
36
+ assert_equal StateMachine::Integrations::Sequel, StateMachine::Integrations.find(:sequel)
37
+ end
38
+
39
+ def test_should_raise_an_exception_if_invalid
40
+ assert_raise(NameError) { StateMachine::Integrations.find(:invalid) }
41
+ end
42
+ end
@@ -0,0 +1,7 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ class InvalidEventTest < Test::Unit::TestCase
4
+ def test_should_exist
5
+ assert_not_nil StateMachine::InvalidEvent
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ class InvalidTransitionTest < Test::Unit::TestCase
4
+ def test_should_exist
5
+ assert_not_nil StateMachine::InvalidTransition
6
+ end
7
+ end
@@ -0,0 +1,938 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ class MachineCollectionByDefaultTest < Test::Unit::TestCase
4
+ def setup
5
+ @machines = StateMachine::MachineCollection.new
6
+ end
7
+
8
+ def test_should_not_have_any_machines
9
+ assert @machines.empty?
10
+ end
11
+ end
12
+
13
+ class MachineCollectionStateInitializationTest < Test::Unit::TestCase
14
+ def setup
15
+ @machines = StateMachine::MachineCollection.new
16
+
17
+ @klass = Class.new
18
+
19
+ @machines[:state] = StateMachine::Machine.new(@klass, :state, :initial => :parked)
20
+ @machines[:alarm_state] = StateMachine::Machine.new(@klass, :alarm_state, :initial => lambda {|object| :active})
21
+ @machines[:alarm_state].state :active, :value => lambda {'active'}
22
+
23
+ # Prevent the auto-initialization hook from firing
24
+ @klass.class_eval do
25
+ def initialize
26
+ end
27
+ end
28
+
29
+ @object = @klass.new
30
+ @object.state = nil
31
+ @object.alarm_state = nil
32
+ end
33
+
34
+ def test_should_set_states_if_nil
35
+ @machines.initialize_states(@object)
36
+
37
+ assert_equal 'parked', @object.state
38
+ assert_equal 'active', @object.alarm_state
39
+ end
40
+
41
+ def test_should_set_states_if_empty
42
+ @object.state = ''
43
+ @object.alarm_state = ''
44
+ @machines.initialize_states(@object)
45
+
46
+ assert_equal 'parked', @object.state
47
+ assert_equal 'active', @object.alarm_state
48
+ end
49
+
50
+ def test_should_not_set_states_if_not_empty
51
+ @object.state = 'idling'
52
+ @object.alarm_state = 'off'
53
+ @machines.initialize_states(@object)
54
+
55
+ assert_equal 'idling', @object.state
56
+ assert_equal 'off', @object.alarm_state
57
+ end
58
+
59
+ def test_should_only_initialize_static_states_if_dynamic_disabled
60
+ @machines.initialize_states(@object, :dynamic => false)
61
+
62
+ assert_equal 'parked', @object.state
63
+ assert_nil @object.alarm_state
64
+ end
65
+
66
+ def test_should_only_initialize_dynamic_states_if_dynamic_enabled
67
+ @machines.initialize_states(@object, :dynamic => true)
68
+
69
+ assert_nil @object.state
70
+ assert_equal 'active', @object.alarm_state
71
+ end
72
+
73
+ def test_should_not_set_states_if_ignored
74
+ @machines.initialize_states(@object, :ignore => [:state, :alarm_state])
75
+
76
+ assert_nil @object.state
77
+ assert_nil @object.alarm_state
78
+ end
79
+
80
+ def test_should_set_states_if_not_ignored_and_nil
81
+ @machines.initialize_states(@object, :ignore => [])
82
+
83
+ assert_equal 'parked', @object.state
84
+ assert_equal 'active', @object.alarm_state
85
+ end
86
+
87
+ def test_should_set_states_if_not_ignored_and_empty
88
+ @object.state = ''
89
+ @object.alarm_state = ''
90
+ @machines.initialize_states(@object, :ignore => [])
91
+
92
+ assert_equal 'parked', @object.state
93
+ assert_equal 'active', @object.alarm_state
94
+ end
95
+
96
+ def test_should_set_states_if_not_ignored_and_not_empty
97
+ @object.state = 'idling'
98
+ @object.alarm_state = 'inactive'
99
+ @machines.initialize_states(@object, :ignore => [])
100
+
101
+ assert_equal 'parked', @object.state
102
+ assert_equal 'active', @object.alarm_state
103
+ end
104
+ end
105
+
106
+ class MachineCollectionFireExplicitTest < Test::Unit::TestCase
107
+ def setup
108
+ @machines = StateMachine::MachineCollection.new
109
+
110
+ @klass = Class.new do
111
+ attr_reader :saved
112
+
113
+ def save
114
+ @saved = true
115
+ end
116
+ end
117
+
118
+ # First machine
119
+ @machines[:state] = @state = StateMachine::Machine.new(@klass, :state, :initial => :parked, :action => :save)
120
+ @state.event :ignite do
121
+ transition :parked => :idling
122
+ end
123
+ @state.event :park do
124
+ transition :idling => :parked
125
+ end
126
+
127
+ # Second machine
128
+ @machines[:alarm_state] = @alarm_state = StateMachine::Machine.new(@klass, :alarm_state, :initial => :active, :action => :save, :namespace => 'alarm')
129
+ @alarm_state.event :enable do
130
+ transition :off => :active
131
+ end
132
+ @alarm_state.event :disable do
133
+ transition :active => :off
134
+ end
135
+
136
+ @object = @klass.new
137
+ end
138
+
139
+ def test_should_raise_exception_if_invalid_event_specified
140
+ exception = assert_raise(StateMachine::InvalidEvent) { @machines.fire_events(@object, :invalid) }
141
+ assert_equal ':invalid is an unknown state machine event', exception.message
142
+
143
+ exception = assert_raise(StateMachine::InvalidEvent) { @machines.fire_events(@object, :ignite, :invalid) }
144
+ assert_equal ':invalid is an unknown state machine event', exception.message
145
+ end
146
+
147
+ def test_should_fail_if_any_event_cannot_transition
148
+ assert !@machines.fire_events(@object, :park, :disable_alarm)
149
+ assert_equal 'parked', @object.state
150
+ assert_equal 'active', @object.alarm_state
151
+ assert !@object.saved
152
+
153
+ assert !@machines.fire_events(@object, :ignite, :enable_alarm)
154
+ assert_equal 'parked', @object.state
155
+ assert_equal 'active', @object.alarm_state
156
+ assert !@object.saved
157
+ end
158
+
159
+ def test_should_be_successful_if_all_events_transition
160
+ assert @machines.fire_events(@object, :ignite, :disable_alarm)
161
+ assert_equal 'idling', @object.state
162
+ assert_equal 'off', @object.alarm_state
163
+ assert @object.saved
164
+ end
165
+
166
+ def test_should_not_save_if_skipping_action
167
+ assert @machines.fire_events(@object, :ignite, :disable_alarm, false)
168
+ assert_equal 'idling', @object.state
169
+ assert_equal 'off', @object.alarm_state
170
+ assert !@object.saved
171
+ end
172
+ end
173
+
174
+ class MachineCollectionFireExplicitWithTransactionsTest < Test::Unit::TestCase
175
+ def setup
176
+ @machines = StateMachine::MachineCollection.new
177
+
178
+ @klass = Class.new do
179
+ attr_accessor :allow_save
180
+
181
+ def save
182
+ @allow_save
183
+ end
184
+ end
185
+
186
+ StateMachine::Integrations.const_set('Custom', Module.new do
187
+ attr_reader :rolled_back
188
+
189
+ def transaction(object)
190
+ @rolled_back = yield
191
+ end
192
+ end)
193
+
194
+ # First machine
195
+ @machines[:state] = @state = StateMachine::Machine.new(@klass, :state, :initial => :parked, :action => :save, :integration => :custom)
196
+ @state.event :ignite do
197
+ transition :parked => :idling
198
+ end
199
+
200
+ # Second machine
201
+ @machines[:alarm_state] = @alarm_state = StateMachine::Machine.new(@klass, :alarm_state, :initial => :active, :action => :save, :namespace => 'alarm', :integration => :custom)
202
+ @alarm_state.event :disable do
203
+ transition :active => :off
204
+ end
205
+
206
+ @object = @klass.new
207
+ end
208
+
209
+ def test_should_not_rollback_if_successful
210
+ @object.allow_save = true
211
+
212
+ assert @machines.fire_events(@object, :ignite, :disable_alarm)
213
+ assert_equal true, @state.rolled_back
214
+ assert_nil @alarm_state.rolled_back
215
+ assert_equal 'idling', @object.state
216
+ assert_equal 'off', @object.alarm_state
217
+ end
218
+
219
+ def test_should_rollback_if_not_successful
220
+ @object.allow_save = false
221
+
222
+ assert !@machines.fire_events(@object, :ignite, :disable_alarm)
223
+ assert_equal false, @state.rolled_back
224
+ assert_nil @alarm_state.rolled_back
225
+ assert_equal 'parked', @object.state
226
+ assert_equal 'active', @object.alarm_state
227
+ end
228
+
229
+ def teardown
230
+ StateMachine::Integrations.send(:remove_const, 'Custom')
231
+ end
232
+ end
233
+
234
+ class MachineCollectionFireExplicitWithValidationsTest < Test::Unit::TestCase
235
+ def setup
236
+ StateMachine::Integrations.const_set('Custom', Module.new do
237
+ def invalidate(object, attribute, message, values = [])
238
+ (object.errors ||= []) << generate_message(message, values)
239
+ end
240
+
241
+ def reset(object)
242
+ object.errors = []
243
+ end
244
+ end)
245
+
246
+ @klass = Class.new do
247
+ attr_accessor :errors
248
+
249
+ def initialize
250
+ @errors = []
251
+ super
252
+ end
253
+ end
254
+
255
+ @machines = StateMachine::MachineCollection.new
256
+ @machines[:state] = @state = StateMachine::Machine.new(@klass, :state, :initial => :parked, :integration => :custom)
257
+ @state.event :ignite do
258
+ transition :parked => :idling
259
+ end
260
+
261
+ @machines[:alarm_state] = @alarm_state = StateMachine::Machine.new(@klass, :alarm_state, :initial => :active, :namespace => 'alarm', :integration => :custom)
262
+ @alarm_state.event :disable do
263
+ transition :active => :off
264
+ end
265
+
266
+ @object = @klass.new
267
+ end
268
+
269
+ def test_should_not_invalidate_if_transitions_exist
270
+ assert @machines.fire_events(@object, :ignite, :disable_alarm)
271
+ assert_equal [], @object.errors
272
+ end
273
+
274
+ def test_should_invalidate_if_no_transitions_exist
275
+ @object.state = 'idling'
276
+ @object.alarm_state = 'off'
277
+
278
+ assert !@machines.fire_events(@object, :ignite, :disable_alarm)
279
+ assert_equal ['cannot transition via "ignite"', 'cannot transition via "disable_alarm"'], @object.errors
280
+ end
281
+
282
+ def teardown
283
+ StateMachine::Integrations.send(:remove_const, 'Custom')
284
+ end
285
+ end
286
+
287
+ class MachineCollectionFireImplicitTest < Test::Unit::TestCase
288
+ def setup
289
+ @klass = Class.new
290
+
291
+ @machines = StateMachine::MachineCollection.new
292
+ @machines[:state] = @machine = StateMachine::Machine.new(@klass, :state, :initial => :parked, :action => :save)
293
+ @machine.event :ignite do
294
+ transition :parked => :idling
295
+ end
296
+
297
+ @saved = false
298
+ @object = @klass.new
299
+ end
300
+
301
+ def default_test
302
+ end
303
+ end
304
+
305
+ class MachineCollectionFireImplicitWithoutEventTest < MachineCollectionFireImplicitTest
306
+ def setup
307
+ super
308
+
309
+ @object.state_event = nil
310
+ @result = @machines.fire_event_attributes(@object, :save) { @saved = true }
311
+ end
312
+
313
+ def test_should_be_successful
314
+ assert_equal true, @result
315
+ end
316
+
317
+ def test_should_run_action
318
+ assert @saved
319
+ end
320
+
321
+ def test_should_not_transition_state
322
+ assert_equal 'parked', @object.state
323
+ end
324
+
325
+ def test_should_not_change_event_attribute
326
+ assert_nil @object.state_event
327
+ end
328
+
329
+ def test_should_not_have_event_transition
330
+ assert_nil @object.send(:state_event_transition)
331
+ end
332
+ end
333
+
334
+ class MachineCollectionFireImplicitWithBlankEventTest < MachineCollectionFireImplicitTest
335
+ def setup
336
+ super
337
+
338
+ @object.state_event = ''
339
+ @result = @machines.fire_event_attributes(@object, :save) { @saved = true }
340
+ end
341
+
342
+ def test_should_be_successful
343
+ assert_equal true, @result
344
+ end
345
+
346
+ def test_should_run_action
347
+ assert @saved
348
+ end
349
+
350
+ def test_should_not_transition_state
351
+ assert_equal 'parked', @object.state
352
+ end
353
+
354
+ def test_should_not_change_event_attribute
355
+ assert_nil @object.state_event
356
+ end
357
+
358
+ def test_should_not_have_event_transition
359
+ assert_nil @object.send(:state_event_transition)
360
+ end
361
+ end
362
+
363
+ class MachineCollectionFireImplicitWithInvalidEventTest < MachineCollectionFireImplicitTest
364
+ def setup
365
+ super
366
+
367
+ @object.state_event = 'invalid'
368
+ @result = @machines.fire_event_attributes(@object, :save) { @saved = true }
369
+ end
370
+
371
+ def test_should_not_be_successful
372
+ assert_equal false, @result
373
+ end
374
+
375
+ def test_should_not_run_action
376
+ assert !@saved
377
+ end
378
+
379
+ def test_should_not_transition_state
380
+ assert_equal 'parked', @object.state
381
+ end
382
+
383
+ def test_should_not_reset_event_attribute
384
+ assert_equal :invalid, @object.state_event
385
+ end
386
+
387
+ def test_should_not_have_event_transition
388
+ assert_nil @object.send(:state_event_transition)
389
+ end
390
+ end
391
+
392
+ class MachineCollectionFireImplicitWithoutTransitionTest < MachineCollectionFireImplicitTest
393
+ def setup
394
+ super
395
+
396
+ @object.state = 'idling'
397
+ @object.state_event = 'ignite'
398
+ @result = @machines.fire_event_attributes(@object, :save) { @saved = true }
399
+ end
400
+
401
+ def test_should_not_be_successful
402
+ assert_equal false, @result
403
+ end
404
+
405
+ def test_should_not_run_action
406
+ assert !@saved
407
+ end
408
+
409
+ def test_should_not_transition_state
410
+ assert_equal 'idling', @object.state
411
+ end
412
+
413
+ def test_should_not_reset_event_attribute
414
+ assert_equal :ignite, @object.state_event
415
+ end
416
+
417
+ def test_should_not_have_event_transition
418
+ assert_nil @object.send(:state_event_transition)
419
+ end
420
+ end
421
+
422
+ class MachineCollectionFireImplicitWithTransitionTest < MachineCollectionFireImplicitTest
423
+ def setup
424
+ super
425
+
426
+ @state_event = nil
427
+
428
+ @object.state_event = 'ignite'
429
+ @result = @machines.fire_event_attributes(@object, :save) do
430
+ @state_event = @object.state_event
431
+ @saved = true
432
+ end
433
+ end
434
+
435
+ def test_should_be_successful
436
+ assert_equal true, @result
437
+ end
438
+
439
+ def test_should_run_action
440
+ assert @saved
441
+ end
442
+
443
+ def test_should_not_have_event_while_running_action
444
+ assert_nil @state_event
445
+ end
446
+
447
+ def test_should_transition_state
448
+ assert_equal 'idling', @object.state
449
+ end
450
+
451
+ def test_should_reset_event_attribute
452
+ assert_nil @object.state_event
453
+ end
454
+
455
+ def test_should_not_have_event_transition
456
+ assert_nil @object.send(:state_event_transition)
457
+ end
458
+
459
+ def test_should_not_be_successful_if_fired_again
460
+ @object.state_event = 'ignite'
461
+ assert !@machines.fire_event_attributes(@object, :save) { true }
462
+ end
463
+ end
464
+
465
+ class MachineCollectionFireImplicitWithNonBooleanResultTest < MachineCollectionFireImplicitTest
466
+ def setup
467
+ super
468
+
469
+ @action_value = Object.new
470
+
471
+ @object.state_event = 'ignite'
472
+ @result = @machines.fire_event_attributes(@object, :save) do
473
+ @saved = true
474
+ @action_value
475
+ end
476
+ end
477
+
478
+ def test_should_be_successful
479
+ assert_equal @action_value, @result
480
+ end
481
+
482
+ def test_should_run_action
483
+ assert @saved
484
+ end
485
+
486
+ def test_should_transition_state
487
+ assert_equal 'idling', @object.state
488
+ end
489
+ end
490
+
491
+ class MachineCollectionFireImplicitWithActionFailureTest < MachineCollectionFireImplicitTest
492
+ def setup
493
+ super
494
+
495
+ @object.state_event = 'ignite'
496
+ @result = @machines.fire_event_attributes(@object, :save) { false }
497
+ end
498
+
499
+ def test_should_not_be_successful
500
+ assert_equal false, @result
501
+ end
502
+
503
+ def test_should_not_transition_state
504
+ assert_equal 'parked', @object.state
505
+ end
506
+
507
+ def test_should_not_reset_event_attribute
508
+ assert_equal :ignite, @object.state_event
509
+ end
510
+
511
+ def test_should_not_have_event_transition
512
+ assert_nil @object.send(:state_event_transition)
513
+ end
514
+ end
515
+
516
+ class MachineCollectionFireImplicitWithActionErrorTest < MachineCollectionFireImplicitTest
517
+ def setup
518
+ super
519
+
520
+ @object.state_event = 'ignite'
521
+ assert_raise(ArgumentError) { @machines.fire_event_attributes(@object, :save) { raise ArgumentError } }
522
+ end
523
+
524
+ def test_should_not_transition_state
525
+ assert_equal 'parked', @object.state
526
+ end
527
+
528
+ def test_should_not_reset_event_attribute
529
+ assert_equal :ignite, @object.state_event
530
+ end
531
+
532
+ def test_should_not_have_event_transition
533
+ assert_nil @object.send(:state_event_transition)
534
+ end
535
+ end
536
+
537
+ class MachineCollectionFireImplicitPartialTest < MachineCollectionFireImplicitTest
538
+ def setup
539
+ super
540
+
541
+ @state_event = nil
542
+ @state_event_transition = nil
543
+
544
+ @object.state_event = 'ignite'
545
+ @result = @machines.fire_event_attributes(@object, :save, false) do
546
+ @state_event = @object.state_event
547
+ @state_event_transition = @object.send(:state_event_transition)
548
+ true
549
+ end
550
+ end
551
+
552
+ def test_should_be_successful
553
+ assert @result
554
+ end
555
+
556
+ def test_should_not_have_event_while_running_action
557
+ assert_nil @state_event
558
+ end
559
+
560
+ def test_should_not_have_event_transition_while_running_action
561
+ assert_nil @state_event_transition
562
+ end
563
+
564
+ def test_should_transition_state
565
+ assert_equal 'idling', @object.state
566
+ end
567
+
568
+ def test_should_reset_event_attribute
569
+ assert_nil @object.state_event
570
+ end
571
+
572
+ def test_should_have_event_transition
573
+ assert_not_nil @object.send(:state_event_transition)
574
+ end
575
+
576
+ def test_should_reset_event_after_next_fire_on_success
577
+ assert @machines.fire_event_attributes(@object, :save) { true }
578
+ assert_equal 'idling', @object.state
579
+ assert_nil @object.state_event
580
+ end
581
+
582
+ def test_should_reset_event_transition_after_next_fire_on_success
583
+ assert @machines.fire_event_attributes(@object, :save) { true }
584
+ assert_nil @object.send(:state_event_transition)
585
+ end
586
+
587
+ def test_should_guard_transition_after_next_fire_on_success
588
+ @machines.fire_event_attributes(@object, :save) { true }
589
+
590
+ @object.state = 'idling'
591
+ @object.state_event = 'ignite'
592
+ assert !@machines.fire_event_attributes(@object, :save) { true }
593
+ end
594
+
595
+ def test_should_rollback_all_attributes_after_next_fire_on_failure
596
+ assert !@machines.fire_event_attributes(@object, :save) { false }
597
+ assert_equal 'parked', @object.state
598
+ assert_equal :ignite, @object.state_event
599
+ assert_nil @object.send(:state_event_transition)
600
+
601
+ @object.state = 'idling'
602
+ assert !@machines.fire_event_attributes(@object, :save) { false }
603
+ end
604
+
605
+ def test_should_guard_transition_after_next_fire_on_failure
606
+ @machines.fire_event_attributes(@object, :save) { false }
607
+
608
+ @object.state = 'idling'
609
+ assert !@machines.fire_event_attributes(@object, :save) { true }
610
+ end
611
+
612
+ def test_should_rollback_all_attributes_after_next_fire_on_error
613
+ assert_raise(ArgumentError) { @machines.fire_event_attributes(@object, :save) { raise ArgumentError } }
614
+ assert_equal 'parked', @object.state
615
+ assert_equal :ignite, @object.state_event
616
+ assert_nil @object.send(:state_event_transition)
617
+ end
618
+
619
+ def test_should_guard_transition_after_next_fire_on_error
620
+ begin
621
+ @machines.fire_event_attributes(@object, :save) { raise ArgumentError }
622
+ rescue ArgumentError
623
+ end
624
+
625
+ @object.state = 'idling'
626
+ assert !@machines.fire_event_attributes(@object, :save) { true }
627
+ end
628
+ end
629
+
630
+ class MachineCollectionFireImplicitPartialWithCallbacksTest < MachineCollectionFireImplicitTest
631
+ def setup
632
+ super
633
+
634
+ @object.state_event = 'ignite'
635
+ end
636
+
637
+ def test_should_run_before_callbacks
638
+ ran_callback = false
639
+ @machine.before_transition { ran_callback = true }
640
+ @machines.fire_event_attributes(@object, :save, false) { true }
641
+
642
+ assert ran_callback
643
+ end
644
+
645
+ def test_should_not_have_event_during_before_callbacks
646
+ state_event = nil
647
+ @machine.before_transition {|object, transition| state_event = object.state_event }
648
+ @machines.fire_event_attributes(@object, :save, false) { true }
649
+
650
+ assert_nil state_event
651
+ end
652
+
653
+ def test_should_not_have_event_transition_during_before_callbacks
654
+ state_event_transition = nil
655
+ @machine.before_transition {|object, transition| state_event_transition = object.send(:state_event_transition) }
656
+ @machines.fire_event_attributes(@object, :save, false) { true }
657
+
658
+ assert_nil state_event_transition
659
+ end
660
+
661
+ def test_should_not_run_after_callbacks
662
+ ran_callback = false
663
+ @machine.after_transition { ran_callback = true }
664
+ @machines.fire_event_attributes(@object, :save, false) { true }
665
+
666
+ assert !ran_callback
667
+ end
668
+
669
+ def test_should_not_have_event_during_after_callbacks
670
+ state_event = nil
671
+ @machine.after_transition {|object, transition| state_event = object.state_event }
672
+ @machines.fire_event_attributes(@object, :save, false) { true }
673
+
674
+ assert_nil state_event
675
+ end
676
+
677
+ def test_should_not_have_event_transition_during_after_callbacks
678
+ state_event_transition = nil
679
+ @machine.after_transition {|object, transition| state_event_transition = object.send(:state_event_transition) }
680
+ @machines.fire_event_attributes(@object, :save, false) { true }
681
+
682
+ assert_nil state_event_transition
683
+ end
684
+ end
685
+
686
+ class MachineCollectionFireImplicitNestedPartialTest < MachineCollectionFireImplicitTest
687
+ def setup
688
+ super
689
+
690
+ @partial_result = nil
691
+
692
+ @object.state_event = 'ignite'
693
+ @result = @machines.fire_event_attributes(@object, :save) do
694
+ @partial_result = @machines.fire_event_attributes(@object, :save, false) { true }
695
+ true
696
+ end
697
+ end
698
+
699
+ def test_should_be_successful
700
+ assert @result
701
+ end
702
+
703
+ def test_should_have_successful_partial_fire
704
+ assert @partial_result
705
+ end
706
+
707
+ def test_should_transition_state
708
+ assert_equal 'idling', @object.state
709
+ end
710
+
711
+ def test_should_reset_event_attribute
712
+ assert_nil @object.state_event
713
+ end
714
+
715
+ def test_should_reset_event_transition_attribute
716
+ assert_nil @object.send(:state_event_transition)
717
+ end
718
+ end
719
+
720
+ class MachineCollectionFireImplicitWithDifferentActionsTest < MachineCollectionFireImplicitTest
721
+ def setup
722
+ super
723
+
724
+ @machines[:alarm_state] = @alarm_machine = StateMachine::Machine.new(@klass, :alarm_state, :initial => :active, :action => :save_alarm)
725
+ @alarm_machine.event :disable do
726
+ transition :active => :off
727
+ end
728
+
729
+ @saved = false
730
+ @object = @klass.new
731
+ @object.state_event = 'ignite'
732
+ @object.alarm_state_event = 'disable'
733
+
734
+ @machines.fire_event_attributes(@object, :save) { true }
735
+ end
736
+
737
+ def test_should_transition_states_for_action
738
+ assert_equal 'idling', @object.state
739
+ end
740
+
741
+ def test_should_reset_event_attribute_for_action
742
+ assert_nil @object.state_event
743
+ end
744
+
745
+ def test_should_reset_event_transition_attribute_for_action
746
+ assert_nil @object.send(:state_event_transition)
747
+ end
748
+
749
+ def test_should_not_transition_states_for_other_actions
750
+ assert_equal 'active', @object.alarm_state
751
+ end
752
+
753
+ def test_should_not_reset_event_attributes_for_other_actions
754
+ assert_equal :disable, @object.alarm_state_event
755
+ end
756
+ end
757
+
758
+ class MachineCollectionFireImplicitWithSameActionsTest < MachineCollectionFireImplicitTest
759
+ def setup
760
+ super
761
+
762
+ @machines[:alarm_state] = @alarm_machine = StateMachine::Machine.new(@klass, :alarm_state, :initial => :active, :action => :save)
763
+ @alarm_machine.event :disable do
764
+ transition :active => :off
765
+ end
766
+
767
+ @saved = false
768
+ @object = @klass.new
769
+ @object.state_event = 'ignite'
770
+ @object.alarm_state_event = 'disable'
771
+
772
+ @machines.fire_event_attributes(@object, :save) { true }
773
+ end
774
+
775
+ def test_should_transition_all_states_for_action
776
+ assert_equal 'idling', @object.state
777
+ assert_equal 'off', @object.alarm_state
778
+ end
779
+
780
+ def test_should_reset_all_event_attributes_for_action
781
+ assert_nil @object.state_event
782
+ assert_nil @object.alarm_state_event
783
+ end
784
+ end
785
+
786
+ class MachineCollectionFireImplicitWithValidationsTest < Test::Unit::TestCase
787
+ def setup
788
+ StateMachine::Integrations.const_set('Custom', Module.new do
789
+ def invalidate(object, attribute, message, values = [])
790
+ (object.errors ||= []) << generate_message(message, values)
791
+ end
792
+
793
+ def reset(object)
794
+ object.errors = []
795
+ end
796
+ end)
797
+
798
+ @klass = Class.new do
799
+ attr_accessor :errors
800
+
801
+ def initialize
802
+ @errors = []
803
+ super
804
+ end
805
+ end
806
+
807
+ @machines = StateMachine::MachineCollection.new
808
+ @machines[:state] = @machine = StateMachine::Machine.new(@klass, :state, :initial => :parked, :action => :save, :integration => :custom)
809
+ @machine.event :ignite do
810
+ transition :parked => :idling
811
+ end
812
+
813
+ @object = @klass.new
814
+ end
815
+
816
+ def test_should_invalidate_if_event_is_invalid
817
+ @object.state_event = 'invalid'
818
+ @machines.fire_event_attributes(@object, :save) { true }
819
+
820
+ assert !@object.errors.empty?
821
+ end
822
+
823
+ def test_should_invalidate_if_no_transition_exists
824
+ @object.state = 'idling'
825
+ @object.state_event = 'ignite'
826
+ @machines.fire_event_attributes(@object, :save) { true }
827
+
828
+ assert !@object.errors.empty?
829
+ end
830
+
831
+ def test_should_not_invalidate_if_transition_exists
832
+ @object.state_event = 'ignite'
833
+ @machines.fire_event_attributes(@object, :save) { true }
834
+
835
+ assert @object.errors.empty?
836
+ end
837
+
838
+ def teardown
839
+ StateMachine::Integrations.send(:remove_const, 'Custom')
840
+ end
841
+ end
842
+
843
+ class MachineCollectionFireImplicitWithCustomMachineNameTest < MachineCollectionFireImplicitTest
844
+ def setup
845
+ super
846
+
847
+ @object.state_event = 'ignite'
848
+ end
849
+
850
+ def test_should_be_successful_on_complete_file
851
+ assert @machines.fire_event_attributes(@object, :save) { true }
852
+ assert_equal 'idling', @object.state
853
+ assert_nil @object.state_event
854
+ assert_nil @object.send(:state_event_transition)
855
+ end
856
+
857
+ def test_should_be_successful_on_partial_fire
858
+ @machines.fire_event_attributes(@object, :save, false) { true }
859
+ assert_equal 'idling', @object.state
860
+ assert_nil @object.state_event
861
+ assert_not_nil @object.send(:state_event_transition)
862
+ end
863
+ end
864
+
865
+ class MachineFireImplicitWithMarshallingTest < MachineCollectionFireImplicitTest
866
+ def setup
867
+ super
868
+ self.class.const_set('Example', @klass)
869
+
870
+ @object.state_event = 'ignite'
871
+ end
872
+
873
+ def test_should_marshal_during_before_callbacks
874
+ @machine.before_transition {|object, transition| Marshal.dump(object)}
875
+ assert_nothing_raised { @machines.fire_event_attributes(@object, :save) { true } }
876
+ end
877
+
878
+ def test_should_marshal_during_action
879
+ assert_nothing_raised do
880
+ @machines.fire_event_attributes(@object, :save) do
881
+ Marshal.dump(@object)
882
+ true
883
+ end
884
+ end
885
+ end
886
+
887
+ def test_should_marshal_during_after_callbacks
888
+ @machine.after_transition {|object, transition| Marshal.dump(object)}
889
+ assert_nothing_raised { @machines.fire_event_attributes(@object, :save) { true } }
890
+ end
891
+
892
+ def teardown
893
+ self.class.send(:remove_const, 'Example')
894
+ end
895
+ end
896
+
897
+ class MachineFireImplicitPartialWithMarshallingTest < MachineCollectionFireImplicitTest
898
+ def setup
899
+ super
900
+ self.class.const_set('Example', @klass)
901
+
902
+ @object.state_event = 'ignite'
903
+ end
904
+
905
+ def test_should_marshal_during_before_callbacks
906
+ @machine.before_transition {|object, transition| Marshal.dump(object)}
907
+ assert_nothing_raised do
908
+ @machines.fire_event_attributes(@object, :save, false) { true }
909
+ @machines.fire_event_attributes(@object, :save) { true }
910
+ end
911
+ end
912
+
913
+ def test_should_marshal_during_action
914
+ assert_nothing_raised do
915
+ @machines.fire_event_attributes(@object, :save, false) do
916
+ Marshal.dump(@object)
917
+ true
918
+ end
919
+
920
+ @machines.fire_event_attributes(@object, :save) do
921
+ Marshal.dump(@object)
922
+ true
923
+ end
924
+ end
925
+ end
926
+
927
+ def test_should_marshal_during_after_callbacks
928
+ @machine.after_transition {|object, transition| Marshal.dump(object)}
929
+ assert_nothing_raised do
930
+ @machines.fire_event_attributes(@object, :save, false) { true }
931
+ @machines.fire_event_attributes(@object, :save) { true }
932
+ end
933
+ end
934
+
935
+ def teardown
936
+ self.class.send(:remove_const, 'Example')
937
+ end
938
+ end