verborghs-state_machine 0.9.4

Sign up to get free protection for your applications and to get access to all the features.
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