state_machine 0.3.1 → 0.4.0

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 (52) hide show
  1. data/CHANGELOG.rdoc +26 -0
  2. data/README.rdoc +254 -46
  3. data/Rakefile +29 -3
  4. data/examples/AutoShop_state.png +0 -0
  5. data/examples/Car_state.jpg +0 -0
  6. data/examples/Vehicle_state.png +0 -0
  7. data/lib/state_machine.rb +161 -116
  8. data/lib/state_machine/assertions.rb +21 -0
  9. data/lib/state_machine/callback.rb +168 -0
  10. data/lib/state_machine/eval_helpers.rb +67 -0
  11. data/lib/state_machine/event.rb +135 -101
  12. data/lib/state_machine/extensions.rb +83 -0
  13. data/lib/state_machine/guard.rb +115 -0
  14. data/lib/state_machine/integrations/active_record.rb +242 -0
  15. data/lib/state_machine/integrations/data_mapper.rb +198 -0
  16. data/lib/state_machine/integrations/data_mapper/observer.rb +153 -0
  17. data/lib/state_machine/integrations/sequel.rb +169 -0
  18. data/lib/state_machine/machine.rb +746 -352
  19. data/lib/state_machine/transition.rb +104 -212
  20. data/test/active_record.log +34865 -0
  21. data/test/classes/switch.rb +11 -0
  22. data/test/data_mapper.log +14015 -0
  23. data/test/functional/state_machine_test.rb +249 -15
  24. data/test/sequel.log +3835 -0
  25. data/test/test_helper.rb +3 -12
  26. data/test/unit/assertions_test.rb +13 -0
  27. data/test/unit/callback_test.rb +189 -0
  28. data/test/unit/eval_helpers_test.rb +92 -0
  29. data/test/unit/event_test.rb +247 -113
  30. data/test/unit/guard_test.rb +420 -0
  31. data/test/unit/integrations/active_record_test.rb +515 -0
  32. data/test/unit/integrations/data_mapper_test.rb +407 -0
  33. data/test/unit/integrations/sequel_test.rb +244 -0
  34. data/test/unit/invalid_transition_test.rb +1 -1
  35. data/test/unit/machine_test.rb +1056 -98
  36. data/test/unit/state_machine_test.rb +14 -113
  37. data/test/unit/transition_test.rb +269 -495
  38. metadata +44 -30
  39. data/test/app_root/app/models/auto_shop.rb +0 -34
  40. data/test/app_root/app/models/car.rb +0 -19
  41. data/test/app_root/app/models/highway.rb +0 -3
  42. data/test/app_root/app/models/motorcycle.rb +0 -3
  43. data/test/app_root/app/models/switch.rb +0 -23
  44. data/test/app_root/app/models/switch_observer.rb +0 -20
  45. data/test/app_root/app/models/toggle_switch.rb +0 -2
  46. data/test/app_root/app/models/vehicle.rb +0 -78
  47. data/test/app_root/config/environment.rb +0 -7
  48. data/test/app_root/db/migrate/001_create_switches.rb +0 -12
  49. data/test/app_root/db/migrate/002_create_auto_shops.rb +0 -13
  50. data/test/app_root/db/migrate/003_create_highways.rb +0 -11
  51. data/test/app_root/db/migrate/004_create_vehicles.rb +0 -16
  52. data/test/factory.rb +0 -77
@@ -2,6 +2,6 @@ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
2
 
3
3
  class InvalidTransitionTest < Test::Unit::TestCase
4
4
  def test_should_exist
5
- assert_not_nil PluginAWeek::StateMachine::InvalidTransition
5
+ assert_not_nil StateMachine::InvalidTransition
6
6
  end
7
7
  end
@@ -2,7 +2,13 @@ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
2
 
3
3
  class MachineByDefaultTest < Test::Unit::TestCase
4
4
  def setup
5
- @machine = PluginAWeek::StateMachine::Machine.new(Switch)
5
+ @klass = Class.new
6
+ @machine = StateMachine::Machine.new(@klass)
7
+ @object = @klass.new
8
+ end
9
+
10
+ def test_should_have_an_owner_class
11
+ assert_equal @klass, @machine.owner_class
6
12
  end
7
13
 
8
14
  def test_should_have_an_attribute
@@ -10,77 +16,299 @@ class MachineByDefaultTest < Test::Unit::TestCase
10
16
  end
11
17
 
12
18
  def test_should_not_have_an_initial_state
13
- assert_nil @machine.initial_state(new_switch)
14
- end
15
-
16
- def test_should_have_an_owner_class
17
- assert_equal Switch, @machine.owner_class
19
+ assert_nil @machine.initial_state(@object)
18
20
  end
19
21
 
20
22
  def test_should_not_have_any_events
21
23
  assert @machine.events.empty?
22
24
  end
23
25
 
26
+ def test_should_not_have_any_before_callbacks
27
+ assert @machine.callbacks[:before].empty?
28
+ end
29
+
30
+ def test_should_not_have_any_after_callbacks
31
+ assert @machine.callbacks[:after].empty?
32
+ end
33
+
34
+ def test_should_not_have_an_action
35
+ assert_nil @machine.action
36
+ end
37
+
24
38
  def test_should_not_have_any_states
25
39
  assert @machine.states.empty?
26
40
  end
41
+
42
+ def test_should_not_be_extended_by_the_active_record_integration
43
+ assert !(class << @machine; ancestors; end).include?(StateMachine::Integrations::ActiveRecord)
44
+ end
45
+
46
+ def test_should_not_be_extended_by_the_datamapper_integration
47
+ assert !(class << @machine; ancestors; end).include?(StateMachine::Integrations::DataMapper)
48
+ end
49
+
50
+ def test_should_not_be_extended_by_the_sequel_integration
51
+ assert !(class << @machine; ancestors; end).include?(StateMachine::Integrations::Sequel)
52
+ end
53
+
54
+ def test_should_define_a_reader_attribute_for_the_attribute
55
+ assert @object.respond_to?(:state)
56
+ end
57
+
58
+ def test_should_define_a_writer_attribute_for_the_attribute
59
+ assert @object.respond_to?(:state=)
60
+ end
61
+
62
+ def test_should_define_a_predicate_for_the_attribute
63
+ assert @object.respond_to?(:state?)
64
+ end
65
+
66
+ def test_should_not_define_singular_with_scope
67
+ assert !@klass.respond_to?(:with_state)
68
+ end
69
+
70
+ def test_should_not_define_singular_without_scope
71
+ assert !@klass.respond_to?(:without_state)
72
+ end
73
+
74
+ def test_should_not_define_plural_with_scope
75
+ assert !@klass.respond_to?(:with_states)
76
+ end
77
+
78
+ def test_should_not_define_plural_without_scope
79
+ assert !@klass.respond_to?(:without_states)
80
+ end
81
+
82
+ def test_should_extend_owner_class_with_class_methods
83
+ assert (class << @klass; ancestors; end).include?(StateMachine::ClassMethods)
84
+ end
85
+
86
+ def test_should_include_instance_methods_in_owner_class
87
+ assert @klass.included_modules.include?(StateMachine::InstanceMethods)
88
+ end
89
+
90
+ def test_should_define_state_machines_reader
91
+ expected = {'state' => @machine}
92
+ assert_equal expected, @klass.state_machines
93
+ end
94
+ end
95
+
96
+ class MachineWithCustomAttributeTest < Test::Unit::TestCase
97
+ def setup
98
+ @klass = Class.new
99
+ @machine = StateMachine::Machine.new(@klass, 'status')
100
+ @object = @klass.new
101
+ end
102
+
103
+ def test_should_use_custom_attribute
104
+ assert_equal 'status', @machine.attribute
105
+ end
106
+
107
+ def test_should_define_a_reader_attribute_for_the_attribute
108
+ assert @object.respond_to?(:status)
109
+ end
110
+
111
+ def test_should_define_a_writer_attribute_for_the_attribute
112
+ assert @object.respond_to?(:status=)
113
+ end
114
+
115
+ def test_should_define_a_predicate_for_the_attribute
116
+ assert @object.respond_to?(:status?)
117
+ end
27
118
  end
28
119
 
29
- class MachineWithInitialStateTest < Test::Unit::TestCase
120
+ class MachineWithStaticInitialStateTest < Test::Unit::TestCase
30
121
  def setup
31
- @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state', :initial => 'off')
122
+ @klass = Class.new do
123
+ def initialize(attributes = {})
124
+ attributes.each {|attr, value| send("#{attr}=", value)}
125
+ end
126
+ end
127
+
128
+ @machine = StateMachine::Machine.new(@klass, :initial => 'off')
32
129
  end
33
130
 
34
131
  def test_should_have_an_initial_state
35
- assert_equal 'off', @machine.initial_state(new_switch)
132
+ object = @klass.new
133
+ assert_equal 'off', @machine.initial_state(object)
134
+ end
135
+
136
+ def test_should_set_initial_state_if_existing_is_nil
137
+ object = @klass.new(:state => nil)
138
+ assert_equal 'off', object.state
139
+ end
140
+
141
+ def test_should_set_initial_state_if_existing_is_empty
142
+ object = @klass.new(:state => '')
143
+ assert_equal 'off', object.state
144
+ end
145
+
146
+ def test_should_not_set_initial_state_if_existing_is_not_empty
147
+ object = @klass.new(:state => 'on')
148
+ assert_equal 'on', object.state
149
+ end
150
+
151
+ def test_should_be_included_in_known_states
152
+ assert_equal %w(off), @machine.states
36
153
  end
37
154
  end
38
155
 
39
156
  class MachineWithDynamicInitialStateTest < Test::Unit::TestCase
40
157
  def setup
41
- @initial_state = lambda {|switch| switch.initial_state}
42
- @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state', :initial => @initial_state)
158
+ @klass = Class.new do
159
+ attr_accessor :initial_state
160
+ end
161
+ @machine = StateMachine::Machine.new(@klass, :initial => lambda {|object| object.initial_state || 'default'})
162
+ @object = @klass.new
43
163
  end
44
164
 
45
165
  def test_should_use_the_record_for_determining_the_initial_state
46
- assert_equal 'off', @machine.initial_state(new_switch(:initial_state => 'off'))
47
- assert_equal 'on', @machine.initial_state(new_switch(:initial_state => 'on'))
166
+ @object.initial_state = 'off'
167
+ assert_equal 'off', @machine.initial_state(@object)
168
+
169
+ @object.initial_state = 'on'
170
+ assert_equal 'on', @machine.initial_state(@object)
171
+ end
172
+
173
+ def test_should_set_initial_state_on_created_object
174
+ assert_equal 'default', @object.state
175
+ end
176
+
177
+ def test_should_not_be_included_in_known_states
178
+ assert_equal [], @machine.states
179
+ end
180
+ end
181
+
182
+ class MachineWithCustomActionTest < Test::Unit::TestCase
183
+ def setup
184
+ @machine = StateMachine::Machine.new(Class.new, :action => :save)
185
+ end
186
+
187
+ def test_should_use_the_custom_action
188
+ assert_equal :save, @machine.action
189
+ end
190
+ end
191
+
192
+ class MachineWithNilActionTest < Test::Unit::TestCase
193
+ def setup
194
+ integration = Module.new do
195
+ def default_action
196
+ :save
197
+ end
198
+ end
199
+ StateMachine::Integrations.const_set('Custom', integration)
200
+ @machine = StateMachine::Machine.new(Class.new, :action => nil, :integration => :custom)
201
+ end
202
+
203
+ def test_should_have_a_nil_action
204
+ assert_nil @machine.action
205
+ end
206
+
207
+ def teardown
208
+ StateMachine::Integrations.send(:remove_const, 'Custom')
209
+ end
210
+ end
211
+
212
+ class MachineWithCustomIntegrationTest < Test::Unit::TestCase
213
+ def setup
214
+ StateMachine::Integrations.const_set('Custom', Module.new)
215
+ @machine = StateMachine::Machine.new(Class.new, :integration => :custom)
216
+ end
217
+
218
+ def test_should_be_extended_by_the_integration
219
+ assert (class << @machine; ancestors; end).include?(StateMachine::Integrations::Custom)
220
+ end
221
+
222
+ def teardown
223
+ StateMachine::Integrations.send(:remove_const, 'Custom')
48
224
  end
49
225
  end
50
226
 
51
227
  class MachineTest < Test::Unit::TestCase
228
+ def test_should_raise_exception_if_invalid_option_specified
229
+ assert_raise(ArgumentError) {StateMachine::Machine.new(Class.new, :invalid => true)}
230
+ end
231
+ end
232
+
233
+ class MachineWithoutIntegrationTest < Test::Unit::TestCase
52
234
  def setup
53
- @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state')
235
+ @klass = Class.new
236
+ @machine = StateMachine::Machine.new(@klass)
237
+ @object = @klass.new
54
238
  end
55
239
 
56
- def test_should_define_a_named_scope_for_the_attribute
57
- on = create_switch(:state => 'on')
58
- off = create_switch(:state => 'off')
240
+ def test_transaction_should_yield
241
+ @yielded = false
242
+ @machine.within_transaction(@object) do
243
+ @yielded = true
244
+ end
59
245
 
60
- assert_equal [on], Switch.with_state('on')
246
+ assert @yielded
61
247
  end
62
-
63
- def test_should_define_a_pluralized_named_scope_for_the_attribute
64
- on = create_switch(:state => 'on')
65
- off = create_switch(:state => 'off')
248
+ end
249
+
250
+ class MachineWithIntegrationTest < Test::Unit::TestCase
251
+ def setup
252
+ @integration = Module.new do
253
+ class << self; attr_accessor :initialized, :with_scopes, :without_scopes; end
254
+ @initialized = false
255
+ @with_scopes = []
256
+ @without_scopes = []
257
+
258
+ def after_initialize
259
+ StateMachine::Integrations::Custom.initialized = true
260
+ end
261
+
262
+ def default_action
263
+ :save
264
+ end
265
+
266
+ def define_with_scope(name)
267
+ StateMachine::Integrations::Custom.with_scopes << name
268
+ end
269
+
270
+ def define_without_scope(name)
271
+ StateMachine::Integrations::Custom.without_scopes << name
272
+ end
273
+ end
66
274
 
67
- assert_equal [on, off], Switch.with_states('on', 'off')
275
+ StateMachine::Integrations.const_set('Custom', @integration)
276
+ @machine = StateMachine::Machine.new(Class.new, :integration => :custom)
68
277
  end
69
278
 
70
- def test_should_raise_exception_if_invalid_option_specified
71
- assert_raise(ArgumentError) {PluginAWeek::StateMachine::Machine.new(Switch, 'state', :invalid => true)}
279
+ def test_should_call_after_initialize_hook
280
+ assert @integration.initialized
281
+ end
282
+
283
+ def test_should_use_the_default_action
284
+ assert_equal :save, @machine.action
72
285
  end
73
286
 
74
- def test_should_symbolize_attribute
75
- machine = PluginAWeek::StateMachine::Machine.new(Switch, :state)
76
- assert_equal 'state', machine.attribute
287
+ def test_should_use_the_custom_action_if_specified
288
+ machine = StateMachine::Machine.new(Class.new, :integration => :custom, :action => :save!)
289
+ assert_equal :save!, machine.action
290
+ end
291
+
292
+ def test_should_define_a_singular_and_plural_with_scope
293
+ assert_equal %w(with_state with_states), @integration.with_scopes
294
+ end
295
+
296
+ def test_should_define_a_singular_and_plural_without_scope
297
+ assert_equal %w(without_state without_states), @integration.without_scopes
298
+ end
299
+
300
+ def teardown
301
+ StateMachine::Integrations.send(:remove_const, 'Custom')
77
302
  end
78
303
  end
79
304
 
80
305
  class MachineAfterBeingCopiedTest < Test::Unit::TestCase
81
306
  def setup
82
- @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state')
307
+ @machine = StateMachine::Machine.new(Class.new, 'state')
83
308
  @machine.event(:turn_on) {}
309
+ @machine.before_transition(lambda {})
310
+ @machine.after_transition(lambda {})
311
+ @machine.states # Caches the states variable
84
312
 
85
313
  @copied_machine = @machine.dup
86
314
  end
@@ -104,65 +332,238 @@ class MachineAfterBeingCopiedTest < Test::Unit::TestCase
104
332
  def test_should_not_update_machine_for_original_event
105
333
  assert_equal @machine, @machine.events['turn_on'].machine
106
334
  end
335
+
336
+ def test_should_not_have_the_same_callbacks
337
+ assert_not_same @copied_machine.callbacks, @machine.callbacks
338
+ end
339
+
340
+ def test_should_not_have_the_same_before_callbacks
341
+ assert_not_same @copied_machine.callbacks[:before], @machine.callbacks[:before]
342
+ end
343
+
344
+ def test_should_not_have_the_same_after_callbacks
345
+ assert_not_same @copied_machine.callbacks[:after], @machine.callbacks[:after]
346
+ end
107
347
  end
108
348
 
109
349
  class MachineAfterChangingContextTest < Test::Unit::TestCase
110
350
  def setup
111
- @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state')
351
+ @original_class = Class.new
352
+ @machine = StateMachine::Machine.new(@original_class, 'state')
353
+
354
+ @new_class = Class.new(@original_class)
355
+ @new_machine = @machine.within_context(@new_class)
356
+
357
+ @object = @new_class.new
112
358
  end
113
359
 
114
360
  def test_should_create_copy_of_machine
115
- new_machine = @machine.within_context(ToggleSwitch)
116
- assert_not_same @machine, new_machine
361
+ assert_not_same @machine, @new_machine
117
362
  end
118
363
 
119
- def test_should_update_owner_clas
120
- new_machine = @machine.within_context(ToggleSwitch)
121
- assert_equal ToggleSwitch, new_machine.owner_class
364
+ def test_should_update_owner_class
365
+ assert_equal @new_class, @new_machine.owner_class
122
366
  end
123
367
 
124
- def test_should_update_initial_state
125
- new_machine = @machine.within_context(ToggleSwitch, :initial => 'off')
126
- assert_equal 'off', new_machine.initial_state(new_switch)
368
+ def test_should_not_change_original_owner_class
369
+ assert_equal @original_class, @machine.owner_class
370
+ end
371
+
372
+ def test_should_allow_changing_the_initial_state
373
+ new_machine = @machine.within_context(@new_class, :initial => 'off')
374
+ assert_equal 'off', new_machine.initial_state(@object)
375
+ end
376
+
377
+ def test_should_not_change_original_initial_state_if_updated
378
+ new_machine = @machine.within_context(@new_class, :initial => 'off')
379
+ assert_nil @machine.initial_state(@object)
127
380
  end
128
381
 
129
382
  def test_should_not_update_initial_state_if_not_provided
130
- new_machine = @machine.within_context(ToggleSwitch)
131
- assert_nil new_machine.initial_state(new_switch)
383
+ assert_nil @new_machine.initial_state(@object)
384
+ end
385
+
386
+ def test_should_allow_changing_the_integration
387
+ StateMachine::Integrations.const_set('Custom', Module.new)
388
+ new_machine = @machine.within_context(@new_class, :integration => :custom)
389
+ assert (class << new_machine; ancestors; end).include?(StateMachine::Integrations::Custom)
390
+ end
391
+
392
+ def test_should_not_change_original_integration_if_updated
393
+ StateMachine::Integrations.const_set('Custom', Module.new)
394
+ new_machine = @machine.within_context(@new_class, :integration => :custom)
395
+ assert !(class << @machine; ancestors; end).include?(StateMachine::Integrations::Custom)
396
+ end
397
+
398
+ def test_should_change_the_associated_machine_in_the_new_class
399
+ assert_equal @new_machine, @new_class.state_machines['state']
400
+ end
401
+
402
+ def test_should_not_change_the_associated_machine_in_the_original_class
403
+ assert_equal @machine, @original_class.state_machines['state']
132
404
  end
133
405
 
134
406
  def test_raise_exception_if_invalid_option_specified
135
- assert_raise(ArgumentError) {@machine.within_context(ToggleSwitch, :invalid => true)}
407
+ assert_raise(ArgumentError) {@machine.within_context(@new_class, :invalid => true)}
408
+ end
409
+
410
+ def teardown
411
+ StateMachine::Integrations.send(:remove_const, 'Custom') if StateMachine::Integrations.const_defined?('Custom')
136
412
  end
137
413
  end
138
414
 
139
- class MachineWithConflictingNamedScopesTest < Test::Unit::TestCase
140
- class Switch < ActiveRecord::Base
141
- def self.with_state
142
- :custom
415
+ class MachineWithConflictingAttributeAccessorsTest < Test::Unit::TestCase
416
+ def setup
417
+ @klass = Class.new do
418
+ attr_accessor :status
419
+
420
+ def state
421
+ status
422
+ end
423
+
424
+ def state=(value)
425
+ self.status = value
426
+ end
427
+
428
+ def state?
429
+ true
430
+ end
143
431
  end
144
-
145
- def self.with_states
146
- :custom
432
+ @machine = StateMachine::Machine.new(@klass)
433
+ @object = @klass.new
434
+ end
435
+
436
+ def test_should_not_define_attribute_reader
437
+ @object.status = 'on'
438
+ assert_equal 'on', @object.state
439
+ end
440
+
441
+ def test_should_not_define_attribute_writer
442
+ @object.state = 'on'
443
+ assert_equal 'on', @object.status
444
+ end
445
+
446
+ def test_should_not_define_attribute_predicate
447
+ assert @object.state?
448
+ end
449
+ end
450
+
451
+ class MachineWithConflictingPrivateAttributeAccessorsTest < Test::Unit::TestCase
452
+ def setup
453
+ @klass = Class.new do
454
+ attr_accessor :status
455
+
456
+ private
457
+ def state
458
+ status
459
+ end
460
+
461
+ def state=(value)
462
+ self.status = value
463
+ end
464
+
465
+ def state?
466
+ true
467
+ end
147
468
  end
469
+ @machine = StateMachine::Machine.new(@klass)
470
+ @object = @klass.new
471
+ end
472
+
473
+ def test_should_not_define_attribute_reader
474
+ @object.status = 'on'
475
+ assert_equal 'on', @object.send(:state)
476
+ end
477
+
478
+ def test_should_not_define_attribute_writer
479
+ @object.send(:state=, 'on')
480
+ assert_equal 'on', @object.status
148
481
  end
149
482
 
483
+ def test_should_not_define_attribute_predicate
484
+ assert @object.send(:state?)
485
+ end
486
+ end
487
+
488
+ class MachineWithConflictingStatePredicatesTest < Test::Unit::TestCase
150
489
  def setup
151
- @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state')
490
+ @klass = Class.new do
491
+ def on?
492
+ true
493
+ end
494
+
495
+ def off?
496
+ true
497
+ end
498
+ end
499
+ @machine = StateMachine::Machine.new(@klass)
500
+ @machine.before_transition :to => 'on', :from => 'off', :do => lambda {}
501
+ @object = @klass.new
502
+ end
503
+
504
+ def test_should_not_define_state_predicates
505
+ assert @object.on?
506
+ assert @object.off?
507
+ end
508
+ end
509
+
510
+ class MachineWithConflictingScopesTest < Test::Unit::TestCase
511
+ def setup
512
+ @klass = Class.new do
513
+ def self.with_state
514
+ :with_state
515
+ end
516
+
517
+ def self.with_states
518
+ :with_states
519
+ end
520
+
521
+ def self.without_state
522
+ :without_state
523
+ end
524
+
525
+ def self.without_states
526
+ :without_states
527
+ end
528
+ end
529
+
530
+ integration = Module.new do
531
+ def define_with_scope(name)
532
+ raise ArgumentError, 'should not define a with scope'
533
+ end
534
+
535
+ def define_without_scope(name)
536
+ raise ArgumentError, 'should not define a without scope'
537
+ end
538
+ end
539
+ StateMachine::Integrations.const_set('Custom', integration)
540
+ @machine = StateMachine::Machine.new(@klass, :integration => :custom)
152
541
  end
153
542
 
154
- def test_should_not_define_a_named_scope_for_the_attribute
155
- assert_equal :custom, Switch.with_state
543
+ def test_should_not_define_singular_with_scope
544
+ assert_equal :with_state, @klass.with_state
156
545
  end
157
546
 
158
- def test_should_not_define_a_pluralized_named_scope_for_the_attribute
159
- assert_equal :custom, Switch.with_states
547
+ def test_should_not_define_plural_with_scope
548
+ assert_equal :with_states, @klass.with_states
549
+ end
550
+
551
+ def test_should_not_define_singular_without_scope
552
+ assert_equal :without_state, @klass.without_state
553
+ end
554
+
555
+ def test_should_not_define_plural_without_scope
556
+ assert_equal :without_states, @klass.without_states
557
+ end
558
+
559
+ def teardown
560
+ StateMachine::Integrations.send(:remove_const, 'Custom')
160
561
  end
161
562
  end
162
563
 
163
564
  class MachineWithEventsTest < Test::Unit::TestCase
164
565
  def setup
165
- @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state')
566
+ @machine = StateMachine::Machine.new(Class.new)
166
567
  end
167
568
 
168
569
  def test_should_create_event_with_given_name
@@ -185,9 +586,230 @@ class MachineWithEventsTest < Test::Unit::TestCase
185
586
  end
186
587
  end
187
588
 
589
+ class MachineWithConflictingPredefinedInitializeTest < Test::Unit::TestCase
590
+ def setup
591
+ @klass = Class.new do
592
+ attr_reader :initialized
593
+ attr_reader :block_given
594
+
595
+ def initialize
596
+ @initialized = true
597
+ @block_given = block_given?
598
+ end
599
+ end
600
+
601
+ @machine = StateMachine::Machine.new(@klass, :initial => 'off')
602
+ @object = @klass.new {}
603
+ end
604
+
605
+ def test_should_not_override_existing_method
606
+ assert @object.initialized
607
+ end
608
+
609
+ def test_should_still_initialize_state
610
+ assert_equal 'off', @object.state
611
+ end
612
+
613
+ def test_should_preserve_block
614
+ assert @object.block_given
615
+ end
616
+
617
+ def test_should_not_include_initialize_in_instance_methods
618
+ assert !@klass.instance_methods(false).include?('initialize')
619
+ end
620
+ end
621
+
622
+ class MachineWithConflictingPostdefinedInitializeTest < Test::Unit::TestCase
623
+ def setup
624
+ @klass = Class.new
625
+ @machine = StateMachine::Machine.new(@klass, :initial => 'off')
626
+ @klass.class_eval do
627
+ attr_reader :initialized
628
+ attr_reader :block_given
629
+
630
+ def initialize
631
+ @initialized = true
632
+ @block_given = block_given?
633
+ end
634
+ end
635
+
636
+ @object = @klass.new {}
637
+ end
638
+
639
+ def test_should_not_override_existing_method
640
+ assert @object.initialized
641
+ end
642
+
643
+ def test_should_still_initialize_state
644
+ assert_equal 'off', @object.state
645
+ end
646
+
647
+ def test_should_preserve_block
648
+ assert @object.block_given
649
+ end
650
+
651
+ def test_should_not_include_initialize_in_instance_methods
652
+ assert !@klass.instance_methods(false).include?('initialize')
653
+ end
654
+ end
655
+
656
+ class MachineWithConflictingSuperclassInitializeTest < Test::Unit::TestCase
657
+ def setup
658
+ @superclass = Class.new do
659
+ attr_reader :initialized
660
+ attr_reader :block_given
661
+
662
+ def initialize
663
+ @initialized = true
664
+ @block_given = block_given?
665
+ end
666
+ end
667
+ @klass = Class.new(@superclass)
668
+ @machine = StateMachine::Machine.new(@klass, :initial => 'off')
669
+ @object = @klass.new {}
670
+ end
671
+
672
+ def test_should_not_override_existing_method
673
+ assert @object.initialized
674
+ end
675
+
676
+ def test_should_still_initialize_state
677
+ assert_equal 'off', @object.state
678
+ end
679
+
680
+ def test_should_preserve_block
681
+ assert @object.block_given
682
+ end
683
+
684
+ def test_should_not_include_initialize_in_instance_methods
685
+ assert !@klass.instance_methods(false).include?('initialize')
686
+ end
687
+ end
688
+
689
+ class MachineWithConflictingPredefinedAndSuperclassInitializeTest < Test::Unit::TestCase
690
+ def setup
691
+ @superclass = Class.new do
692
+ attr_reader :base_initialized
693
+
694
+ def initialize
695
+ @base_initialized = true
696
+ end
697
+ end
698
+ @klass = Class.new(@superclass) do
699
+ attr_reader :initialized
700
+
701
+ def initialize
702
+ super
703
+ @initialized = true
704
+ end
705
+ end
706
+
707
+ @machine = StateMachine::Machine.new(@klass, :initial => 'off')
708
+ @object = @klass.new
709
+ end
710
+
711
+ def test_should_not_override_base_method
712
+ assert @object.base_initialized
713
+ end
714
+
715
+ def test_should_not_override_existing_method
716
+ assert @object.initialized
717
+ end
718
+
719
+ def test_should_still_initialize_state
720
+ assert_equal 'off', @object.state
721
+ end
722
+
723
+ def test_should_not_include_initialize_in_instance_methods
724
+ assert !@klass.instance_methods(false).include?('initialize')
725
+ end
726
+ end
727
+
728
+ class MachineWithConflictingPostdefinedAndSuperclassInitializeTest < Test::Unit::TestCase
729
+ def setup
730
+ @superclass = Class.new do
731
+ attr_reader :base_initialized
732
+
733
+ def initialize
734
+ @base_initialized = true
735
+ end
736
+ end
737
+ @klass = Class.new(@superclass)
738
+
739
+ @machine = StateMachine::Machine.new(@klass, :initial => 'off')
740
+ @klass.class_eval do
741
+ attr_reader :initialized
742
+
743
+ def initialize
744
+ super
745
+ @initialized = true
746
+ end
747
+ end
748
+
749
+ @object = @klass.new
750
+ end
751
+
752
+ def test_should_not_override_base_method
753
+ assert @object.base_initialized
754
+ end
755
+
756
+ def test_should_not_override_existing_method
757
+ assert @object.initialized
758
+ end
759
+
760
+ def test_should_still_initialize_state
761
+ assert_equal 'off', @object.state
762
+ end
763
+
764
+ def test_should_not_include_initialize_in_instance_methods
765
+ assert !@klass.instance_methods(false).include?('initialize')
766
+ end
767
+ end
768
+
769
+ class MachineWithConflictingMethodAddedTest < Test::Unit::TestCase
770
+ def setup
771
+ @klass = Class.new do
772
+ class << self
773
+ attr_reader :called_method_added
774
+
775
+ def method_added(method)
776
+ super
777
+ @called_method_added = true
778
+ end
779
+ end
780
+ end
781
+ @machine = StateMachine::Machine.new(@klass, :initial => 'off')
782
+ @object = @klass.new
783
+ end
784
+
785
+ def test_should_not_override_existing_method
786
+ assert @klass.called_method_added
787
+ end
788
+
789
+ def test_should_still_initialize_state
790
+ assert_equal 'off', @object.state
791
+ end
792
+ end
793
+
794
+ class MachineWithExistingAttributeValue < Test::Unit::TestCase
795
+ def setup
796
+ @klass = Class.new do
797
+ def initialize
798
+ @state = 'on'
799
+ end
800
+ end
801
+ @machine = StateMachine::Machine.new(@klass, :initial => 'off')
802
+ @object = @klass.new
803
+ end
804
+
805
+ def test_should_not_set_the_initial_state
806
+ assert_equal 'on', @object.state
807
+ end
808
+ end
809
+
188
810
  class MachineWithExistingEventTest < Test::Unit::TestCase
189
811
  def setup
190
- @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state')
812
+ @machine = StateMachine::Machine.new(Class.new)
191
813
  @event = @machine.event(:turn_on) {}
192
814
  @same_event = @machine.event(:turn_on) {}
193
815
  end
@@ -197,9 +819,10 @@ class MachineWithExistingEventTest < Test::Unit::TestCase
197
819
  end
198
820
  end
199
821
 
200
- class MachineWithEventsAndTransitionsTest < Test::Unit::TestCase
822
+ class MachineWithEventsWithTransitionsTest < Test::Unit::TestCase
201
823
  def setup
202
- @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state')
824
+ @klass = Class.new
825
+ @machine = StateMachine::Machine.new(@klass)
203
826
  @machine.event(:turn_on) do
204
827
  transition :to => 'on', :from => 'off'
205
828
  transition :to => 'error', :from => 'unknown'
@@ -211,7 +834,7 @@ class MachineWithEventsAndTransitionsTest < Test::Unit::TestCase
211
834
  end
212
835
 
213
836
  def test_should_track_states_defined_in_event_transitions
214
- assert_equal %w(error off on unknown), @machine.states
837
+ assert_equal %w(error off on unknown), @machine.states.sort
215
838
  end
216
839
 
217
840
  def test_should_not_duplicate_states_defined_in_multiple_event_transitions
@@ -219,18 +842,71 @@ class MachineWithEventsAndTransitionsTest < Test::Unit::TestCase
219
842
  transition :to => 'off', :from => 'on'
220
843
  end
221
844
 
222
- assert_equal %w(error off on unknown), @machine.states
845
+ assert_equal %w(error off on unknown), @machine.states.sort
846
+ end
847
+
848
+ def test_should_track_state_from_new_events
849
+ @machine.states
850
+ @machine.event :turn_off do
851
+ transition :to => 'maybe'
852
+ end
853
+
854
+ assert_equal %w(error maybe off on unknown), @machine.states.sort
855
+ end
856
+
857
+ def test_should_define_predicates_for_each_state
858
+ object = @klass.new
859
+
860
+ [:on?, :off?, :error?, :unknown?].each {|predicate| assert object.respond_to?(predicate)}
861
+ end
862
+ end
863
+
864
+ class MachineWithSymbolStatesTest < Test::Unit::TestCase
865
+ def setup
866
+ @klass = Class.new
867
+ @machine = StateMachine::Machine.new(@klass)
868
+ @machine.event(:turn_on) do
869
+ transition :to => :on, :from => :off
870
+ end
871
+ end
872
+
873
+ def test_should_define_predicates_for_each_state
874
+ object = @klass.new
875
+
876
+ [:on?, :off?].each {|predicate| assert object.respond_to?(predicate)}
877
+ end
878
+ end
879
+
880
+ class MachineWithNumericStatesTest < Test::Unit::TestCase
881
+ def setup
882
+ @klass = Class.new
883
+ @machine = StateMachine::Machine.new(@klass)
884
+ @machine.event(:turn_on) do
885
+ transition :to => 1, :from => 2
886
+ end
887
+ end
888
+
889
+ def test_should_not_define_predicates_for_each_state
890
+ object = @klass.new
891
+
892
+ ['1?', '2?'].each {|predicate| assert !object.respond_to?(predicate)}
223
893
  end
224
894
  end
225
895
 
226
896
  class MachineWithTransitionCallbacksTest < Test::Unit::TestCase
227
897
  def setup
228
- @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state')
898
+ @klass = Class.new do
899
+ attr_accessor :callbacks
900
+ end
901
+
902
+ @machine = StateMachine::Machine.new(@klass)
229
903
  @event = @machine.event :turn_on do
230
904
  transition :to => 'on', :from => 'off'
231
905
  end
232
906
 
233
- @switch = create_switch(:state => 'off')
907
+ @object = @klass.new
908
+ @object.state = 'off'
909
+ @object.callbacks = []
234
910
  end
235
911
 
236
912
  def test_should_raise_exception_if_invalid_option_specified
@@ -242,65 +918,347 @@ class MachineWithTransitionCallbacksTest < Test::Unit::TestCase
242
918
  end
243
919
 
244
920
  def test_should_invoke_callbacks_during_transition
245
- @machine.before_transition lambda {|switch| switch.callbacks << 'before'}
246
- @machine.after_transition lambda {|switch| switch.callbacks << 'after'}
921
+ @machine.before_transition lambda {|object| object.callbacks << 'before'}
922
+ @machine.after_transition lambda {|object| object.callbacks << 'after'}
923
+
924
+ @event.fire(@object)
925
+ assert_equal %w(before after), @object.callbacks
926
+ end
927
+
928
+ def test_should_support_from_requirement
929
+ @machine.before_transition :from => 'off', :do => lambda {|object| object.callbacks << 'off'}
930
+ @machine.before_transition :from => 'on', :do => lambda {|object| object.callbacks << 'on'}
931
+
932
+ @event.fire(@object)
933
+ assert_equal %w(off), @object.callbacks
934
+ end
935
+
936
+ def test_should_support_except_from_requirement
937
+ @machine.before_transition :except_from => 'off', :do => lambda {|object| object.callbacks << 'off'}
938
+ @machine.before_transition :except_from => 'on', :do => lambda {|object| object.callbacks << 'on'}
939
+
940
+ @event.fire(@object)
941
+ assert_equal %w(on), @object.callbacks
942
+ end
943
+
944
+ def test_should_support_to_requirement
945
+ @machine.before_transition :to => 'off', :do => lambda {|object| object.callbacks << 'off'}
946
+ @machine.before_transition :to => 'on', :do => lambda {|object| object.callbacks << 'on'}
947
+
948
+ @event.fire(@object)
949
+ assert_equal %w(on), @object.callbacks
950
+ end
951
+
952
+ def test_should_support_except_to_requirement
953
+ @machine.before_transition :except_to => 'off', :do => lambda {|object| object.callbacks << 'off'}
954
+ @machine.before_transition :except_to => 'on', :do => lambda {|object| object.callbacks << 'on'}
247
955
 
248
- @event.fire(@switch)
249
- assert_equal %w(before after), @switch.callbacks
956
+ @event.fire(@object)
957
+ assert_equal %w(off), @object.callbacks
250
958
  end
251
959
 
252
- def test_should_support_from_query
253
- @machine.before_transition :from => 'off', :do => lambda {|switch| switch.callbacks << 'off'}
254
- @machine.before_transition :from => 'on', :do => lambda {|switch| switch.callbacks << 'on'}
960
+ def test_should_support_on_requirement
961
+ @machine.before_transition :on => 'turn_off', :do => lambda {|object| object.callbacks << 'turn_off'}
962
+ @machine.before_transition :on => 'turn_on', :do => lambda {|object| object.callbacks << 'turn_on'}
255
963
 
256
- @event.fire(@switch)
257
- assert_equal %w(off), @switch.callbacks
964
+ @event.fire(@object)
965
+ assert_equal %w(turn_on), @object.callbacks
258
966
  end
259
967
 
260
- def test_should_support_except_from_query
261
- @machine.before_transition :except_from => 'off', :do => lambda {|switch| switch.callbacks << 'off'}
262
- @machine.before_transition :except_from => 'on', :do => lambda {|switch| switch.callbacks << 'on'}
968
+ def test_should_support_except_on_requirement
969
+ @machine.before_transition :except_on => 'turn_off', :do => lambda {|object| object.callbacks << 'turn_off'}
970
+ @machine.before_transition :except_on => 'turn_on', :do => lambda {|object| object.callbacks << 'turn_on'}
263
971
 
264
- @event.fire(@switch)
265
- assert_equal %w(on), @switch.callbacks
972
+ @event.fire(@object)
973
+ assert_equal %w(turn_off), @object.callbacks
266
974
  end
267
975
 
268
- def test_should_support_to_query
269
- @machine.before_transition :to => 'off', :do => lambda {|switch| switch.callbacks << 'off'}
270
- @machine.before_transition :to => 'on', :do => lambda {|switch| switch.callbacks << 'on'}
976
+ def test_should_track_states_defined_in_transition_callbacks
977
+ @machine.before_transition :from => 'off', :to => 'on', :do => lambda {}
978
+ @machine.after_transition :from => 'unknown', :to => 'error', :do => lambda {}
271
979
 
272
- @event.fire(@switch)
273
- assert_equal %w(on), @switch.callbacks
980
+ assert_equal %w(error off on unknown), @machine.states.sort
274
981
  end
275
982
 
276
- def test_should_support_except_to_query
277
- @machine.before_transition :except_to => 'off', :do => lambda {|switch| switch.callbacks << 'off'}
278
- @machine.before_transition :except_to => 'on', :do => lambda {|switch| switch.callbacks << 'on'}
983
+ def test_should_not_duplicate_states_defined_in_multiple_event_transitions
984
+ @machine.before_transition :from => 'off', :to => 'on', :do => lambda {}
985
+ @machine.after_transition :from => 'unknown', :to => 'error', :do => lambda {}
986
+ @machine.after_transition :from => 'off', :to => 'on', :do => lambda {}
279
987
 
280
- @event.fire(@switch)
281
- assert_equal %w(off), @switch.callbacks
988
+ assert_equal %w(error off on unknown), @machine.states.sort
989
+ end
990
+
991
+ def test_should_define_predicates_for_each_state
992
+ [:on?, :off?].each {|predicate| assert @object.respond_to?(predicate)}
993
+ end
994
+ end
995
+
996
+ class MachineWithOtherStates < Test::Unit::TestCase
997
+ def setup
998
+ @klass = Class.new
999
+ @machine = StateMachine::Machine.new(@klass, :initial => 'on')
1000
+ @machine.other_states('on', 'off')
282
1001
  end
283
1002
 
284
- def test_should_support_on_query
285
- @machine.before_transition :on => 'turn_off', :do => lambda {|switch| switch.callbacks << 'turn_off'}
286
- @machine.before_transition :on => 'turn_on', :do => lambda {|switch| switch.callbacks << 'turn_on'}
1003
+ def test_should_include_other_states_in_known_states
1004
+ assert_equal %w(off on), @machine.states.sort
1005
+ end
1006
+
1007
+ def test_should_define_predicates_for_each_state
1008
+ object = @klass.new
287
1009
 
288
- @event.fire(@switch)
289
- assert_equal %w(turn_on), @switch.callbacks
1010
+ [:on?, :off?].each {|predicate| assert object.respond_to?(predicate)}
1011
+ end
1012
+ end
1013
+
1014
+ class MachineWithOwnerSubclassTest < Test::Unit::TestCase
1015
+ def setup
1016
+ @klass = Class.new
1017
+ @machine = StateMachine::Machine.new(@klass)
1018
+ @subclass = Class.new(@klass)
1019
+ end
1020
+
1021
+ def test_should_have_a_different_collection_of_state_machines
1022
+ assert_not_same @klass.state_machines, @subclass.state_machines
1023
+ end
1024
+
1025
+ def test_should_have_the_same_attribute_associated_state_machines
1026
+ assert_equal @klass.state_machines, @subclass.state_machines
1027
+ end
1028
+ end
1029
+
1030
+ class MachineWithExistingMachinesOnOwnerClassTest < Test::Unit::TestCase
1031
+ def setup
1032
+ @klass = Class.new
1033
+ @machine = StateMachine::Machine.new(@klass, :initial => 'off')
1034
+ @second_machine = StateMachine::Machine.new(@klass, 'status', :initial => 'active')
1035
+ @object = @klass.new
1036
+ end
1037
+
1038
+ def test_should_track_each_state_machine
1039
+ expected = {'state' => @machine, 'status' => @second_machine}
1040
+ assert_equal expected, @klass.state_machines
1041
+ end
1042
+
1043
+ def test_should_initialize_state_for_both_machines
1044
+ assert_equal 'off', @object.state
1045
+ assert_equal 'active', @object.status
1046
+ end
1047
+ end
1048
+
1049
+ class MachineFinderWithoutExistingMachineTest < Test::Unit::TestCase
1050
+ def setup
1051
+ @klass = Class.new
1052
+ @machine = StateMachine::Machine.find_or_create(@klass)
1053
+ end
1054
+
1055
+ def test_should_create_a_new_machine
1056
+ assert_not_nil @machine
1057
+ end
1058
+
1059
+ def test_should_use_default_state
1060
+ assert_equal 'state', @machine.attribute
1061
+ end
1062
+ end
1063
+
1064
+ class MachineFinderWithExistingOnSameClassTest < Test::Unit::TestCase
1065
+ def setup
1066
+ @klass = Class.new
1067
+ @existing_machine = StateMachine::Machine.new(@klass)
1068
+ @machine = StateMachine::Machine.find_or_create(@klass)
290
1069
  end
291
1070
 
292
- def test_should_support_except_on_query
293
- @machine.before_transition :except_on => 'turn_off', :do => lambda {|switch| switch.callbacks << 'turn_off'}
294
- @machine.before_transition :except_on => 'turn_on', :do => lambda {|switch| switch.callbacks << 'turn_on'}
1071
+ def test_should_not_create_a_new_machine
1072
+ assert_same @machine, @existing_machine
1073
+ end
1074
+ end
1075
+
1076
+ class MachineFinderWithExistingMachineOnSuperclassTest < Test::Unit::TestCase
1077
+ def setup
1078
+ integration = Module.new do
1079
+ def self.matches?(klass)
1080
+ false
1081
+ end
1082
+ end
1083
+ StateMachine::Integrations.const_set('Custom', integration)
1084
+
1085
+ @base_class = Class.new
1086
+ @base_machine = StateMachine::Machine.new(@base_class, 'status', :action => :save, :integration => :custom)
1087
+ @base_machine.event(:turn_on) {}
1088
+ @base_machine.before_transition(lambda {})
1089
+ @base_machine.after_transition(lambda {})
295
1090
 
296
- @event.fire(@switch)
297
- assert_equal %w(turn_off), @switch.callbacks
1091
+ @klass = Class.new(@base_class)
1092
+ @machine = StateMachine::Machine.find_or_create(@klass, 'status')
1093
+ end
1094
+
1095
+ def test_should_create_a_new_machine
1096
+ assert_not_nil @machine
1097
+ assert_not_same @machine, @base_machine
1098
+ end
1099
+
1100
+ def test_should_copy_the_base_attribute
1101
+ assert_equal 'status', @machine.attribute
1102
+ end
1103
+
1104
+ def test_should_copy_the_base_configuration
1105
+ assert_equal :save, @machine.action
1106
+ end
1107
+
1108
+ def test_should_copy_events
1109
+ # Can't assert equal arrays since their machines change
1110
+ assert_equal 1, @machine.events.size
1111
+ end
1112
+
1113
+ def test_should_copy_before_callbacks
1114
+ assert_equal @base_machine.callbacks[:before], @machine.callbacks[:before]
1115
+ end
1116
+
1117
+ def test_should_copy_after_transitions
1118
+ assert_equal @base_machine.callbacks[:after], @machine.callbacks[:after]
1119
+ end
1120
+
1121
+ def test_should_use_the_same_integration
1122
+ assert (class << @machine; ancestors; end).include?(StateMachine::Integrations::Custom)
298
1123
  end
299
1124
 
300
1125
  def teardown
301
- Switch.class_eval do
302
- @before_transition_state_callbacks = nil
303
- @after_transition_state_callbacks = nil
1126
+ StateMachine::Integrations.send(:remove_const, 'Custom')
1127
+ end
1128
+ end
1129
+
1130
+ class MachineFinderCustomOptionsTest < Test::Unit::TestCase
1131
+ def setup
1132
+ @klass = Class.new
1133
+ @machine = StateMachine::Machine.find_or_create(@klass, 'status', :initial => 'off')
1134
+ @object = @klass.new
1135
+ end
1136
+
1137
+ def test_should_use_custom_attribute
1138
+ assert_equal 'status', @machine.attribute
1139
+ end
1140
+
1141
+ def test_should_set_custom_initial_state
1142
+ assert_equal 'off', @machine.initial_state(@object)
1143
+ end
1144
+ end
1145
+
1146
+ begin
1147
+ # Load library
1148
+ require 'rubygems'
1149
+ require 'graphviz'
1150
+
1151
+ class MachineDrawingTest < Test::Unit::TestCase
1152
+ def setup
1153
+ @klass = Class.new do
1154
+ def self.name; 'Vehicle'; end
1155
+ end
1156
+ @machine = StateMachine::Machine.new(@klass)
1157
+ @machine.event :ignite do
1158
+ transition :from => 'parked', :to => 'idling'
1159
+ end
1160
+ end
1161
+
1162
+ def test_should_raise_exception_if_invalid_option_specified
1163
+ assert_raise(ArgumentError) {@machine.draw(:invalid => true)}
1164
+ end
1165
+
1166
+ def test_should_save_file_with_class_name_by_default
1167
+ @machine.draw
1168
+ assert File.exist?('./Vehicle_state.png')
1169
+ ensure
1170
+ FileUtils.rm('./Vehicle_state.png')
1171
+ end
1172
+
1173
+ def test_should_allow_base_name_to_be_customized
1174
+ @machine.draw(:name => 'machine')
1175
+ assert File.exist?('./machine.png')
1176
+ ensure
1177
+ FileUtils.rm('./machine.png')
1178
+ end
1179
+
1180
+ def test_should_allow_format_to_be_customized
1181
+ @machine.draw(:format => 'jpg')
1182
+ assert File.exist?('./Vehicle_state.jpg')
1183
+ ensure
1184
+ FileUtils.rm('./Vehicle_state.jpg')
1185
+ end
1186
+
1187
+ def test_should_allow_path_to_be_customized
1188
+ @machine.draw(:path => "#{File.dirname(__FILE__)}/")
1189
+ assert File.exist?("#{File.dirname(__FILE__)}/Vehicle_state.png")
1190
+ ensure
1191
+ FileUtils.rm("#{File.dirname(__FILE__)}/Vehicle_state.png")
1192
+ end
1193
+ end
1194
+
1195
+ class MachineDrawingWithIntegerStatesTest < Test::Unit::TestCase
1196
+ def setup
1197
+ @klass = Class.new do
1198
+ def self.name; 'Vehicle'; end
1199
+ end
1200
+ @machine = StateMachine::Machine.new(@klass, :state_id)
1201
+ @machine.event :ignite do
1202
+ transition :from => 2, :to => 1
1203
+ end
1204
+ @machine.draw
1205
+ end
1206
+
1207
+ def test_should_draw_machine
1208
+ assert File.exist?('./Vehicle_state_id.png')
1209
+ ensure
1210
+ FileUtils.rm('./Vehicle_state_id.png')
1211
+ end
1212
+ end
1213
+
1214
+ class MachineDrawingWithTimeStatesTest < Test::Unit::TestCase
1215
+ def setup
1216
+ @klass = Class.new do
1217
+ def self.name; 'Vehicle'; end
1218
+ end
1219
+ @machine = StateMachine::Machine.new(@klass, :activated_at)
1220
+ @machine.event :activate do
1221
+ transition :from => nil, :to => lambda {Time.now}
1222
+ end
1223
+ @machine.draw
1224
+ end
1225
+
1226
+ def test_should_draw_machine
1227
+ assert File.exist?('./Vehicle_activated_at.png')
1228
+ ensure
1229
+ FileUtils.rm('./Vehicle_activated_at.png')
1230
+ end
1231
+ end
1232
+
1233
+ class MachineClassDrawingTest < Test::Unit::TestCase
1234
+ def setup
1235
+ @klass = Class.new do
1236
+ def self.name; 'Vehicle'; end
1237
+ end
1238
+ @machine = StateMachine::Machine.new(@klass)
1239
+ @machine.event :ignite do
1240
+ transition :from => 'parked', :to => 'idling'
1241
+ end
1242
+ end
1243
+
1244
+ def test_should_raise_exception_if_no_class_names_specified
1245
+ assert_raise(ArgumentError) {StateMachine::Machine.draw(nil)}
1246
+ end
1247
+
1248
+ def test_should_load_files
1249
+ StateMachine::Machine.draw('Switch', :file => "#{File.dirname(__FILE__)}/../classes/switch.rb")
1250
+ assert defined?(::Switch)
1251
+ ensure
1252
+ FileUtils.rm('./Switch_state.png')
1253
+ end
1254
+
1255
+ def test_should_allow_path_and_format_to_be_customized
1256
+ StateMachine::Machine.draw('Switch', :file => "#{File.dirname(__FILE__)}/../classes/switch.rb", :path => "#{File.dirname(__FILE__)}/", :format => 'jpg')
1257
+ assert File.exist?("#{File.dirname(__FILE__)}/Switch_state.jpg")
1258
+ ensure
1259
+ FileUtils.rm("#{File.dirname(__FILE__)}/Switch_state.jpg")
304
1260
  end
305
1261
  end
1262
+ rescue LoadError
1263
+ $stderr.puts 'Skipping GraphViz tests. `gem install ruby-graphviz` and try again.'
306
1264
  end