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,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,710 @@
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 do
18
+ def initialize(attributes = {})
19
+ attributes.each do |attribute, value|
20
+ self.send("#{attribute}=", value)
21
+ end
22
+
23
+ super()
24
+ end
25
+ end
26
+
27
+ @machines[:state] = StateMachine::Machine.new(@klass, :state, :initial => :parked)
28
+ @machines[:alarm_state] = StateMachine::Machine.new(@klass, :alarm_state, :initial => :active)
29
+ @machines[:alarm_state].state :active, :value => lambda {'active'}
30
+ end
31
+
32
+ def test_should_set_states_if_nil
33
+ object = @klass.new
34
+ assert_equal 'parked', object.state
35
+ assert_equal 'active', object.alarm_state
36
+ end
37
+
38
+ def test_should_set_states_if_empty
39
+ object = @klass.new(:state => '', :alarm_state => '')
40
+ assert_equal 'parked', object.state
41
+ assert_equal 'active', object.alarm_state
42
+ end
43
+
44
+ def test_should_not_set_states_if_not_empty
45
+ object = @klass.new(:state => 'idling', :alarm_state => 'off')
46
+ assert_equal 'idling', object.state
47
+ assert_equal 'off', object.alarm_state
48
+ end
49
+ end
50
+
51
+ class MachineCollectionFireExplicitTest < Test::Unit::TestCase
52
+ def setup
53
+ @machines = StateMachine::MachineCollection.new
54
+
55
+ @klass = Class.new do
56
+ attr_reader :saved
57
+
58
+ def save
59
+ @saved = true
60
+ end
61
+ end
62
+
63
+ # First machine
64
+ @machines[:state] = @state = StateMachine::Machine.new(@klass, :state, :initial => :parked, :action => :save)
65
+ @state.event :ignite do
66
+ transition :parked => :idling
67
+ end
68
+ @state.event :park do
69
+ transition :idling => :parked
70
+ end
71
+
72
+ # Second machine
73
+ @machines[:alarm_state] = @alarm_state = StateMachine::Machine.new(@klass, :alarm_state, :initial => :active, :action => :save, :namespace => 'alarm')
74
+ @alarm_state.event :enable do
75
+ transition :off => :active
76
+ end
77
+ @alarm_state.event :disable do
78
+ transition :active => :off
79
+ end
80
+
81
+ @object = @klass.new
82
+ end
83
+
84
+ def test_should_raise_exception_if_invalid_event_specified
85
+ exception = assert_raise(StateMachine::InvalidEvent) { @machines.fire_events(@object, :invalid) }
86
+ assert_equal ':invalid is an unknown state machine event', exception.message
87
+
88
+ exception = assert_raise(StateMachine::InvalidEvent) { @machines.fire_events(@object, :ignite, :invalid) }
89
+ assert_equal ':invalid is an unknown state machine event', exception.message
90
+ end
91
+
92
+ def test_should_fail_if_any_event_cannot_transition
93
+ assert !@machines.fire_events(@object, :park, :disable_alarm)
94
+ assert_equal 'parked', @object.state
95
+ assert_equal 'active', @object.alarm_state
96
+ assert !@object.saved
97
+
98
+ assert !@machines.fire_events(@object, :ignite, :enable_alarm)
99
+ assert_equal 'parked', @object.state
100
+ assert_equal 'active', @object.alarm_state
101
+ assert !@object.saved
102
+ end
103
+
104
+ def test_should_be_successful_if_all_events_transition
105
+ assert @machines.fire_events(@object, :ignite, :disable_alarm)
106
+ assert_equal 'idling', @object.state
107
+ assert_equal 'off', @object.alarm_state
108
+ assert @object.saved
109
+ end
110
+
111
+ def test_should_not_save_if_skipping_action
112
+ assert @machines.fire_events(@object, :ignite, :disable_alarm, false)
113
+ assert_equal 'idling', @object.state
114
+ assert_equal 'off', @object.alarm_state
115
+ assert !@object.saved
116
+ end
117
+ end
118
+
119
+ class MachineCollectionFireExplicitWithTransactionsTest < Test::Unit::TestCase
120
+ def setup
121
+ @machines = StateMachine::MachineCollection.new
122
+
123
+ @klass = Class.new do
124
+ attr_accessor :allow_save
125
+
126
+ def save
127
+ @allow_save
128
+ end
129
+ end
130
+
131
+ StateMachine::Integrations.const_set('Custom', Module.new do
132
+ attr_reader :rolled_back
133
+
134
+ def transaction(object)
135
+ @rolled_back = yield
136
+ end
137
+ end)
138
+
139
+ # First machine
140
+ @machines[:state] = @state = StateMachine::Machine.new(@klass, :state, :initial => :parked, :action => :save, :integration => :custom)
141
+ @state.event :ignite do
142
+ transition :parked => :idling
143
+ end
144
+
145
+ # Second machine
146
+ @machines[:alarm_state] = @alarm_state = StateMachine::Machine.new(@klass, :alarm_state, :initial => :active, :action => :save, :namespace => 'alarm', :integration => :custom)
147
+ @alarm_state.event :disable do
148
+ transition :active => :off
149
+ end
150
+
151
+ @object = @klass.new
152
+ end
153
+
154
+ def test_should_not_rollback_if_successful
155
+ @object.allow_save = true
156
+
157
+ assert @machines.fire_events(@object, :ignite, :disable_alarm)
158
+ assert_equal true, @state.rolled_back
159
+ assert_nil @alarm_state.rolled_back
160
+ assert_equal 'idling', @object.state
161
+ assert_equal 'off', @object.alarm_state
162
+ end
163
+
164
+ def test_should_rollback_if_not_successful
165
+ @object.allow_save = false
166
+
167
+ assert !@machines.fire_events(@object, :ignite, :disable_alarm)
168
+ assert_equal false, @state.rolled_back
169
+ assert_nil @alarm_state.rolled_back
170
+ assert_equal 'parked', @object.state
171
+ assert_equal 'active', @object.alarm_state
172
+ end
173
+
174
+ def teardown
175
+ StateMachine::Integrations.send(:remove_const, 'Custom')
176
+ end
177
+ end
178
+
179
+ class MachineCollectionFireExplicitWithValidationsTest < Test::Unit::TestCase
180
+ def setup
181
+ StateMachine::Integrations.const_set('Custom', Module.new do
182
+ def invalidate(object, attribute, message, values = [])
183
+ (object.errors ||= []) << generate_message(message, values)
184
+ end
185
+
186
+ def reset(object)
187
+ object.errors = []
188
+ end
189
+ end)
190
+
191
+ @klass = Class.new do
192
+ attr_accessor :errors
193
+
194
+ def initialize
195
+ @errors = []
196
+ super
197
+ end
198
+ end
199
+
200
+ @machines = StateMachine::MachineCollection.new
201
+ @machines[:state] = @state = StateMachine::Machine.new(@klass, :state, :initial => :parked, :integration => :custom)
202
+ @state.event :ignite do
203
+ transition :parked => :idling
204
+ end
205
+
206
+ @machines[:alarm_state] = @alarm_state = StateMachine::Machine.new(@klass, :alarm_state, :initial => :active, :namespace => 'alarm', :integration => :custom)
207
+ @alarm_state.event :disable do
208
+ transition :active => :off
209
+ end
210
+
211
+ @object = @klass.new
212
+ end
213
+
214
+ def test_should_not_invalidate_if_transitions_exist
215
+ assert @machines.fire_events(@object, :ignite, :disable_alarm)
216
+ assert_equal [], @object.errors
217
+ end
218
+
219
+ def test_should_invalidate_if_no_transitions_exist
220
+ @object.state = 'idling'
221
+ @object.alarm_state = 'off'
222
+
223
+ assert !@machines.fire_events(@object, :ignite, :disable_alarm)
224
+ assert_equal ['cannot transition via "ignite"', 'cannot transition via "disable_alarm"'], @object.errors
225
+ end
226
+
227
+ def teardown
228
+ StateMachine::Integrations.send(:remove_const, 'Custom')
229
+ end
230
+ end
231
+
232
+ class MachineCollectionFireImplicitTest < Test::Unit::TestCase
233
+ def setup
234
+ @klass = Class.new
235
+
236
+ @machines = StateMachine::MachineCollection.new
237
+ @machines[:state] = @machine = StateMachine::Machine.new(@klass, :state, :initial => :parked, :action => :save)
238
+ @machine.event :ignite do
239
+ transition :parked => :idling
240
+ end
241
+
242
+ @saved = false
243
+ @object = @klass.new
244
+ end
245
+
246
+ def default_test
247
+ end
248
+ end
249
+
250
+ class MachineCollectionFireImplicitWithoutEventTest < MachineCollectionFireImplicitTest
251
+ def setup
252
+ super
253
+
254
+ @object.state_event = nil
255
+ @result = @machines.fire_event_attributes(@object, :save) { @saved = true }
256
+ end
257
+
258
+ def test_should_be_successful
259
+ assert_equal true, @result
260
+ end
261
+
262
+ def test_should_run_action
263
+ assert @saved
264
+ end
265
+
266
+ def test_should_not_transition_state
267
+ assert_equal 'parked', @object.state
268
+ end
269
+
270
+ def test_should_not_change_event_attribute
271
+ assert_nil @object.state_event
272
+ end
273
+ end
274
+
275
+ class MachineCollectionFireImplicitWithBlankEventTest < MachineCollectionFireImplicitTest
276
+ def setup
277
+ super
278
+
279
+ @object.state_event = ''
280
+ @result = @machines.fire_event_attributes(@object, :save) { @saved = true }
281
+ end
282
+
283
+ def test_should_be_successful
284
+ assert_equal true, @result
285
+ end
286
+
287
+ def test_should_run_action
288
+ assert @saved
289
+ end
290
+
291
+ def test_should_not_transition_state
292
+ assert_equal 'parked', @object.state
293
+ end
294
+
295
+ def test_should_not_change_event_attribute
296
+ assert_nil @object.state_event
297
+ end
298
+ end
299
+
300
+ class MachineCollectionFireImplicitWithInvalidEventTest < MachineCollectionFireImplicitTest
301
+ def setup
302
+ super
303
+
304
+ @object.state_event = 'invalid'
305
+ @result = @machines.fire_event_attributes(@object, :save) { @saved = true }
306
+ end
307
+
308
+ def test_should_not_be_successful
309
+ assert_equal false, @result
310
+ end
311
+
312
+ def test_should_not_run_action
313
+ assert !@saved
314
+ end
315
+
316
+ def test_should_not_transition_state
317
+ assert_equal 'parked', @object.state
318
+ end
319
+
320
+ def test_should_not_reset_event_attribute
321
+ assert_equal :invalid, @object.state_event
322
+ end
323
+ end
324
+
325
+ class MachineCollectionFireImplicitWithoutTransitionTest < MachineCollectionFireImplicitTest
326
+ def setup
327
+ super
328
+
329
+ @object.state = 'idling'
330
+ @object.state_event = 'ignite'
331
+ @result = @machines.fire_event_attributes(@object, :save) { @saved = true }
332
+ end
333
+
334
+ def test_should_not_be_successful
335
+ assert_equal false, @result
336
+ end
337
+
338
+ def test_should_not_run_action
339
+ assert !@saved
340
+ end
341
+
342
+ def test_should_not_transition_state
343
+ assert_equal 'idling', @object.state
344
+ end
345
+
346
+ def test_should_not_reset_event_attribute
347
+ assert_equal :ignite, @object.state_event
348
+ end
349
+ end
350
+
351
+ class MachineCollectionFireImplicitWithTransitionTest < MachineCollectionFireImplicitTest
352
+ def setup
353
+ super
354
+
355
+ @state_event = nil
356
+
357
+ @object.state_event = 'ignite'
358
+ @result = @machines.fire_event_attributes(@object, :save) do
359
+ @state_event = @object.state_event
360
+ @saved = true
361
+ end
362
+ end
363
+
364
+ def test_should_be_successful
365
+ assert_equal true, @result
366
+ end
367
+
368
+ def test_should_run_action
369
+ assert @saved
370
+ end
371
+
372
+ def test_should_not_have_event_while_running_action
373
+ assert_nil @state_event
374
+ end
375
+
376
+ def test_should_transition_state
377
+ assert_equal 'idling', @object.state
378
+ end
379
+
380
+ def test_should_reset_event_attribute
381
+ assert_nil @object.state_event
382
+ end
383
+
384
+ def test_should_not_be_successful_if_fired_again
385
+ @object.state_event = 'ignite'
386
+ assert !@machines.fire_event_attributes(@object, :save) { true }
387
+ end
388
+ end
389
+
390
+ class MachineCollectionFireImplicitWithNonBooleanResultTest < MachineCollectionFireImplicitTest
391
+ def setup
392
+ super
393
+
394
+ @action_value = Object.new
395
+
396
+ @object.state_event = 'ignite'
397
+ @result = @machines.fire_event_attributes(@object, :save) do
398
+ @saved = true
399
+ @action_value
400
+ end
401
+ end
402
+
403
+ def test_should_be_successful
404
+ assert_equal @action_value, @result
405
+ end
406
+
407
+ def test_should_run_action
408
+ assert @saved
409
+ end
410
+
411
+ def test_should_transition_state
412
+ assert_equal 'idling', @object.state
413
+ end
414
+ end
415
+
416
+ class MachineCollectionFireImplicitWithActionFailureTest < MachineCollectionFireImplicitTest
417
+ def setup
418
+ super
419
+
420
+ @object.state_event = 'ignite'
421
+ @result = @machines.fire_event_attributes(@object, :save) { false }
422
+ end
423
+
424
+ def test_should_not_be_successful
425
+ assert_equal false, @result
426
+ end
427
+
428
+ def test_should_not_transition_state
429
+ assert_equal 'parked', @object.state
430
+ end
431
+
432
+ def test_should_not_reset_event_attribute
433
+ assert_equal :ignite, @object.state_event
434
+ end
435
+ end
436
+
437
+ class MachineCollectionFireImplicitWithActionErrorTest < MachineCollectionFireImplicitTest
438
+ def setup
439
+ super
440
+
441
+ @object.state_event = 'ignite'
442
+ assert_raise(ArgumentError) { @machines.fire_event_attributes(@object, :save) { raise ArgumentError } }
443
+ end
444
+
445
+ def test_should_not_transition_state
446
+ assert_equal 'parked', @object.state
447
+ end
448
+
449
+ def test_should_not_reset_event_attribute
450
+ assert_equal :ignite, @object.state_event
451
+ end
452
+ end
453
+
454
+ class MachineCollectionFireImplicitPartialTest < MachineCollectionFireImplicitTest
455
+ def setup
456
+ super
457
+
458
+ @ran_before_callback = false
459
+ @ran_after_callback = false
460
+ @machine.before_transition { @ran_before_callback = true }
461
+ @machine.after_transition { @ran_after_callback = true }
462
+
463
+ @state_event = nil
464
+
465
+ @object.state_event = 'ignite'
466
+ @result = @machines.fire_event_attributes(@object, :save, false) do
467
+ @state_event = @object.state_event
468
+ true
469
+ end
470
+ end
471
+
472
+ def test_should_run_before_callbacks
473
+ assert @ran_before_callback
474
+ end
475
+
476
+ def test_should_not_run_after_callbacks
477
+ assert !@ran_after_callback
478
+ end
479
+
480
+ def test_should_be_successful
481
+ assert @result
482
+ end
483
+
484
+ def test_should_not_have_event_while_running_action
485
+ assert_nil @state_event
486
+ end
487
+
488
+ def test_should_transition_state
489
+ assert_equal 'idling', @object.state
490
+ end
491
+
492
+ def test_should_not_reset_event_attribute
493
+ assert_equal :ignite, @object.state_event
494
+ end
495
+
496
+ def test_should_reset_event_attributes_after_next_fire_on_success
497
+ assert @machines.fire_event_attributes(@object, :save) { true }
498
+ assert_equal 'idling', @object.state
499
+ assert_nil @object.state_event
500
+ end
501
+
502
+ def test_should_guard_transition_after_next_fire_on_success
503
+ @machines.fire_event_attributes(@object, :save) { true }
504
+
505
+ @object.state = 'idling'
506
+ @object.state_event = 'ignite'
507
+ assert !@machines.fire_event_attributes(@object, :save) { true }
508
+ end
509
+
510
+ def test_should_rollback_all_attributes_after_next_fire_on_failure
511
+ assert !@machines.fire_event_attributes(@object, :save) { false }
512
+ assert_equal 'parked', @object.state
513
+ assert_equal :ignite, @object.state_event
514
+
515
+ @object.state = 'idling'
516
+ assert !@machines.fire_event_attributes(@object, :save) { false }
517
+ end
518
+
519
+ def test_should_guard_transition_after_next_fire_on_failure
520
+ @machines.fire_event_attributes(@object, :save) { false }
521
+
522
+ @object.state = 'idling'
523
+ assert !@machines.fire_event_attributes(@object, :save) { true }
524
+ end
525
+
526
+ def test_should_rollback_all_attributes_after_next_fire_on_error
527
+ assert_raise(ArgumentError) { @machines.fire_event_attributes(@object, :save) { raise ArgumentError } }
528
+ assert_equal 'parked', @object.state
529
+ assert_equal :ignite, @object.state_event
530
+ end
531
+
532
+ def test_should_guard_transition_after_next_fire_on_error
533
+ begin
534
+ @machines.fire_event_attributes(@object, :save) { raise ArgumentError }
535
+ rescue ArgumentError
536
+ end
537
+
538
+ @object.state = 'idling'
539
+ assert !@machines.fire_event_attributes(@object, :save) { true }
540
+ end
541
+ end
542
+
543
+ class MachineCollectionFireImplicitNestedPartialTest < MachineCollectionFireImplicitTest
544
+ def setup
545
+ super
546
+
547
+ @partial_result = nil
548
+
549
+ @object.state_event = 'ignite'
550
+ @result = @machines.fire_event_attributes(@object, :save) do
551
+ @partial_result = @machines.fire_event_attributes(@object, :save, false) { true }
552
+ true
553
+ end
554
+ end
555
+
556
+ def test_should_be_successful
557
+ assert @result
558
+ end
559
+
560
+ def test_should_have_successful_partial_fire
561
+ assert @partial_result
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
+ end
572
+
573
+ class MachineCollectionFireImplicitWithDifferentActionsTest < MachineCollectionFireImplicitTest
574
+ def setup
575
+ super
576
+
577
+ @machines[:alarm_state] = @alarm_machine = StateMachine::Machine.new(@klass, :alarm_state, :initial => :active, :action => :save_alarm)
578
+ @alarm_machine.event :disable do
579
+ transition :active => :off
580
+ end
581
+
582
+ @saved = false
583
+ @object = @klass.new
584
+ @object.state_event = 'ignite'
585
+ @object.alarm_state_event = 'disable'
586
+
587
+ @machines.fire_event_attributes(@object, :save) { true }
588
+ end
589
+
590
+ def test_should_transition_states_for_action
591
+ assert_equal 'idling', @object.state
592
+ end
593
+
594
+ def test_should_reset_event_attributes_for_action
595
+ assert_nil @object.state_event
596
+ end
597
+
598
+ def test_should_not_transition_states_for_other_actions
599
+ assert_equal 'active', @object.alarm_state
600
+ end
601
+
602
+ def test_should_not_reset_event_attributes_for_other_actions
603
+ assert_equal :disable, @object.alarm_state_event
604
+ end
605
+ end
606
+
607
+ class MachineCollectionFireImplicitWithSameActionsTest < MachineCollectionFireImplicitTest
608
+ def setup
609
+ super
610
+
611
+ @machines[:alarm_state] = @alarm_machine = StateMachine::Machine.new(@klass, :alarm_state, :initial => :active, :action => :save)
612
+ @alarm_machine.event :disable do
613
+ transition :active => :off
614
+ end
615
+
616
+ @saved = false
617
+ @object = @klass.new
618
+ @object.state_event = 'ignite'
619
+ @object.alarm_state_event = 'disable'
620
+
621
+ @machines.fire_event_attributes(@object, :save) { true }
622
+ end
623
+
624
+ def test_should_transition_all_states_for_action
625
+ assert_equal 'idling', @object.state
626
+ assert_equal 'off', @object.alarm_state
627
+ end
628
+
629
+ def test_should_reset_all_event_attributes_for_action
630
+ assert_nil @object.state_event
631
+ assert_nil @object.alarm_state_event
632
+ end
633
+ end
634
+
635
+ class MachineCollectionFireImplicitWithValidationsTest < Test::Unit::TestCase
636
+ def setup
637
+ StateMachine::Integrations.const_set('Custom', Module.new do
638
+ def invalidate(object, attribute, message, values = [])
639
+ (object.errors ||= []) << generate_message(message, values)
640
+ end
641
+
642
+ def reset(object)
643
+ object.errors = []
644
+ end
645
+ end)
646
+
647
+ @klass = Class.new do
648
+ attr_accessor :errors
649
+
650
+ def initialize
651
+ @errors = []
652
+ super
653
+ end
654
+ end
655
+
656
+ @machines = StateMachine::MachineCollection.new
657
+ @machines[:state] = @machine = StateMachine::Machine.new(@klass, :state, :initial => :parked, :action => :save, :integration => :custom)
658
+ @machine.event :ignite do
659
+ transition :parked => :idling
660
+ end
661
+
662
+ @object = @klass.new
663
+ end
664
+
665
+ def test_should_invalidate_if_event_is_invalid
666
+ @object.state_event = 'invalid'
667
+ @machines.fire_event_attributes(@object, :save) { true }
668
+
669
+ assert !@object.errors.empty?
670
+ end
671
+
672
+ def test_should_invalidate_if_no_transition_exists
673
+ @object.state = 'idling'
674
+ @object.state_event = 'ignite'
675
+ @machines.fire_event_attributes(@object, :save) { true }
676
+
677
+ assert !@object.errors.empty?
678
+ end
679
+
680
+ def test_should_not_invalidate_if_transition_exists
681
+ @object.state_event = 'ignite'
682
+ @machines.fire_event_attributes(@object, :save) { true }
683
+
684
+ assert @object.errors.empty?
685
+ end
686
+
687
+ def teardown
688
+ StateMachine::Integrations.send(:remove_const, 'Custom')
689
+ end
690
+ end
691
+
692
+ class MachineCollectionFireImplicitWithCustomMachineNameTest < MachineCollectionFireImplicitTest
693
+ def setup
694
+ super
695
+
696
+ @object.state_event = 'ignite'
697
+ end
698
+
699
+ def test_should_be_successful_on_complete_file
700
+ assert @machines.fire_event_attributes(@object, :save) { true }
701
+ assert_equal 'idling', @object.state
702
+ assert_nil @object.state_event
703
+ end
704
+
705
+ def test_should_be_successful_on_partial_fire
706
+ @machines.fire_event_attributes(@object, :save, false) { true }
707
+ assert_equal 'idling', @object.state
708
+ assert_equal :ignite, @object.state_event
709
+ end
710
+ end