verborghs-state_machine 0.9.4

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 (89) hide show
  1. data/CHANGELOG.rdoc +360 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +635 -0
  4. data/Rakefile +77 -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/assertions.rb +36 -0
  28. data/lib/state_machine/callback.rb +241 -0
  29. data/lib/state_machine/condition_proxy.rb +106 -0
  30. data/lib/state_machine/eval_helpers.rb +83 -0
  31. data/lib/state_machine/event.rb +267 -0
  32. data/lib/state_machine/event_collection.rb +122 -0
  33. data/lib/state_machine/extensions.rb +149 -0
  34. data/lib/state_machine/guard.rb +230 -0
  35. data/lib/state_machine/initializers/merb.rb +1 -0
  36. data/lib/state_machine/initializers/rails.rb +5 -0
  37. data/lib/state_machine/initializers.rb +4 -0
  38. data/lib/state_machine/integrations/active_model/locale.rb +11 -0
  39. data/lib/state_machine/integrations/active_model/observer.rb +45 -0
  40. data/lib/state_machine/integrations/active_model.rb +445 -0
  41. data/lib/state_machine/integrations/active_record/locale.rb +20 -0
  42. data/lib/state_machine/integrations/active_record.rb +522 -0
  43. data/lib/state_machine/integrations/data_mapper/observer.rb +175 -0
  44. data/lib/state_machine/integrations/data_mapper.rb +379 -0
  45. data/lib/state_machine/integrations/mongo_mapper.rb +309 -0
  46. data/lib/state_machine/integrations/sequel.rb +356 -0
  47. data/lib/state_machine/integrations.rb +83 -0
  48. data/lib/state_machine/machine.rb +1645 -0
  49. data/lib/state_machine/machine_collection.rb +64 -0
  50. data/lib/state_machine/matcher.rb +123 -0
  51. data/lib/state_machine/matcher_helpers.rb +54 -0
  52. data/lib/state_machine/node_collection.rb +152 -0
  53. data/lib/state_machine/state.rb +260 -0
  54. data/lib/state_machine/state_collection.rb +112 -0
  55. data/lib/state_machine/transition.rb +399 -0
  56. data/lib/state_machine/transition_collection.rb +244 -0
  57. data/lib/state_machine.rb +421 -0
  58. data/lib/tasks/state_machine.rake +1 -0
  59. data/lib/tasks/state_machine.rb +27 -0
  60. data/test/files/en.yml +9 -0
  61. data/test/files/switch.rb +11 -0
  62. data/test/functional/state_machine_test.rb +980 -0
  63. data/test/test_helper.rb +4 -0
  64. data/test/unit/assertions_test.rb +40 -0
  65. data/test/unit/callback_test.rb +728 -0
  66. data/test/unit/condition_proxy_test.rb +328 -0
  67. data/test/unit/eval_helpers_test.rb +222 -0
  68. data/test/unit/event_collection_test.rb +324 -0
  69. data/test/unit/event_test.rb +795 -0
  70. data/test/unit/guard_test.rb +909 -0
  71. data/test/unit/integrations/active_model_test.rb +956 -0
  72. data/test/unit/integrations/active_record_test.rb +1918 -0
  73. data/test/unit/integrations/data_mapper_test.rb +1814 -0
  74. data/test/unit/integrations/mongo_mapper_test.rb +1382 -0
  75. data/test/unit/integrations/sequel_test.rb +1492 -0
  76. data/test/unit/integrations_test.rb +50 -0
  77. data/test/unit/invalid_event_test.rb +7 -0
  78. data/test/unit/invalid_transition_test.rb +7 -0
  79. data/test/unit/machine_collection_test.rb +565 -0
  80. data/test/unit/machine_test.rb +2349 -0
  81. data/test/unit/matcher_helpers_test.rb +37 -0
  82. data/test/unit/matcher_test.rb +155 -0
  83. data/test/unit/node_collection_test.rb +207 -0
  84. data/test/unit/state_collection_test.rb +280 -0
  85. data/test/unit/state_machine_test.rb +31 -0
  86. data/test/unit/state_test.rb +848 -0
  87. data/test/unit/transition_collection_test.rb +2098 -0
  88. data/test/unit/transition_test.rb +1384 -0
  89. metadata +176 -0
@@ -0,0 +1,50 @@
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_model
28
+ assert_equal StateMachine::Integrations::ActiveModel, StateMachine::Integrations.find(:active_model)
29
+ end
30
+
31
+ def test_should_find_active_record
32
+ assert_equal StateMachine::Integrations::ActiveRecord, StateMachine::Integrations.find(:active_record)
33
+ end
34
+
35
+ def test_should_find_data_mapper
36
+ assert_equal StateMachine::Integrations::DataMapper, StateMachine::Integrations.find(:data_mapper)
37
+ end
38
+
39
+ def test_should_find_mongo_mapper
40
+ assert_equal StateMachine::Integrations::MongoMapper, StateMachine::Integrations.find(:mongo_mapper)
41
+ end
42
+
43
+ def test_should_find_sequel
44
+ assert_equal StateMachine::Integrations::Sequel, StateMachine::Integrations.find(:sequel)
45
+ end
46
+
47
+ def test_should_raise_an_exception_if_invalid
48
+ assert_raise(NameError) { StateMachine::Integrations.find(:invalid) }
49
+ end
50
+ 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,565 @@
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
+
105
+ def test_should_not_modify_ignore_option
106
+ ignore = ['state', 'alarm_state']
107
+ @machines.initialize_states(@object, :ignore => ignore)
108
+
109
+ assert_nil @object.state
110
+ assert_nil @object.alarm_state
111
+ assert_equal ['state', 'alarm_state'], ignore
112
+ end
113
+ end
114
+
115
+ class MachineCollectionFireTest < Test::Unit::TestCase
116
+ def setup
117
+ @machines = StateMachine::MachineCollection.new
118
+
119
+ @klass = Class.new do
120
+ attr_reader :saved
121
+
122
+ def save
123
+ @saved = true
124
+ end
125
+ end
126
+
127
+ # First machine
128
+ @machines[:state] = @state = StateMachine::Machine.new(@klass, :state, :initial => :parked, :action => :save)
129
+ @state.event :ignite do
130
+ transition :parked => :idling
131
+ end
132
+ @state.event :park do
133
+ transition :idling => :parked
134
+ end
135
+
136
+ # Second machine
137
+ @machines[:alarm_state] = @alarm_state = StateMachine::Machine.new(@klass, :alarm_state, :initial => :active, :action => :save, :namespace => 'alarm')
138
+ @alarm_state.event :enable do
139
+ transition :off => :active
140
+ end
141
+ @alarm_state.event :disable do
142
+ transition :active => :off
143
+ end
144
+
145
+ @object = @klass.new
146
+ end
147
+
148
+ def test_should_raise_exception_if_invalid_event_specified
149
+ exception = assert_raise(StateMachine::InvalidEvent) { @machines.fire_events(@object, :invalid) }
150
+ assert_equal ':invalid is an unknown state machine event', exception.message
151
+
152
+ exception = assert_raise(StateMachine::InvalidEvent) { @machines.fire_events(@object, :ignite, :invalid) }
153
+ assert_equal ':invalid is an unknown state machine event', exception.message
154
+ end
155
+
156
+ def test_should_fail_if_any_event_cannot_transition
157
+ assert !@machines.fire_events(@object, :park, :disable_alarm)
158
+ assert_equal 'parked', @object.state
159
+ assert_equal 'active', @object.alarm_state
160
+ assert !@object.saved
161
+
162
+ assert !@machines.fire_events(@object, :ignite, :enable_alarm)
163
+ assert_equal 'parked', @object.state
164
+ assert_equal 'active', @object.alarm_state
165
+ assert !@object.saved
166
+ end
167
+
168
+ def test_should_be_successful_if_all_events_transition
169
+ assert @machines.fire_events(@object, :ignite, :disable_alarm)
170
+ assert_equal 'idling', @object.state
171
+ assert_equal 'off', @object.alarm_state
172
+ assert @object.saved
173
+ end
174
+
175
+ def test_should_not_save_if_skipping_action
176
+ assert @machines.fire_events(@object, :ignite, :disable_alarm, false)
177
+ assert_equal 'idling', @object.state
178
+ assert_equal 'off', @object.alarm_state
179
+ assert !@object.saved
180
+ end
181
+ end
182
+
183
+ class MachineCollectionFireWithTransactionsTest < Test::Unit::TestCase
184
+ def setup
185
+ @machines = StateMachine::MachineCollection.new
186
+
187
+ @klass = Class.new do
188
+ attr_accessor :allow_save
189
+
190
+ def save
191
+ @allow_save
192
+ end
193
+ end
194
+
195
+ StateMachine::Integrations.const_set('Custom', Module.new do
196
+ attr_reader :rolled_back
197
+
198
+ def transaction(object)
199
+ @rolled_back = yield
200
+ end
201
+ end)
202
+
203
+ # First machine
204
+ @machines[:state] = @state = StateMachine::Machine.new(@klass, :state, :initial => :parked, :action => :save, :integration => :custom)
205
+ @state.event :ignite do
206
+ transition :parked => :idling
207
+ end
208
+
209
+ # Second machine
210
+ @machines[:alarm_state] = @alarm_state = StateMachine::Machine.new(@klass, :alarm_state, :initial => :active, :action => :save, :namespace => 'alarm', :integration => :custom)
211
+ @alarm_state.event :disable do
212
+ transition :active => :off
213
+ end
214
+
215
+ @object = @klass.new
216
+ end
217
+
218
+ def test_should_not_rollback_if_successful
219
+ @object.allow_save = true
220
+
221
+ assert @machines.fire_events(@object, :ignite, :disable_alarm)
222
+ assert_equal true, @state.rolled_back
223
+ assert_nil @alarm_state.rolled_back
224
+ assert_equal 'idling', @object.state
225
+ assert_equal 'off', @object.alarm_state
226
+ end
227
+
228
+ def test_should_rollback_if_not_successful
229
+ @object.allow_save = false
230
+
231
+ assert !@machines.fire_events(@object, :ignite, :disable_alarm)
232
+ assert_equal false, @state.rolled_back
233
+ assert_nil @alarm_state.rolled_back
234
+ assert_equal 'parked', @object.state
235
+ assert_equal 'active', @object.alarm_state
236
+ end
237
+
238
+ def teardown
239
+ StateMachine::Integrations.send(:remove_const, 'Custom')
240
+ end
241
+ end
242
+
243
+ class MachineCollectionFireWithValidationsTest < Test::Unit::TestCase
244
+ def setup
245
+ StateMachine::Integrations.const_set('Custom', Module.new do
246
+ def invalidate(object, attribute, message, values = [])
247
+ (object.errors ||= []) << generate_message(message, values)
248
+ end
249
+
250
+ def reset(object)
251
+ object.errors = []
252
+ end
253
+ end)
254
+
255
+ @klass = Class.new do
256
+ attr_accessor :errors
257
+
258
+ def initialize
259
+ @errors = []
260
+ super
261
+ end
262
+ end
263
+
264
+ @machines = StateMachine::MachineCollection.new
265
+ @machines[:state] = @state = StateMachine::Machine.new(@klass, :state, :initial => :parked, :integration => :custom)
266
+ @state.event :ignite do
267
+ transition :parked => :idling
268
+ end
269
+
270
+ @machines[:alarm_state] = @alarm_state = StateMachine::Machine.new(@klass, :alarm_state, :initial => :active, :namespace => 'alarm', :integration => :custom)
271
+ @alarm_state.event :disable do
272
+ transition :active => :off
273
+ end
274
+
275
+ @object = @klass.new
276
+ end
277
+
278
+ def test_should_not_invalidate_if_transitions_exist
279
+ assert @machines.fire_events(@object, :ignite, :disable_alarm)
280
+ assert_equal [], @object.errors
281
+ end
282
+
283
+ def test_should_invalidate_if_no_transitions_exist
284
+ @object.state = 'idling'
285
+ @object.alarm_state = 'off'
286
+
287
+ assert !@machines.fire_events(@object, :ignite, :disable_alarm)
288
+ assert_equal ['cannot transition via "ignite"', 'cannot transition via "disable"'], @object.errors
289
+ end
290
+
291
+ def teardown
292
+ StateMachine::Integrations.send(:remove_const, 'Custom')
293
+ end
294
+ end
295
+
296
+ class MachineCollectionTransitionsWithoutEventsTest < Test::Unit::TestCase
297
+ def setup
298
+ @klass = Class.new
299
+
300
+ @machines = StateMachine::MachineCollection.new
301
+ @machines[:state] = @machine = StateMachine::Machine.new(@klass, :state, :initial => :parked, :action => :save)
302
+ @machine.event :ignite do
303
+ transition :parked => :idling
304
+ end
305
+
306
+ @object = @klass.new
307
+ @object.state_event = nil
308
+ @transitions = @machines.transitions(@object, :save)
309
+ end
310
+
311
+ def test_should_be_empty
312
+ assert @transitions.empty?
313
+ end
314
+
315
+ def test_should_perform
316
+ assert_equal true, @transitions.perform
317
+ end
318
+ end
319
+
320
+ class MachineCollectionTransitionsWithBlankEventsTest < Test::Unit::TestCase
321
+ def setup
322
+ @klass = Class.new
323
+
324
+ @machines = StateMachine::MachineCollection.new
325
+ @machines[:state] = @machine = StateMachine::Machine.new(@klass, :state, :initial => :parked, :action => :save)
326
+ @machine.event :ignite do
327
+ transition :parked => :idling
328
+ end
329
+
330
+ @object = @klass.new
331
+ @object.state_event = ''
332
+ @transitions = @machines.transitions(@object, :save)
333
+ end
334
+
335
+ def test_should_be_empty
336
+ assert @transitions.empty?
337
+ end
338
+
339
+ def test_should_perform
340
+ assert_equal true, @transitions.perform
341
+ end
342
+ end
343
+
344
+ class MachineCollectionTransitionsWithInvalidEventsTest < Test::Unit::TestCase
345
+ def setup
346
+ @klass = Class.new
347
+
348
+ @machines = StateMachine::MachineCollection.new
349
+ @machines[:state] = @machine = StateMachine::Machine.new(@klass, :state, :initial => :parked, :action => :save)
350
+ @machine.event :ignite do
351
+ transition :parked => :idling
352
+ end
353
+
354
+ @object = @klass.new
355
+ @object.state_event = 'invalid'
356
+ @transitions = @machines.transitions(@object, :save)
357
+ end
358
+
359
+ def test_should_be_empty
360
+ assert @transitions.empty?
361
+ end
362
+
363
+ def test_should_not_perform
364
+ assert_equal false, @transitions.perform
365
+ end
366
+ end
367
+
368
+ class MachineCollectionTransitionsWithoutTransitionTest < Test::Unit::TestCase
369
+ def setup
370
+ @klass = Class.new
371
+
372
+ @machines = StateMachine::MachineCollection.new
373
+ @machines[:state] = @machine = StateMachine::Machine.new(@klass, :state, :initial => :parked, :action => :save)
374
+ @machine.event :ignite do
375
+ transition :parked => :idling
376
+ end
377
+
378
+ @object = @klass.new
379
+ @object.state = 'idling'
380
+ @object.state_event = 'ignite'
381
+ @transitions = @machines.transitions(@object, :save)
382
+ end
383
+
384
+ def test_should_be_empty
385
+ assert @transitions.empty?
386
+ end
387
+
388
+ def test_should_not_perform
389
+ assert_equal false, @transitions.perform
390
+ end
391
+ end
392
+
393
+ class MachineCollectionTransitionsWithTransitionTest < Test::Unit::TestCase
394
+ def setup
395
+ @klass = Class.new
396
+
397
+ @machines = StateMachine::MachineCollection.new
398
+ @machines[:state] = @machine = StateMachine::Machine.new(@klass, :state, :initial => :parked, :action => :save)
399
+ @machine.event :ignite do
400
+ transition :parked => :idling
401
+ end
402
+
403
+ @object = @klass.new
404
+ @object.state_event = 'ignite'
405
+ @transitions = @machines.transitions(@object, :save)
406
+ end
407
+
408
+ def test_should_not_be_empty
409
+ assert_equal 1, @transitions.length
410
+ end
411
+
412
+ def test_should_perform
413
+ assert_equal true, @transitions.perform
414
+ end
415
+ end
416
+
417
+ class MachineCollectionTransitionsWithSameActionsTest < Test::Unit::TestCase
418
+ def setup
419
+ @klass = Class.new
420
+
421
+ @machines = StateMachine::MachineCollection.new
422
+ @machines[:state] = @machine = StateMachine::Machine.new(@klass, :state, :initial => :parked, :action => :save)
423
+ @machine.event :ignite do
424
+ transition :parked => :idling
425
+ end
426
+ @machines[:status] = @machine = StateMachine::Machine.new(@klass, :status, :initial => :first_gear, :action => :save)
427
+ @machine.event :shift_up do
428
+ transition :first_gear => :second_gear
429
+ end
430
+
431
+ @object = @klass.new
432
+ @object.state_event = 'ignite'
433
+ @object.status_event = 'shift_up'
434
+ @transitions = @machines.transitions(@object, :save)
435
+ end
436
+
437
+ def test_should_not_be_empty
438
+ assert_equal 2, @transitions.length
439
+ end
440
+
441
+ def test_should_perform
442
+ assert_equal true, @transitions.perform
443
+ end
444
+ end
445
+
446
+ class MachineCollectionTransitionsWithDifferentActionsTest < Test::Unit::TestCase
447
+ def setup
448
+ @klass = Class.new
449
+
450
+ @machines = StateMachine::MachineCollection.new
451
+ @machines[:state] = @state = StateMachine::Machine.new(@klass, :state, :initial => :parked, :action => :save)
452
+ @state.event :ignite do
453
+ transition :parked => :idling
454
+ end
455
+ @machines[:status] = @status = StateMachine::Machine.new(@klass, :status, :initial => :first_gear, :action => :persist)
456
+ @status.event :shift_up do
457
+ transition :first_gear => :second_gear
458
+ end
459
+
460
+ @object = @klass.new
461
+ @object.state_event = 'ignite'
462
+ @object.status_event = 'shift_up'
463
+ @transitions = @machines.transitions(@object, :save)
464
+ end
465
+
466
+ def test_should_only_select_matching_actions
467
+ assert_equal 1, @transitions.length
468
+ end
469
+ end
470
+
471
+ class MachineCollectionTransitionsWithExisitingTransitionsTest < Test::Unit::TestCase
472
+ def setup
473
+ @klass = Class.new
474
+
475
+ @machines = StateMachine::MachineCollection.new
476
+ @machines[:state] = @machine = StateMachine::Machine.new(@klass, :state, :initial => :parked, :action => :save)
477
+ @machine.event :ignite do
478
+ transition :parked => :idling
479
+ end
480
+
481
+ @object = @klass.new
482
+ @object.send(:state_event_transition=, StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling))
483
+ @transitions = @machines.transitions(@object, :save)
484
+ end
485
+
486
+ def test_should_not_be_empty
487
+ assert_equal 1, @transitions.length
488
+ end
489
+
490
+ def test_should_perform
491
+ assert_equal true, @transitions.perform
492
+ end
493
+ end
494
+
495
+ class MachineCollectionTransitionsWithCustomOptionsTest < Test::Unit::TestCase
496
+ def setup
497
+ @klass = Class.new
498
+
499
+ @machines = StateMachine::MachineCollection.new
500
+ @machines[:state] = @machine = StateMachine::Machine.new(@klass, :state, :initial => :parked, :action => :save)
501
+ @machine.event :ignite do
502
+ transition :parked => :idling
503
+ end
504
+
505
+ @object = @klass.new
506
+ @transitions = @machines.transitions(@object, :save, :after => false)
507
+ end
508
+
509
+ def test_should_use_custom_options
510
+ assert @transitions.skip_after
511
+ end
512
+ end
513
+
514
+ class MachineCollectionFireAttributesWithValidationsTest < Test::Unit::TestCase
515
+ def setup
516
+ @klass = Class.new do
517
+ attr_accessor :errors
518
+
519
+ def initialize
520
+ @errors = []
521
+ super
522
+ end
523
+ end
524
+
525
+ @machines = StateMachine::MachineCollection.new
526
+ @machines[:state] = @machine = StateMachine::Machine.new(@klass, :state, :initial => :parked, :action => :save)
527
+ @machine.event :ignite do
528
+ transition :parked => :idling
529
+ end
530
+
531
+ class << @machine
532
+ def invalidate(object, attribute, message, values = [])
533
+ (object.errors ||= []) << generate_message(message, values)
534
+ end
535
+
536
+ def reset(object)
537
+ object.errors = []
538
+ end
539
+ end
540
+
541
+ @object = @klass.new
542
+ end
543
+
544
+ def test_should_invalidate_if_event_is_invalid
545
+ @object.state_event = 'invalid'
546
+ @machines.transitions(@object, :save)
547
+
548
+ assert !@object.errors.empty?
549
+ end
550
+
551
+ def test_should_invalidate_if_no_transition_exists
552
+ @object.state = 'idling'
553
+ @object.state_event = 'ignite'
554
+ @machines.transitions(@object, :save)
555
+
556
+ assert !@object.errors.empty?
557
+ end
558
+
559
+ def test_should_not_invalidate_if_transition_exists
560
+ @object.state_event = 'ignite'
561
+ @machines.transitions(@object, :save)
562
+
563
+ assert @object.errors.empty?
564
+ end
565
+ end