pluginaweek-state_machine 0.7.6

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