state_machine 0.8.1 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/CHANGELOG.rdoc +17 -0
  2. data/LICENSE +1 -1
  3. data/README.rdoc +162 -23
  4. data/Rakefile +3 -18
  5. data/lib/state_machine.rb +3 -4
  6. data/lib/state_machine/callback.rb +65 -13
  7. data/lib/state_machine/eval_helpers.rb +20 -4
  8. data/lib/state_machine/initializers.rb +4 -0
  9. data/lib/state_machine/initializers/merb.rb +1 -0
  10. data/lib/state_machine/initializers/rails.rb +7 -0
  11. data/lib/state_machine/integrations.rb +21 -6
  12. data/lib/state_machine/integrations/active_model.rb +414 -0
  13. data/lib/state_machine/integrations/active_model/locale.rb +11 -0
  14. data/lib/state_machine/integrations/{active_record → active_model}/observer.rb +7 -7
  15. data/lib/state_machine/integrations/active_record.rb +65 -129
  16. data/lib/state_machine/integrations/active_record/locale.rb +4 -11
  17. data/lib/state_machine/integrations/data_mapper.rb +24 -6
  18. data/lib/state_machine/integrations/data_mapper/observer.rb +36 -0
  19. data/lib/state_machine/integrations/mongo_mapper.rb +295 -0
  20. data/lib/state_machine/integrations/sequel.rb +33 -7
  21. data/lib/state_machine/machine.rb +121 -23
  22. data/lib/state_machine/machine_collection.rb +12 -103
  23. data/lib/state_machine/transition.rb +125 -164
  24. data/lib/state_machine/transition_collection.rb +244 -0
  25. data/lib/tasks/state_machine.rb +12 -15
  26. data/test/functional/state_machine_test.rb +11 -1
  27. data/test/unit/callback_test.rb +305 -32
  28. data/test/unit/eval_helpers_test.rb +103 -1
  29. data/test/unit/event_test.rb +2 -1
  30. data/test/unit/guard_test.rb +2 -1
  31. data/test/unit/integrations/active_model_test.rb +909 -0
  32. data/test/unit/integrations/active_record_test.rb +1542 -1292
  33. data/test/unit/integrations/data_mapper_test.rb +1369 -1041
  34. data/test/unit/integrations/mongo_mapper_test.rb +1349 -0
  35. data/test/unit/integrations/sequel_test.rb +1214 -985
  36. data/test/unit/integrations_test.rb +8 -0
  37. data/test/unit/machine_collection_test.rb +140 -513
  38. data/test/unit/machine_test.rb +212 -10
  39. data/test/unit/state_test.rb +2 -1
  40. data/test/unit/transition_collection_test.rb +2098 -0
  41. data/test/unit/transition_test.rb +704 -552
  42. metadata +16 -3
@@ -69,6 +69,10 @@ class MachineByDefaultTest < Test::Unit::TestCase
69
69
  assert_equal 'cannot transition via "park"', @machine.generate_message(:invalid_transition, [[:event, :park]])
70
70
  end
71
71
 
72
+ def test_should_not_be_extended_by_the_active_model_integration
73
+ assert !(class << @machine; ancestors; end).include?(StateMachine::Integrations::ActiveModel)
74
+ end
75
+
72
76
  def test_should_not_be_extended_by_the_active_record_integration
73
77
  assert !(class << @machine; ancestors; end).include?(StateMachine::Integrations::ActiveRecord)
74
78
  end
@@ -77,6 +81,10 @@ class MachineByDefaultTest < Test::Unit::TestCase
77
81
  assert !(class << @machine; ancestors; end).include?(StateMachine::Integrations::DataMapper)
78
82
  end
79
83
 
84
+ def test_should_not_be_extended_by_the_mongo_mapper_integration
85
+ assert !(class << @machine; ancestors; end).include?(StateMachine::Integrations::MongoMapper)
86
+ end
87
+
80
88
  def test_should_not_be_extended_by_the_sequel_integration
81
89
  assert !(class << @machine; ancestors; end).include?(StateMachine::Integrations::Sequel)
82
90
  end
@@ -216,6 +224,12 @@ class MachineWithStaticInitialStateTest < Test::Unit::TestCase
216
224
  assert_equal 'parked', @machine.initial_state(object).value
217
225
  end
218
226
 
227
+ def test_should_write_to_attribute_when_initializing_state
228
+ object = @klass.allocate
229
+ @machine.initialize_state(object)
230
+ assert_equal 'parked', object.state
231
+ end
232
+
219
233
  def test_should_set_initial_on_state_object
220
234
  assert @machine.state(:parked).initial
221
235
  end
@@ -276,6 +290,13 @@ class MachineWithDynamicInitialStateTest < Test::Unit::TestCase
276
290
  assert_equal :idling, @machine.initial_state(@object).name
277
291
  end
278
292
 
293
+ def test_should_write_to_attribute_when_initializing_state
294
+ object = @klass.allocate
295
+ object.initial_state = :parked
296
+ @machine.initialize_state(object)
297
+ assert_equal 'parked', object.state
298
+ end
299
+
279
300
  def test_should_set_initial_state_on_created_object
280
301
  assert_equal 'default', @object.state
281
302
  end
@@ -435,7 +456,39 @@ class MachineWithIntegrationTest < Test::Unit::TestCase
435
456
  end
436
457
  end
437
458
 
438
- class MachineWithActionTest < Test::Unit::TestCase
459
+ class MachineWithActionUndefinedTest < Test::Unit::TestCase
460
+ def setup
461
+ @klass = Class.new
462
+ @machine = StateMachine::Machine.new(@klass, :action => :save)
463
+ @object = @klass.new
464
+ end
465
+
466
+ def test_should_define_an_event_attribute_reader
467
+ assert @object.respond_to?(:state_event)
468
+ end
469
+
470
+ def test_should_define_an_event_attribute_writer
471
+ assert @object.respond_to?(:state_event=)
472
+ end
473
+
474
+ def test_should_define_an_event_transition_attribute_reader
475
+ assert @object.respond_to?(:state_event_transition)
476
+ end
477
+
478
+ def test_should_define_an_event_transition_attribute_writer
479
+ assert @object.respond_to?(:state_event_transition=)
480
+ end
481
+
482
+ def test_should_not_define_action
483
+ assert !@object.respond_to?(:save)
484
+ end
485
+
486
+ def test_should_not_mark_action_helper_as_defined
487
+ assert !@machine.action_helper_defined?
488
+ end
489
+ end
490
+
491
+ class MachineWithActionDefinedInClassTest < Test::Unit::TestCase
439
492
  def setup
440
493
  @klass = Class.new do
441
494
  def save
@@ -461,11 +514,27 @@ class MachineWithActionTest < Test::Unit::TestCase
461
514
  def test_should_define_an_event_transition_attribute_writer
462
515
  assert @object.respond_to?(:state_event_transition=)
463
516
  end
517
+
518
+ def test_should_not_define_action
519
+ assert !@klass.ancestors.any? {|ancestor| ancestor != @klass && ancestor.method_defined?(:save)}
520
+ end
521
+
522
+ def test_should_not_mark_action_helper_as_defined
523
+ assert !@machine.action_helper_defined?
524
+ end
464
525
  end
465
526
 
466
- class MachineWithActionUndefinedTest < Test::Unit::TestCase
527
+ class MachineWithActionDefinedInIncludedModuleTest < Test::Unit::TestCase
467
528
  def setup
468
- @klass = Class.new
529
+ @mod = mod = Module.new do
530
+ def save
531
+ end
532
+ end
533
+
534
+ @klass = Class.new do
535
+ include mod
536
+ end
537
+
469
538
  @machine = StateMachine::Machine.new(@klass, :action => :save)
470
539
  @object = @klass.new
471
540
  end
@@ -486,8 +555,121 @@ class MachineWithActionUndefinedTest < Test::Unit::TestCase
486
555
  assert @object.respond_to?(:state_event_transition=)
487
556
  end
488
557
 
489
- def test_should_not_define_action
490
- assert !@object.respond_to?(:save)
558
+ def test_should_define_action
559
+ assert @klass.ancestors.any? {|ancestor| ![@klass, @mod].include?(ancestor) && ancestor.method_defined?(:save)}
560
+ end
561
+
562
+ def test_should_keep_action_public
563
+ assert @klass.public_method_defined?(:save)
564
+ end
565
+
566
+ def test_should_mark_action_helper_as_defined
567
+ assert @machine.action_helper_defined?
568
+ end
569
+ end
570
+
571
+ class MachineWithActionDefinedInSuperclassTest < Test::Unit::TestCase
572
+ def setup
573
+ @superclass = Class.new do
574
+ def save
575
+ end
576
+ end
577
+ @klass = Class.new(@superclass)
578
+
579
+ @machine = StateMachine::Machine.new(@klass, :action => :save)
580
+ @object = @klass.new
581
+ end
582
+
583
+ def test_should_define_an_event_attribute_reader
584
+ assert @object.respond_to?(:state_event)
585
+ end
586
+
587
+ def test_should_define_an_event_attribute_writer
588
+ assert @object.respond_to?(:state_event=)
589
+ end
590
+
591
+ def test_should_define_an_event_transition_attribute_reader
592
+ assert @object.respond_to?(:state_event_transition)
593
+ end
594
+
595
+ def test_should_define_an_event_transition_attribute_writer
596
+ assert @object.respond_to?(:state_event_transition=)
597
+ end
598
+
599
+ def test_should_define_action
600
+ assert @klass.ancestors.any? {|ancestor| ![@klass, @superclass].include?(ancestor) && ancestor.method_defined?(:save)}
601
+ end
602
+
603
+ def test_should_keep_action_public
604
+ assert @klass.public_method_defined?(:save)
605
+ end
606
+
607
+ def test_should_mark_action_helper_as_defined
608
+ assert @machine.action_helper_defined?
609
+ end
610
+ end
611
+
612
+ class MachineWithPrivateActionTest < Test::Unit::TestCase
613
+ def setup
614
+ @superclass = Class.new do
615
+ private
616
+ def save
617
+ end
618
+ end
619
+ @klass = Class.new(@superclass)
620
+
621
+ @machine = StateMachine::Machine.new(@klass, :action => :save)
622
+ @object = @klass.new
623
+ end
624
+
625
+ def test_should_define_an_event_attribute_reader
626
+ assert @object.respond_to?(:state_event)
627
+ end
628
+
629
+ def test_should_define_an_event_attribute_writer
630
+ assert @object.respond_to?(:state_event=)
631
+ end
632
+
633
+ def test_should_define_an_event_transition_attribute_reader
634
+ assert @object.respond_to?(:state_event_transition)
635
+ end
636
+
637
+ def test_should_define_an_event_transition_attribute_writer
638
+ assert @object.respond_to?(:state_event_transition=)
639
+ end
640
+
641
+ def test_should_define_action
642
+ assert @klass.ancestors.any? {|ancestor| ![@klass, @superclass].include?(ancestor) && ancestor.private_method_defined?(:save)}
643
+ end
644
+
645
+ def test_should_keep_action_private
646
+ assert @klass.private_method_defined?(:save)
647
+ end
648
+
649
+ def test_should_mark_action_helper_as_defined
650
+ assert @machine.action_helper_defined?
651
+ end
652
+ end
653
+
654
+ class MachineWithActionAlreadyOverriddenTest < Test::Unit::TestCase
655
+ def setup
656
+ @superclass = Class.new do
657
+ def save
658
+ end
659
+ end
660
+ @klass = Class.new(@superclass)
661
+
662
+ StateMachine::Machine.new(@klass, :action => :save)
663
+ @machine = StateMachine::Machine.new(@klass, :status, :action => :save)
664
+ @object = @klass.new
665
+ end
666
+
667
+ def test_should_not_redefine_action
668
+ assert_equal 1, @klass.ancestors.select {|ancestor| ![@klass, @superclass].include?(ancestor) && ancestor.method_defined?(:save)}.length
669
+ end
670
+
671
+ def test_should_mark_action_helper_as_defined
672
+ assert @machine.action_helper_defined?
491
673
  end
492
674
  end
493
675
 
@@ -595,6 +777,7 @@ class MachineAfterBeingCopiedTest < Test::Unit::TestCase
595
777
  @machine.event(:ignite) {}
596
778
  @machine.before_transition(lambda {})
597
779
  @machine.after_transition(lambda {})
780
+ @machine.around_transition(lambda {})
598
781
 
599
782
  @copied_machine = @machine.clone
600
783
  end
@@ -1407,17 +1590,22 @@ class MachineWithTransitionCallbacksTest < Test::Unit::TestCase
1407
1590
  def test_should_invoke_callbacks_during_transition
1408
1591
  @machine.before_transition lambda {|object| object.callbacks << 'before'}
1409
1592
  @machine.after_transition lambda {|object| object.callbacks << 'after'}
1593
+ @machine.around_transition lambda {|object, transition, block| object.callbacks << 'before_around'; block.call; object.callbacks << 'after_around'}
1410
1594
 
1411
1595
  @event.fire(@object)
1412
- assert_equal %w(before after), @object.callbacks
1596
+ assert_equal %w(before before_around after_around after), @object.callbacks
1413
1597
  end
1414
1598
 
1415
1599
  def test_should_allow_multiple_callbacks
1416
1600
  @machine.before_transition lambda {|object| object.callbacks << 'before1'}, lambda {|object| object.callbacks << 'before2'}
1417
1601
  @machine.after_transition lambda {|object| object.callbacks << 'after1'}, lambda {|object| object.callbacks << 'after2'}
1602
+ @machine.around_transition(
1603
+ lambda {|object, transition, block| object.callbacks << 'before_around1'; block.call; object.callbacks << 'after_around1'},
1604
+ lambda {|object, transition, block| object.callbacks << 'before_around2'; block.call; object.callbacks << 'after_around2'}
1605
+ )
1418
1606
 
1419
1607
  @event.fire(@object)
1420
- assert_equal %w(before1 before2 after1 after2), @object.callbacks
1608
+ assert_equal %w(before1 before2 before_around1 before_around2 after_around2 after_around1 after1 after2), @object.callbacks
1421
1609
  end
1422
1610
 
1423
1611
  def test_should_allow_multiple_callbacks_with_requirements
@@ -1425,9 +1613,19 @@ class MachineWithTransitionCallbacksTest < Test::Unit::TestCase
1425
1613
  @machine.before_transition lambda {|object| object.callbacks << 'before_idling1'}, lambda {|object| object.callbacks << 'before_idling2'}, :from => :idling
1426
1614
  @machine.after_transition lambda {|object| object.callbacks << 'after_parked1'}, lambda {|object| object.callbacks << 'after_parked2'}, :from => :parked
1427
1615
  @machine.after_transition lambda {|object| object.callbacks << 'after_idling1'}, lambda {|object| object.callbacks << 'after_idling2'}, :from => :idling
1616
+ @machine.around_transition(
1617
+ lambda {|object, transition, block| object.callbacks << 'before_around_parked1'; block.call; object.callbacks << 'after_around_parked1'},
1618
+ lambda {|object, transition, block| object.callbacks << 'before_around_parked2'; block.call; object.callbacks << 'after_around_parked2'},
1619
+ :from => :parked
1620
+ )
1621
+ @machine.around_transition(
1622
+ lambda {|object, transition, block| object.callbacks << 'before_around_idling1'; block.call; object.callbacks << 'after_around_idling1'},
1623
+ lambda {|object, transition, block| object.callbacks << 'before_around_idling2'; block.call; object.callbacks << 'after_around_idling2'},
1624
+ :from => :idling
1625
+ )
1428
1626
 
1429
1627
  @event.fire(@object)
1430
- assert_equal %w(before_parked1 before_parked2 after_parked1 after_parked2), @object.callbacks
1628
+ assert_equal %w(before_parked1 before_parked2 before_around_parked1 before_around_parked2 after_around_parked2 after_around_parked1 after_parked1 after_parked2), @object.callbacks
1431
1629
  end
1432
1630
 
1433
1631
  def test_should_support_from_requirement
@@ -1489,14 +1687,16 @@ class MachineWithTransitionCallbacksTest < Test::Unit::TestCase
1489
1687
  def test_should_track_states_defined_in_transition_callbacks
1490
1688
  @machine.before_transition :parked => :idling, :do => lambda {}
1491
1689
  @machine.after_transition :first_gear => :second_gear, :do => lambda {}
1690
+ @machine.around_transition :third_gear => :fourth_gear, :do => lambda {}
1492
1691
 
1493
- assert_equal [:parked, :idling, :first_gear, :second_gear], @machine.states.map {|state| state.name}
1692
+ assert_equal [:parked, :idling, :first_gear, :second_gear, :third_gear, :fourth_gear], @machine.states.map {|state| state.name}
1494
1693
  end
1495
1694
 
1496
1695
  def test_should_not_duplicate_states_defined_in_multiple_event_transitions
1497
1696
  @machine.before_transition :parked => :idling, :do => lambda {}
1498
1697
  @machine.after_transition :first_gear => :second_gear, :do => lambda {}
1499
1698
  @machine.after_transition :parked => :idling, :do => lambda {}
1699
+ @machine.around_transition :parked => :idling, :do => lambda {}
1500
1700
 
1501
1701
  assert_equal [:parked, :idling, :first_gear, :second_gear], @machine.states.map {|state| state.name}
1502
1702
  end
@@ -1754,6 +1954,7 @@ class MachineFinderWithExistingMachineOnSuperclassTest < Test::Unit::TestCase
1754
1954
  @base_machine.event(:ignite) {}
1755
1955
  @base_machine.before_transition(lambda {})
1756
1956
  @base_machine.after_transition(lambda {})
1957
+ @base_machine.around_transition(lambda {})
1757
1958
 
1758
1959
  @klass = Class.new(@base_class)
1759
1960
  @machine = StateMachine::Machine.find_or_create(@klass, :status) {}
@@ -1835,6 +2036,7 @@ end
1835
2036
  begin
1836
2037
  # Load library
1837
2038
  require 'rubygems'
2039
+ gem 'ruby-graphviz', '>=0.9.0'
1838
2040
  require 'graphviz'
1839
2041
 
1840
2042
  class MachineDrawingTest < Test::Unit::TestCase
@@ -2003,5 +2205,5 @@ begin
2003
2205
  end
2004
2206
  end
2005
2207
  rescue LoadError
2006
- $stderr.puts 'Skipping GraphViz StateMachine::Machine tests. `gem install ruby-graphviz` and try again.'
2208
+ $stderr.puts 'Skipping GraphViz StateMachine::Machine tests. `gem install ruby-graphviz` >= v0.9.0 and try again.'
2007
2209
  end
@@ -666,6 +666,7 @@ end
666
666
  begin
667
667
  # Load library
668
668
  require 'rubygems'
669
+ gem 'ruby-graphviz', '>=0.9.0'
669
670
  require 'graphviz'
670
671
 
671
672
  class StateDrawingTest < Test::Unit::TestCase
@@ -790,5 +791,5 @@ begin
790
791
  end
791
792
  end
792
793
  rescue LoadError
793
- $stderr.puts 'Skipping GraphViz StateMachine::State tests. `gem install ruby-graphviz` and try again.'
794
+ $stderr.puts 'Skipping GraphViz StateMachine::State tests. `gem install ruby-graphviz` >= v0.9.0 and try again.'
794
795
  end
@@ -0,0 +1,2098 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ class TransitionCollectionTest < Test::Unit::TestCase
4
+ def test_should_raise_exception_if_invalid_option_specified
5
+ exception = assert_raise(ArgumentError) {StateMachine::TransitionCollection.new([], :invalid => true)}
6
+
7
+ end
8
+
9
+ def test_should_raise_exception_if_multiple_transitions_for_same_attribute_specified
10
+ @klass = Class.new
11
+
12
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
13
+ @machine.state :parked, :idling
14
+ @machine.event :ignite
15
+
16
+ @object = @klass.new
17
+
18
+ exception = assert_raise(ArgumentError) do
19
+ StateMachine::TransitionCollection.new([
20
+ StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling),
21
+ StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
22
+ ])
23
+ end
24
+ assert_equal 'Cannot perform multiple transitions in parallel for the same state machine attribute', exception.message
25
+ end
26
+ end
27
+
28
+ class TransitionCollectionByDefaultTest < Test::Unit::TestCase
29
+ def setup
30
+ @transitions = StateMachine::TransitionCollection.new
31
+ end
32
+
33
+ def test_should_not_skip_actions
34
+ assert !@transitions.skip_actions
35
+ end
36
+
37
+ def test_should_not_skip_after
38
+ assert !@transitions.skip_after
39
+ end
40
+
41
+ def test_should_use_transaction
42
+ assert @transitions.use_transaction
43
+ end
44
+
45
+ def test_should_be_empty
46
+ assert @transitions.empty?
47
+ end
48
+ end
49
+
50
+ class TransitionCollectionEmptyWithoutBlockTest < Test::Unit::TestCase
51
+ def setup
52
+ @transitions = StateMachine::TransitionCollection.new
53
+ @result = @transitions.perform
54
+ end
55
+
56
+ def test_should_succeed
57
+ assert_equal true, @result
58
+ end
59
+ end
60
+
61
+
62
+ class TransitionCollectionEmptyWithBlockTest < Test::Unit::TestCase
63
+ def setup
64
+ @transitions = StateMachine::TransitionCollection.new
65
+ end
66
+
67
+ def test_should_raise_exception_if_perform_raises_exception
68
+ assert_raise(ArgumentError) { @transitions.perform { raise ArgumentError } }
69
+ end
70
+
71
+ def test_should_use_block_result_if_non_boolean
72
+ assert_equal 1, @transitions.perform { 1 }
73
+ end
74
+
75
+ def test_should_use_block_result_if_false
76
+ assert_equal false, @transitions.perform { false }
77
+ end
78
+
79
+ def test_should_use_block_reslut_if_nil
80
+ assert_equal nil, @transitions.perform { nil }
81
+ end
82
+ end
83
+
84
+ class TransitionCollectionInvalidTest < Test::Unit::TestCase
85
+ def setup
86
+ @transitions = StateMachine::TransitionCollection.new([false])
87
+ end
88
+
89
+ def test_should_be_empty
90
+ assert @transitions.empty?
91
+ end
92
+
93
+ def test_should_not_succeed
94
+ assert_equal false, @transitions.perform
95
+ end
96
+
97
+ def test_should_not_run_perform_block
98
+ ran_block = false
99
+ @transitions.perform { ran_block = true }
100
+ assert !ran_block
101
+ end
102
+ end
103
+
104
+ class TransitionCollectionPartialInvalidTest < Test::Unit::TestCase
105
+ def setup
106
+ @klass = Class.new do
107
+ attr_accessor :ran_transaction
108
+ end
109
+
110
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
111
+ @machine.state :idling
112
+ @machine.event :ignite
113
+ @machine.before_transition {@ran_before = true}
114
+ @machine.after_transition {@ran_after = true}
115
+ @machine.around_transition {|block| @ran_around_before = true; block.call; @ran_around_after = true}
116
+
117
+ class << @machine
118
+ def within_transaction(object)
119
+ object.ran_transaction = true
120
+ end
121
+ end
122
+
123
+ @object = @klass.new
124
+
125
+ @transitions = StateMachine::TransitionCollection.new([
126
+ StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling),
127
+ false
128
+ ])
129
+ end
130
+
131
+ def test_should_not_store_invalid_values
132
+ assert_equal 1, @transitions.length
133
+ end
134
+
135
+ def test_should_not_succeed
136
+ assert_equal false, @transitions.perform
137
+ end
138
+
139
+ def test_should_not_start_transaction
140
+ assert !@object.ran_transaction
141
+ end
142
+
143
+ def test_should_not_run_perform_block
144
+ ran_block = false
145
+ @transitions.perform { ran_block = true }
146
+ assert !ran_block
147
+ end
148
+
149
+ def test_should_not_run_before_callbacks
150
+ assert !@ran_before
151
+ end
152
+
153
+ def test_should_not_persist_states
154
+ assert_equal 'parked', @object.state
155
+ end
156
+
157
+ def test_should_not_run_after_callbacks
158
+ assert !@ran_after
159
+ end
160
+
161
+ def test_should_not_run_around_callbacks_before_yield
162
+ assert !@ran_around_before
163
+ end
164
+
165
+ def test_should_not_run_around_callbacks_after_yield
166
+ assert !@ran_around_after
167
+ end
168
+ end
169
+
170
+ class TransitionCollectionValidTest < Test::Unit::TestCase
171
+ def setup
172
+ @klass = Class.new do
173
+ attr_reader :persisted
174
+
175
+ def initialize
176
+ super
177
+ @persisted = []
178
+ end
179
+
180
+ def state=(value)
181
+ @persisted << 'state' if @persisted
182
+ @state = value
183
+ end
184
+
185
+ def status=(value)
186
+ @persisted << 'status' if @persisted
187
+ @status = value
188
+ end
189
+ end
190
+
191
+ @state = StateMachine::Machine.new(@klass, :initial => :parked)
192
+ @state.state :idling
193
+ @state.event :ignite
194
+ @status = StateMachine::Machine.new(@klass, :status, :initial => :first_gear)
195
+ @status.state :second_gear
196
+ @status.event :shift_up
197
+
198
+ @object = @klass.new
199
+
200
+ @result = StateMachine::TransitionCollection.new([
201
+ @state_transition = StateMachine::Transition.new(@object, @state, :ignite, :parked, :idling),
202
+ @status_transition = StateMachine::Transition.new(@object, @status, :shift_up, :first_gear, :second_gear)
203
+ ]).perform
204
+ end
205
+
206
+ def test_should_succeed
207
+ assert_equal true, @result
208
+ end
209
+
210
+ def test_should_persist_each_state
211
+ assert_equal 'idling', @object.state
212
+ assert_equal 'second_gear', @object.status
213
+ end
214
+
215
+ def test_should_persist_in_order
216
+ assert_equal ['state', 'status'], @object.persisted
217
+ end
218
+
219
+ def test_should_store_results_in_transitions
220
+ assert_nil @state_transition.result
221
+ assert_nil @status_transition.result
222
+ end
223
+ end
224
+
225
+ class TransitionCollectionWithoutTransactionsTest < Test::Unit::TestCase
226
+ def setup
227
+ @klass = Class.new do
228
+ attr_accessor :ran_transaction
229
+ end
230
+
231
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
232
+ @machine.state :idling
233
+ @machine.event :ignite
234
+
235
+ class << @machine
236
+ def within_transaction(object)
237
+ object.ran_transaction = true
238
+ end
239
+ end
240
+
241
+ @object = @klass.new
242
+ @transitions = StateMachine::TransitionCollection.new([
243
+ StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
244
+ ], :transaction => false)
245
+ @transitions.perform
246
+ end
247
+
248
+ def test_should_not_run_within_transaction
249
+ assert !@object.ran_transaction
250
+ end
251
+ end
252
+
253
+ class TransitionCollectionWithTransactionsTest < Test::Unit::TestCase
254
+ def setup
255
+ @klass = Class.new do
256
+ attr_accessor :running_transaction, :cancelled_transaction
257
+ end
258
+
259
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
260
+ @machine.state :idling
261
+ @machine.event :ignite
262
+
263
+ class << @machine
264
+ def within_transaction(object)
265
+ object.running_transaction = true
266
+ object.cancelled_transaction = yield == false
267
+ object.running_transaction = false
268
+ end
269
+ end
270
+
271
+ @object = @klass.new
272
+ @transitions = StateMachine::TransitionCollection.new([
273
+ StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
274
+ ], :transaction => true)
275
+ end
276
+
277
+ def test_should_run_before_callbacks_within_transaction
278
+ @machine.before_transition {|object| @in_transaction = object.running_transaction}
279
+ @transitions.perform
280
+
281
+ assert @in_transaction
282
+ end
283
+
284
+ def test_should_run_action_within_transaction
285
+ @transitions.perform { @in_transaction = @object.running_transaction }
286
+
287
+ assert @in_transaction
288
+ end
289
+
290
+ def test_should_run_after_callbacks_within_transaction
291
+ @machine.after_transition {|object| @in_transaction = object.running_transaction}
292
+ @transitions.perform
293
+
294
+ assert @in_transaction
295
+ end
296
+
297
+ def test_should_cancel_the_transaction_on_before_halt
298
+ @machine.before_transition {throw :halt}
299
+
300
+ @transitions.perform
301
+ assert @object.cancelled_transaction
302
+ end
303
+
304
+ def test_should_cancel_the_transaction_on_action_failure
305
+ @transitions.perform { false }
306
+ assert @object.cancelled_transaction
307
+ end
308
+
309
+ def test_should_not_cancel_the_transaction_on_after_halt
310
+ @machine.after_transition {throw :halt}
311
+
312
+ @transitions.perform
313
+ assert !@object.cancelled_transaction
314
+ end
315
+ end
316
+
317
+ class TransitionCollectionWithEmptyActionsTest < Test::Unit::TestCase
318
+ def setup
319
+ @klass = Class.new
320
+
321
+ @state = StateMachine::Machine.new(@klass, :initial => :parked)
322
+ @state.state :idling
323
+ @state.event :ignite
324
+
325
+ @status = StateMachine::Machine.new(@klass, :status, :initial => :first_gear)
326
+ @status.state :second_gear
327
+ @status.event :shift_up
328
+
329
+ @object = @klass.new
330
+
331
+ @transitions = StateMachine::TransitionCollection.new([
332
+ @state_transition = StateMachine::Transition.new(@object, @state, :ignite, :parked, :idling),
333
+ @status_transition = StateMachine::Transition.new(@object, @status, :shift_up, :first_gear, :second_gear)
334
+ ])
335
+
336
+ @object.state = 'idling'
337
+ @object.status = 'second_gear'
338
+
339
+ @result = @transitions.perform
340
+ end
341
+
342
+ def test_should_succeed
343
+ assert_equal true, @result
344
+ end
345
+
346
+ def test_should_persist_states
347
+ assert_equal 'idling', @object.state
348
+ assert_equal 'second_gear', @object.status
349
+ end
350
+
351
+ def test_should_store_results_in_transitions
352
+ assert_nil @state_transition.result
353
+ assert_nil @status_transition.result
354
+ end
355
+ end
356
+
357
+ class TransitionCollectionWithSkippedActionsTest < Test::Unit::TestCase
358
+ def setup
359
+ @klass = Class.new do
360
+ attr_reader :actions
361
+
362
+ def save_state
363
+ (@actions ||= []) << :save_state
364
+ :save_state
365
+ end
366
+
367
+ def save_status
368
+ (@actions ||= []) << :save_status
369
+ :save_status
370
+ end
371
+ end
372
+
373
+ @callbacks = []
374
+
375
+ @state = StateMachine::Machine.new(@klass, :initial => :parked, :action => :save_state)
376
+ @state.state :idling
377
+ @state.event :ignite
378
+ @state.before_transition {@callbacks << :state_before}
379
+ @state.after_transition {@callbacks << :state_after}
380
+ @state.around_transition {|block| @callbacks << :state_around_before; block.call; @callbacks << :state_around_after}
381
+
382
+ @status = StateMachine::Machine.new(@klass, :status, :initial => :first_gear, :action => :save_status)
383
+ @status.state :second_gear
384
+ @status.event :shift_up
385
+ @status.before_transition {@callbacks << :status_before}
386
+ @status.after_transition {@callbacks << :status_after}
387
+ @status.around_transition {|block| @callbacks << :status_around_before; block.call; @callbacks << :status_around_after}
388
+
389
+ @object = @klass.new
390
+
391
+ @transitions = StateMachine::TransitionCollection.new([
392
+ @state_transition = StateMachine::Transition.new(@object, @state, :ignite, :parked, :idling),
393
+ @status_transition = StateMachine::Transition.new(@object, @status, :shift_up, :first_gear, :second_gear)
394
+ ], :actions => false)
395
+ @result = @transitions.perform
396
+ end
397
+
398
+ def test_should_skip_actions
399
+ assert_equal true, @transitions.skip_actions
400
+ end
401
+
402
+ def test_should_succeed
403
+ assert_equal true, @result
404
+ end
405
+
406
+ def test_should_persist_states
407
+ assert_equal 'idling', @object.state
408
+ assert_equal 'second_gear', @object.status
409
+ end
410
+
411
+ def test_should_not_run_actions
412
+ assert_nil @object.actions
413
+ end
414
+
415
+ def test_should_store_results_in_transitions
416
+ assert_nil @state_transition.result
417
+ assert_nil @status_transition.result
418
+ end
419
+
420
+ def test_should_run_all_callbacks
421
+ assert_equal [:state_before, :state_around_before, :status_before, :status_around_before, :status_around_after, :status_after, :state_around_after, :state_after], @callbacks
422
+ end
423
+ end
424
+
425
+ class TransitionCollectionWithSkippedActionsAndBlockTest < Test::Unit::TestCase
426
+ def setup
427
+ @klass = Class.new
428
+
429
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked, :action => :save_state)
430
+ @machine.state :idling
431
+ @machine.event :ignite
432
+
433
+ @object = @klass.new
434
+
435
+ @transitions = StateMachine::TransitionCollection.new([
436
+ @state_transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
437
+ ], :actions => false)
438
+ @result = @transitions.perform { @ran_block = true; 1 }
439
+ end
440
+
441
+ def test_should_succeed
442
+ assert_equal 1, @result
443
+ end
444
+
445
+ def test_should_persist_states
446
+ assert_equal 'idling', @object.state
447
+ end
448
+
449
+ def test_should_run_block
450
+ assert @ran_block
451
+ end
452
+
453
+ def test_should_store_results_in_transitions
454
+ assert_equal 1, @state_transition.result
455
+ end
456
+ end
457
+
458
+ class TransitionCollectionWithDuplicateActionsTest < Test::Unit::TestCase
459
+ def setup
460
+ @klass = Class.new do
461
+ attr_reader :actions
462
+
463
+ def save
464
+ (@actions ||= []) << :save
465
+ :save
466
+ end
467
+ end
468
+
469
+ @state = StateMachine::Machine.new(@klass, :initial => :parked, :action => :save)
470
+ @state.state :idling
471
+ @state.event :ignite
472
+
473
+ @status = StateMachine::Machine.new(@klass, :status, :initial => :first_gear, :action => :save)
474
+ @status.state :second_gear
475
+ @status.event :shift_up
476
+
477
+ @object = @klass.new
478
+
479
+ @transitions = StateMachine::TransitionCollection.new([
480
+ @state_transition = StateMachine::Transition.new(@object, @state, :ignite, :parked, :idling),
481
+ @status_transition = StateMachine::Transition.new(@object, @status, :shift_up, :first_gear, :second_gear)
482
+ ])
483
+ @result = @transitions.perform
484
+ end
485
+
486
+ def test_should_succeed
487
+ assert_equal :save, @result
488
+ end
489
+
490
+ def test_should_persist_states
491
+ assert_equal 'idling', @object.state
492
+ assert_equal 'second_gear', @object.status
493
+ end
494
+
495
+ def test_should_run_action_once
496
+ assert_equal [:save], @object.actions
497
+ end
498
+
499
+ def test_should_store_results_in_transitions
500
+ assert_equal :save, @state_transition.result
501
+ assert_equal :save, @status_transition.result
502
+ end
503
+ end
504
+
505
+ class TransitionCollectionWithDifferentActionsTest < Test::Unit::TestCase
506
+ def setup
507
+ @klass = Class.new do
508
+ attr_reader :actions
509
+
510
+ def save_state
511
+ (@actions ||= []) << :save_state
512
+ :save_state
513
+ end
514
+
515
+ def save_status
516
+ (@actions ||= []) << :save_status
517
+ :save_status
518
+ end
519
+ end
520
+
521
+ @state = StateMachine::Machine.new(@klass, :initial => :parked, :action => :save_state)
522
+ @state.state :idling
523
+ @state.event :ignite
524
+
525
+ @status = StateMachine::Machine.new(@klass, :status, :initial => :first_gear, :action => :save_status)
526
+ @status.state :second_gear
527
+ @status.event :shift_up
528
+
529
+ @object = @klass.new
530
+
531
+ @transitions = StateMachine::TransitionCollection.new([
532
+ @state_transition = StateMachine::Transition.new(@object, @state, :ignite, :parked, :idling),
533
+ @status_transition = StateMachine::Transition.new(@object, @status, :shift_up, :first_gear, :second_gear)
534
+ ])
535
+ end
536
+
537
+ def test_should_succeed
538
+ assert_equal true, @transitions.perform
539
+ end
540
+
541
+ def test_should_persist_states
542
+ @transitions.perform
543
+ assert_equal 'idling', @object.state
544
+ assert_equal 'second_gear', @object.status
545
+ end
546
+
547
+ def test_should_run_actions_in_order
548
+ @transitions.perform
549
+ assert_equal [:save_state, :save_status], @object.actions
550
+ end
551
+
552
+ def test_should_store_results_in_transitions
553
+ @transitions.perform
554
+ assert_equal :save_state, @state_transition.result
555
+ assert_equal :save_status, @status_transition.result
556
+ end
557
+
558
+ def test_should_not_halt_if_action_fails_for_first_transition
559
+ @klass.class_eval do
560
+ def save_state
561
+ (@actions ||= []) << :save_state
562
+ false
563
+ end
564
+ end
565
+
566
+
567
+ assert_equal false, @transitions.perform
568
+ assert_equal [:save_state, :save_status], @object.actions
569
+ end
570
+
571
+ def test_should_halt_if_action_fails_for_second_transition
572
+ @klass.class_eval do
573
+ def save_status
574
+ (@actions ||= []) << :save_status
575
+ false
576
+ end
577
+ end
578
+
579
+ assert_equal false, @transitions.perform
580
+ assert_equal [:save_state, :save_status], @object.actions
581
+ end
582
+
583
+ def test_should_rollback_if_action_errors_for_first_transition
584
+ @klass.class_eval do
585
+ def save_state
586
+ raise ArgumentError
587
+ end
588
+ end
589
+
590
+ begin; @transitions.perform; rescue; end
591
+ assert_equal 'parked', @object.state
592
+ assert_equal 'first_gear', @object.status
593
+ end
594
+
595
+ def test_should_rollback_if_action_errors_for_second_transition
596
+ @klass.class_eval do
597
+ def save_status
598
+ raise ArgumentError
599
+ end
600
+ end
601
+
602
+ begin; @transitions.perform; rescue; end
603
+ assert_equal 'parked', @object.state
604
+ assert_equal 'first_gear', @object.status
605
+ end
606
+
607
+ def test_should_not_run_after_callbacks_if_action_fails_for_first_transition
608
+ @klass.class_eval do
609
+ def save_state
610
+ false
611
+ end
612
+ end
613
+
614
+ @callbacks = []
615
+ @state.after_transition { @callbacks << :state_after }
616
+ @state.around_transition {|block| block.call; @callbacks << :state_around }
617
+ @status.after_transition { @callbacks << :status_after }
618
+ @status.around_transition {|block| block.call; @callbacks << :status_around }
619
+
620
+ @transitions.perform
621
+ assert_equal [], @callbacks
622
+ end
623
+
624
+ def test_should_not_run_after_callbacks_if_action_fails_for_second_transition
625
+ @klass.class_eval do
626
+ def save_status
627
+ false
628
+ end
629
+ end
630
+
631
+ @callbacks = []
632
+ @state.after_transition { @callbacks << :state_after }
633
+ @state.around_transition {|block| block.call; @callbacks << :state_around }
634
+ @status.after_transition { @callbacks << :status_after }
635
+ @status.around_transition {|block| block.call; @callbacks << :status_around }
636
+
637
+ @transitions.perform
638
+ assert_equal [], @callbacks
639
+ end
640
+
641
+ def test_should_run_after_failure_callbacks_if_action_fails_for_first_transition
642
+ @klass.class_eval do
643
+ def save_state
644
+ false
645
+ end
646
+ end
647
+
648
+ @callbacks = []
649
+ @state.after_transition(:include_failures => true) { @callbacks << :state_after }
650
+ @state.around_transition(:include_failures => true) {|block| block.call; @callbacks << :state_around }
651
+ @status.after_transition(:include_failures => true) { @callbacks << :status_after }
652
+ @status.around_transition(:include_failures => true) {|block| block.call; @callbacks << :status_around }
653
+
654
+ @transitions.perform
655
+ assert_equal [:status_around, :status_after, :state_around, :state_after], @callbacks
656
+ end
657
+
658
+ def test_should_run_after_failure_callbacks_if_action_fails_for_second_transition
659
+ @klass.class_eval do
660
+ def save_status
661
+ false
662
+ end
663
+ end
664
+
665
+ @callbacks = []
666
+ @state.after_transition(:include_failures => true) { @callbacks << :state_after }
667
+ @state.around_transition(:include_failures => true) {|block| block.call; @callbacks << :state_around }
668
+ @status.after_transition(:include_failures => true) { @callbacks << :status_after }
669
+ @status.around_transition(:include_failures => true) {|block| block.call; @callbacks << :status_around }
670
+
671
+ @transitions.perform
672
+ assert_equal [:status_around, :status_after, :state_around, :state_after], @callbacks
673
+ end
674
+ end
675
+
676
+ class TransitionCollectionWithMixedActionsTest < Test::Unit::TestCase
677
+ def setup
678
+ @klass = Class.new do
679
+ def save
680
+ true
681
+ end
682
+ end
683
+
684
+ @state = StateMachine::Machine.new(@klass, :initial => :parked, :action => :save)
685
+ @state.state :idling
686
+ @state.event :ignite
687
+
688
+ @status = StateMachine::Machine.new(@klass, :status, :initial => :first_gear)
689
+ @status.state :second_gear
690
+ @status.event :shift_up
691
+
692
+ @object = @klass.new
693
+
694
+ @transitions = StateMachine::TransitionCollection.new([
695
+ @state_transition = StateMachine::Transition.new(@object, @state, :ignite, :parked, :idling),
696
+ @status_transition = StateMachine::Transition.new(@object, @status, :shift_up, :first_gear, :second_gear)
697
+ ])
698
+ @result = @transitions.perform
699
+ end
700
+
701
+ def test_should_succeed
702
+ assert_equal true, @result
703
+ end
704
+
705
+ def test_should_persist_states
706
+ assert_equal 'idling', @object.state
707
+ assert_equal 'second_gear', @object.status
708
+ end
709
+
710
+ def test_should_store_results_in_transitions
711
+ assert_equal true, @state_transition.result
712
+ assert_nil @status_transition.result
713
+ end
714
+ end
715
+
716
+ class TransitionCollectionWithBlockTest < Test::Unit::TestCase
717
+ def setup
718
+ @klass = Class.new do
719
+ attr_reader :actions
720
+
721
+ def save
722
+ (@actions ||= []) << :save
723
+ end
724
+ end
725
+
726
+ @state = StateMachine::Machine.new(@klass, :state, :initial => :parked, :action => :save)
727
+ @state.state :idling
728
+ @state.event :ignite
729
+
730
+ @status = StateMachine::Machine.new(@klass, :status, :initial => :first_gear, :action => :save)
731
+ @status.state :second_gear
732
+ @status.event :shift_up
733
+
734
+ @object = @klass.new
735
+ @transitions = StateMachine::TransitionCollection.new([
736
+ @state_transition = StateMachine::Transition.new(@object, @state, :ignite, :parked, :idling),
737
+ @status_transition = StateMachine::Transition.new(@object, @status, :shift_up, :first_gear, :second_gear)
738
+ ])
739
+ @result = @transitions.perform { 1 }
740
+ end
741
+
742
+ def test_should_succeed
743
+ assert_equal 1, @result
744
+ end
745
+
746
+ def test_should_persist_states
747
+ assert_equal 'idling', @object.state
748
+ assert_equal 'second_gear', @object.status
749
+ end
750
+
751
+ def test_should_not_run_machine_actions
752
+ assert_nil @object.actions
753
+ end
754
+
755
+ def test_should_use_result_as_transition_result
756
+ assert_equal 1, @state_transition.result
757
+ assert_equal 1, @status_transition.result
758
+ end
759
+ end
760
+
761
+ class TransitionCollectionWithActionFailedTest < Test::Unit::TestCase
762
+ def setup
763
+ @klass = Class.new do
764
+ def save
765
+ false
766
+ end
767
+ end
768
+ @before_count = 0
769
+ @around_before_count = 0
770
+ @after_count = 0
771
+ @around_after_count = 0
772
+
773
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked, :action => :save)
774
+ @machine.state :idling
775
+ @machine.event :ignite
776
+
777
+ @machine.before_transition {@before_count += 1}
778
+ @machine.after_transition {@after_count += 1}
779
+ @machine.after_transition(:include_failures => true) {@after_count += 1}
780
+ @machine.around_transition {|block| @around_before_count += 1; block.call; @around_after_count += 1}
781
+ @machine.around_transition(:include_failures => true) {|block| @around_before_count += 1; block.call; @around_after_count += 1}
782
+
783
+ @object = @klass.new
784
+
785
+ @transitions = StateMachine::TransitionCollection.new([
786
+ StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
787
+ ])
788
+ @result = @transitions.perform
789
+ end
790
+
791
+ def test_should_not_succeed
792
+ assert_equal false, @result
793
+ end
794
+
795
+ def test_should_not_persist_state
796
+ assert_equal 'parked', @object.state
797
+ end
798
+
799
+ def test_should_run_before_callbacks
800
+ assert_equal 1, @before_count
801
+ end
802
+
803
+ def test_should_run_around_callbacks_before_yield
804
+ assert_equal 2, @around_before_count
805
+ end
806
+
807
+ def test_should_only_run_after_callbacks_that_include_failures
808
+ assert_equal 1, @after_count
809
+ end
810
+
811
+ def test_should_only_run_around_callbacks_after_yield_that_include_failures
812
+ assert_equal 1, @around_after_count
813
+ end
814
+ end
815
+
816
+ class TransitionCollectionWithActionErrorTest < Test::Unit::TestCase
817
+ def setup
818
+ @klass = Class.new do
819
+ def save
820
+ raise ArgumentError
821
+ end
822
+ end
823
+ @before_count = 0
824
+ @around_before_count = 0
825
+ @after_count = 0
826
+ @around_after_count = 0
827
+
828
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked, :action => :save)
829
+ @machine.state :idling
830
+ @machine.event :ignite
831
+
832
+ @machine.before_transition {@before_count += 1}
833
+ @machine.after_transition {@after_count += 1}
834
+ @machine.after_transition(:include_failures => true) {@after_count += 1}
835
+ @machine.around_transition {|block| @around_before_count += 1; block.call; @around_after_count += 1}
836
+ @machine.around_transition(:include_failures => true) {|block| @around_before_count += 1; block.call; @around_after_count += 1}
837
+
838
+ @object = @klass.new
839
+
840
+ @transitions = StateMachine::TransitionCollection.new([
841
+ StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
842
+ ])
843
+
844
+ @raised = true
845
+ begin
846
+ @transitions.perform
847
+ @raised = false
848
+ rescue ArgumentError
849
+ end
850
+ end
851
+
852
+ def test_should_not_catch_exception
853
+ assert @raised
854
+ end
855
+
856
+ def test_should_not_persist_state
857
+ assert_equal 'parked', @object.state
858
+ end
859
+
860
+ def test_should_run_before_callbacks
861
+ assert_equal 1, @before_count
862
+ end
863
+
864
+ def test_should_run_around_callbacks_before_yield
865
+ assert_equal 2, @around_before_count
866
+ end
867
+
868
+ def test_should_not_run_after_callbacks
869
+ assert_equal 0, @after_count
870
+ end
871
+
872
+ def test_should_not_run_around_callbacks_after_yield
873
+ assert_equal 0, @around_after_count
874
+ end
875
+ end
876
+
877
+ class TransitionCollectionWithCallbacksTest < Test::Unit::TestCase
878
+ def setup
879
+ @klass = Class.new do
880
+ attr_reader :saved
881
+
882
+ def save
883
+ @saved = true
884
+ end
885
+ end
886
+
887
+ @before_callbacks = []
888
+ @after_callbacks = []
889
+
890
+ @state = StateMachine::Machine.new(@klass, :initial => :parked, :action => :save)
891
+ @state.state :idling
892
+ @state.event :ignite
893
+ @state.before_transition {@before_callbacks << :state_before}
894
+ @state.after_transition(:include_failures => true) {@after_callbacks << :state_after}
895
+ @state.around_transition(:include_failures => true) {|block| @before_callbacks << :state_around; block.call; @after_callbacks << :state_around}
896
+
897
+ @status = StateMachine::Machine.new(@klass, :status, :initial => :first_gear, :action => :save)
898
+ @status.state :second_gear
899
+ @status.event :shift_up
900
+ @status.before_transition {@before_callbacks << :status_before}
901
+ @status.after_transition(:include_failures => true) {@after_callbacks << :status_after}
902
+ @status.around_transition(:include_failures => true) {|block| @before_callbacks << :status_around; block.call; @after_callbacks << :status_around}
903
+
904
+ @object = @klass.new
905
+ @transitions = StateMachine::TransitionCollection.new([
906
+ StateMachine::Transition.new(@object, @state, :ignite, :parked, :idling),
907
+ StateMachine::Transition.new(@object, @status, :shift_up, :first_gear, :second_gear)
908
+ ])
909
+ end
910
+
911
+ def test_should_run_before_callbacks_in_order
912
+ @transitions.perform
913
+ assert_equal [:state_before, :state_around, :status_before, :status_around], @before_callbacks
914
+ end
915
+
916
+ def test_should_halt_if_before_callback_halted_for_first_transition
917
+ @state.before_transition {throw :halt}
918
+
919
+ assert_equal false, @transitions.perform
920
+ assert_equal [:state_before, :state_around], @before_callbacks
921
+ end
922
+
923
+ def test_should_halt_if_before_callback_halted_for_second_transition
924
+ @status.before_transition {throw :halt}
925
+
926
+ assert_equal false, @transitions.perform
927
+ assert_equal [:state_before, :state_around, :status_before, :status_around], @before_callbacks
928
+ end
929
+
930
+ def test_should_halt_if_around_callback_halted_before_yield_for_first_transition
931
+ @state.around_transition {throw :halt}
932
+
933
+ assert_equal false, @transitions.perform
934
+ assert_equal [:state_before, :state_around], @before_callbacks
935
+ end
936
+
937
+ def test_should_halt_if_around_callback_halted_before_yield_for_second_transition
938
+ @status.around_transition {throw :halt}
939
+
940
+ assert_equal false, @transitions.perform
941
+ assert_equal [:state_before, :state_around, :status_before, :status_around], @before_callbacks
942
+ end
943
+
944
+ def test_should_run_after_callbacks_in_reverse_order
945
+ @transitions.perform
946
+ assert_equal [:status_around, :status_after, :state_around, :state_after], @after_callbacks
947
+ end
948
+
949
+ def test_should_not_halt_if_after_callback_halted_for_first_transition
950
+ @state.after_transition(:include_failures => true) {throw :halt}
951
+
952
+ assert_equal true, @transitions.perform
953
+ assert_equal [:status_around, :status_after, :state_around, :state_after], @after_callbacks
954
+ end
955
+
956
+ def test_should_not_halt_if_around_callback_halted_for_second_transition
957
+ @status.around_transition(:include_failures => true) {|block| block.call; throw :halt}
958
+
959
+ assert_equal true, @transitions.perform
960
+ assert_equal [:state_around, :state_after], @after_callbacks
961
+ end
962
+
963
+ def test_should_run_before_callbacks_before_persisting_the_state
964
+ @state.before_transition {|object| @before_state = object.state}
965
+ @state.around_transition {|object, transition, block| @around_state = object.state; block.call}
966
+ @transitions.perform
967
+
968
+ assert_equal 'parked', @before_state
969
+ assert_equal 'parked', @around_state
970
+ end
971
+
972
+ def test_should_persist_state_before_running_action
973
+ @klass.class_eval do
974
+ attr_reader :saved_on_persist
975
+
976
+ def state=(value)
977
+ @state = value
978
+ @saved_on_persist = @saved
979
+ end
980
+ end
981
+
982
+ @transitions.perform
983
+ assert !@object.saved_on_persist
984
+ end
985
+
986
+ def test_should_persist_state_before_running_action_block
987
+ @klass.class_eval do
988
+ attr_writer :saved
989
+ attr_reader :saved_on_persist
990
+
991
+ def state=(value)
992
+ @state = value
993
+ @saved_on_persist = @saved
994
+ end
995
+ end
996
+
997
+ @transitions.perform { @object.saved = true }
998
+ assert !@object.saved_on_persist
999
+ end
1000
+
1001
+ def test_should_run_after_callbacks_after_running_the_action
1002
+ @state.after_transition {|object| @after_saved = object.saved}
1003
+ @state.around_transition {|object, transition, block| block.call; @around_saved = object.saved}
1004
+ @transitions.perform
1005
+
1006
+ assert @after_saved
1007
+ assert @around_saved
1008
+ end
1009
+ end
1010
+
1011
+ class TransitionCollectionWithBeforeCallbackHaltTest < Test::Unit::TestCase
1012
+ def setup
1013
+ @klass = Class.new do
1014
+ attr_reader :saved
1015
+
1016
+ def save
1017
+ @saved = true
1018
+ end
1019
+ end
1020
+ @before_count = 0
1021
+ @after_count = 0
1022
+
1023
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked, :action => :save)
1024
+ @machine.state :idling
1025
+ @machine.event :ignite
1026
+
1027
+ @machine.before_transition {@before_count += 1; throw :halt}
1028
+ @machine.before_transition {@before_count += 1}
1029
+ @machine.after_transition {@after_count += 1}
1030
+ @machine.around_transition {|block| @before_count += 1; block.call; @after_count += 1}
1031
+
1032
+ @object = @klass.new
1033
+
1034
+ @transitions = StateMachine::TransitionCollection.new([
1035
+ StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
1036
+ ])
1037
+ @result = @transitions.perform
1038
+ end
1039
+
1040
+ def test_should_not_succeed
1041
+ assert_equal false, @result
1042
+ end
1043
+
1044
+ def test_should_not_persist_state
1045
+ assert_equal 'parked', @object.state
1046
+ end
1047
+
1048
+ def test_should_not_run_action
1049
+ assert !@object.saved
1050
+ end
1051
+
1052
+ def test_should_not_run_further_before_callbacks
1053
+ assert_equal 1, @before_count
1054
+ end
1055
+
1056
+ def test_should_not_run_after_callbacks
1057
+ assert_equal 0, @after_count
1058
+ end
1059
+ end
1060
+
1061
+ class TransitionCollectionWithAfterCallbackHaltTest < Test::Unit::TestCase
1062
+ def setup
1063
+ @klass = Class.new do
1064
+ attr_reader :saved
1065
+
1066
+ def save
1067
+ @saved = true
1068
+ end
1069
+ end
1070
+ @before_count = 0
1071
+ @after_count = 0
1072
+
1073
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked, :action => :save)
1074
+ @machine.state :idling
1075
+ @machine.event :ignite
1076
+
1077
+ @machine.before_transition {@before_count += 1}
1078
+ @machine.after_transition {@after_count += 1; throw :halt}
1079
+ @machine.after_transition {@after_count += 1}
1080
+ @machine.around_transition {|block| @before_count += 1; block.call; @after_count += 1}
1081
+
1082
+ @object = @klass.new
1083
+
1084
+ @transitions = StateMachine::TransitionCollection.new([
1085
+ StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
1086
+ ])
1087
+ @result = @transitions.perform
1088
+ end
1089
+
1090
+ def test_should_succeed
1091
+ assert_equal true, @result
1092
+ end
1093
+
1094
+ def test_should_persist_state
1095
+ assert_equal 'idling', @object.state
1096
+ end
1097
+
1098
+ def test_should_run_before_callbacks
1099
+ assert_equal 2, @before_count
1100
+ end
1101
+
1102
+ def test_should_not_run_further_after_callbacks
1103
+ assert_equal 2, @after_count
1104
+ end
1105
+ end
1106
+
1107
+ class TransitionCollectionWithSkippedAfterCallbacksTest < Test::Unit::TestCase
1108
+ def setup
1109
+ @klass = Class.new
1110
+
1111
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
1112
+ @machine.state :idling
1113
+ @machine.event :ignite
1114
+ @machine.after_transition {@ran_after = true}
1115
+ @machine.around_transition {|block| @ran_around_before = true; block.call; @ran_around_after = true}
1116
+
1117
+ @object = @klass.new
1118
+
1119
+ @transitions = StateMachine::TransitionCollection.new([
1120
+ @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
1121
+ ], :after => false)
1122
+ @result = @transitions.perform
1123
+ end
1124
+
1125
+ def test_should_succeed
1126
+ assert_equal true, @result
1127
+ end
1128
+
1129
+ def test_should_not_run_after_callbacks
1130
+ assert !@ran_after
1131
+ end
1132
+
1133
+ def test_should_not_run_around_callbacks_after_yield
1134
+ assert !@ran_around_after
1135
+ end
1136
+
1137
+ def test_should_run_after_callbacks_on_subsequent_perform
1138
+ StateMachine::TransitionCollection.new([@transition]).perform
1139
+ assert @ran_after
1140
+ end
1141
+
1142
+ def test_should_run_around_callbacks_after_yield_on_subsequent_perform
1143
+ StateMachine::TransitionCollection.new([@transition]).perform
1144
+ assert @ran_around_after
1145
+ end
1146
+
1147
+ def test_should_not_rerun_around_callbacks_before_yield_on_subsequent_perform
1148
+ @ran_around_before = false
1149
+ StateMachine::TransitionCollection.new([@transition]).perform
1150
+
1151
+ assert !@ran_around_before
1152
+ end
1153
+ end
1154
+
1155
+ class TransitionCollectionWithActionHelperBaseTest < Test::Unit::TestCase
1156
+ def setup
1157
+ @superclass = Class.new do
1158
+ def save
1159
+ true
1160
+ end
1161
+ end
1162
+
1163
+ @klass = Class.new(@superclass) do
1164
+ attr_reader :saved, :state_on_save, :state_event_on_save, :state_event_transition_on_save
1165
+
1166
+ def save
1167
+ @saved = true
1168
+ @state_on_save = state
1169
+ @state_event_on_save = state_event
1170
+ @state_event_transition_on_save = state_event_transition
1171
+ super
1172
+ end
1173
+ end
1174
+
1175
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked, :action => :save)
1176
+ @machine.state :idling
1177
+ @machine.event :ignite
1178
+
1179
+ @object = @klass.new
1180
+
1181
+ @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
1182
+ end
1183
+
1184
+ def default_test
1185
+ end
1186
+ end
1187
+
1188
+ class TransitionCollectionWithActionHelperAndSkippedActionTest < TransitionCollectionWithActionHelperBaseTest
1189
+ def setup
1190
+ super
1191
+ @result = StateMachine::TransitionCollection.new([@transition], :actions => false).perform
1192
+ end
1193
+
1194
+ def test_should_succeed
1195
+ assert_equal true, @result
1196
+ end
1197
+
1198
+ def test_should_not_run_action
1199
+ assert !@object.saved
1200
+ end
1201
+ end
1202
+
1203
+ class TransitionCollectionWithActionHelperAndSkippedAfterCallbacksTest < TransitionCollectionWithActionHelperBaseTest
1204
+ def setup
1205
+ super
1206
+ @result = StateMachine::TransitionCollection.new([@transition], :after => false).perform
1207
+ end
1208
+
1209
+ def test_should_succeed
1210
+ assert_equal true, @result
1211
+ end
1212
+
1213
+ def test_should_run_action
1214
+ assert @object.saved
1215
+ end
1216
+
1217
+ def test_should_have_already_persisted_when_running_action
1218
+ assert_equal 'idling', @object.state_on_save
1219
+ end
1220
+
1221
+ def test_should_not_have_event_during_action
1222
+ assert_nil @object.state_event_on_save
1223
+ end
1224
+
1225
+ def test_should_not_write_event
1226
+ assert_nil @object.state_event
1227
+ end
1228
+
1229
+ def test_should_not_have_event_transition_during_save
1230
+ assert_nil @object.state_event_transition_on_save
1231
+ end
1232
+
1233
+ def test_should_not_write_event_attribute
1234
+ assert_nil @object.send(:state_event_transition)
1235
+ end
1236
+ end
1237
+
1238
+ class TransitionCollectionWithActionHelperAndBlockTest < TransitionCollectionWithActionHelperBaseTest
1239
+ def setup
1240
+ super
1241
+ @result = StateMachine::TransitionCollection.new([@transition]).perform { true }
1242
+ end
1243
+
1244
+ def test_should_succeed
1245
+ assert_equal true, @result
1246
+ end
1247
+
1248
+ def test_should_not_run_action
1249
+ assert !@object.saved
1250
+ end
1251
+ end
1252
+
1253
+ class TransitionCollectionWithActionHelperInvalidTest < TransitionCollectionWithActionHelperBaseTest
1254
+ def setup
1255
+ super
1256
+ @result = StateMachine::TransitionCollection.new([@transition, nil]).perform
1257
+ end
1258
+
1259
+ def test_should_not_succeed
1260
+ assert_equal false, @result
1261
+ end
1262
+
1263
+ def test_should_not_run_action
1264
+ assert !@object.saved
1265
+ end
1266
+ end
1267
+
1268
+ class TransitionCollectionWithActionHelperWithNilActionTest < TransitionCollectionWithActionHelperBaseTest
1269
+ def setup
1270
+ super
1271
+
1272
+ @machine = StateMachine::Machine.new(@klass, :status, :initial => :parked)
1273
+ @machine.state :idling
1274
+ @machine.event :ignite
1275
+
1276
+ @result = StateMachine::TransitionCollection.new([@transition, StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)]).perform
1277
+ end
1278
+
1279
+ def test_should_succeed
1280
+ assert_equal true, @result
1281
+ end
1282
+
1283
+ def test_should_run_action
1284
+ assert @object.saved
1285
+ end
1286
+
1287
+ def test_should_have_already_persisted_when_running_action
1288
+ assert_equal 'idling', @object.state_on_save
1289
+ end
1290
+
1291
+ def test_should_not_have_event_during_action
1292
+ assert_nil @object.state_event_on_save
1293
+ end
1294
+
1295
+ def test_should_not_write_event
1296
+ assert_nil @object.state_event
1297
+ end
1298
+
1299
+ def test_should_not_have_event_transition_during_save
1300
+ assert_nil @object.state_event_transition_on_save
1301
+ end
1302
+
1303
+ def test_should_not_write_event_attribute
1304
+ assert_nil @object.send(:state_event_transition)
1305
+ end
1306
+ end
1307
+
1308
+ class TransitionCollectionWithActionHelperWithDifferentActionsTest < TransitionCollectionWithActionHelperBaseTest
1309
+ def setup
1310
+ super
1311
+
1312
+ @klass.class_eval do
1313
+ def save_status
1314
+ true
1315
+ end
1316
+ end
1317
+
1318
+ @machine = StateMachine::Machine.new(@klass, :status, :initial => :parked, :action => :save_status)
1319
+ @machine.state :idling
1320
+ @machine.event :ignite
1321
+
1322
+ @result = StateMachine::TransitionCollection.new([@transition, StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)]).perform
1323
+ end
1324
+
1325
+ def test_should_succeed
1326
+ assert_equal true, @result
1327
+ end
1328
+
1329
+ def test_should_run_action
1330
+ assert @object.saved
1331
+ end
1332
+
1333
+ def test_should_have_already_persisted_when_running_action
1334
+ assert_equal 'idling', @object.state_on_save
1335
+ end
1336
+
1337
+ def test_should_not_have_event_during_action
1338
+ assert_nil @object.state_event_on_save
1339
+ end
1340
+
1341
+ def test_should_not_write_event
1342
+ assert_nil @object.state_event
1343
+ end
1344
+
1345
+ def test_should_not_have_event_transition_during_save
1346
+ assert_nil @object.state_event_transition_on_save
1347
+ end
1348
+
1349
+ def test_should_not_write_event_attribute
1350
+ assert_nil @object.send(:state_event_transition)
1351
+ end
1352
+ end
1353
+
1354
+ class TransitionCollectionWithActionHelperTest < TransitionCollectionWithActionHelperBaseTest
1355
+ def setup
1356
+ super
1357
+ @result = StateMachine::TransitionCollection.new([@transition]).perform
1358
+ end
1359
+
1360
+ def test_should_succeed
1361
+ assert_equal true, @result
1362
+ end
1363
+
1364
+ def test_should_run_action
1365
+ assert @object.saved
1366
+ end
1367
+
1368
+ def test_should_not_have_already_persisted_when_running_action
1369
+ assert_equal 'parked', @object.state_on_save
1370
+ end
1371
+
1372
+ def test_should_persist
1373
+ assert_equal 'idling', @object.state
1374
+ end
1375
+
1376
+ def test_should_not_have_event_during_action
1377
+ assert_nil @object.state_event_on_save
1378
+ end
1379
+
1380
+ def test_should_not_write_event
1381
+ assert_nil @object.state_event
1382
+ end
1383
+
1384
+ def test_should_have_event_transition_during_action
1385
+ assert_equal @transition, @object.state_event_transition_on_save
1386
+ end
1387
+
1388
+ def test_should_not_write_event_transition
1389
+ assert_nil @object.send(:state_event_transition)
1390
+ end
1391
+
1392
+ def test_should_mark_event_transition_as_transient
1393
+ assert @transition.transient?
1394
+ end
1395
+ end
1396
+
1397
+ class TransitionCollectionWithActionHelperMultipleTest < TransitionCollectionWithActionHelperBaseTest
1398
+ def setup
1399
+ super
1400
+
1401
+ @status_machine = StateMachine::Machine.new(@klass, :status, :initial => :first_gear, :action => :save)
1402
+ @status_machine.state :second_gear
1403
+ @status_machine.event :shift_up
1404
+
1405
+ @klass.class_eval do
1406
+ attr_reader :status_on_save, :status_event_on_save, :status_event_transition_on_save
1407
+
1408
+ def save
1409
+ @saved = true
1410
+ @state_on_save = state
1411
+ @state_event_on_save = state_event
1412
+ @state_event_transition_on_save = state_event_transition
1413
+ @status_on_save = status
1414
+ @status_event_on_save = status_event
1415
+ @status_event_transition_on_save = status_event_transition
1416
+ super
1417
+ 1
1418
+ end
1419
+ end
1420
+
1421
+ @object = @klass.new
1422
+ @state_transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
1423
+ @status_transition = StateMachine::Transition.new(@object, @status_machine, :shift_up, :first_gear, :second_gear)
1424
+
1425
+ @result = StateMachine::TransitionCollection.new([@state_transition, @status_transition]).perform
1426
+ end
1427
+
1428
+ def test_should_succeed
1429
+ assert_equal 1, @result
1430
+ end
1431
+
1432
+ def test_should_run_action
1433
+ assert @object.saved
1434
+ end
1435
+
1436
+ def test_should_not_have_already_persisted_when_running_action
1437
+ assert_equal 'parked', @object.state_on_save
1438
+ assert_equal 'first_gear', @object.status_on_save
1439
+ end
1440
+
1441
+ def test_should_persist
1442
+ assert_equal 'idling', @object.state
1443
+ assert_equal 'second_gear', @object.status
1444
+ end
1445
+
1446
+ def test_should_not_have_events_during_action
1447
+ assert_nil @object.state_event_on_save
1448
+ assert_nil @object.status_event_on_save
1449
+ end
1450
+
1451
+ def test_should_not_write_events
1452
+ assert_nil @object.state_event
1453
+ assert_nil @object.status_event
1454
+ end
1455
+
1456
+ def test_should_have_event_transitions_during_action
1457
+ assert_equal @state_transition, @object.state_event_transition_on_save
1458
+ assert_equal @status_transition, @object.status_event_transition_on_save
1459
+ end
1460
+
1461
+ def test_should_not_write_event_transitions
1462
+ assert_nil @object.send(:state_event_transition)
1463
+ assert_nil @object.send(:status_event_transition)
1464
+ end
1465
+
1466
+ def test_should_mark_event_transitions_as_transient
1467
+ assert @state_transition.transient?
1468
+ assert @status_transition.transient?
1469
+ end
1470
+ end
1471
+
1472
+ class TransitionCollectionWithActionHelperErrorTest < TransitionCollectionWithActionHelperBaseTest
1473
+ def setup
1474
+ super
1475
+
1476
+ @superclass.class_eval do
1477
+ def save
1478
+ raise ArgumentError
1479
+ end
1480
+ end
1481
+
1482
+ begin; StateMachine::TransitionCollection.new([@transition]).perform; rescue; end
1483
+ end
1484
+
1485
+ def test_should_not_write_event
1486
+ assert_nil @object.state_event
1487
+ end
1488
+
1489
+ def test_should_not_write_event_transition
1490
+ assert_nil @object.send(:state_event_transition)
1491
+ end
1492
+ end
1493
+
1494
+ class AttributeTransitionCollectionByDefaultTest < Test::Unit::TestCase
1495
+ def setup
1496
+ @transitions = StateMachine::AttributeTransitionCollection.new
1497
+ end
1498
+
1499
+ def test_should_skip_actions
1500
+ assert @transitions.skip_actions
1501
+ end
1502
+
1503
+ def test_should_not_skip_after
1504
+ assert !@transitions.skip_after
1505
+ end
1506
+
1507
+ def test_should_not_use_transaction
1508
+ assert !@transitions.use_transaction
1509
+ end
1510
+
1511
+ def test_should_be_empty
1512
+ assert @transitions.empty?
1513
+ end
1514
+ end
1515
+
1516
+ class AttributeTransitionCollectionWithEventsTest < Test::Unit::TestCase
1517
+ def setup
1518
+ @klass = Class.new
1519
+
1520
+ @state = StateMachine::Machine.new(@klass, :initial => :parked, :action => :save)
1521
+ @state.state :idling
1522
+ @state.event :ignite
1523
+
1524
+ @status = StateMachine::Machine.new(@klass, :status, :initial => :first_gear, :action => :save)
1525
+ @status.state :second_gear
1526
+ @status.event :shift_up
1527
+
1528
+ @object = @klass.new
1529
+ @object.state_event = 'ignite'
1530
+ @object.status_event = 'shift_up'
1531
+
1532
+ @transitions = StateMachine::AttributeTransitionCollection.new([
1533
+ @state_transition = StateMachine::Transition.new(@object, @state, :ignite, :parked, :idling),
1534
+ @status_transition = StateMachine::Transition.new(@object, @status, :shift_up, :first_gear, :second_gear)
1535
+ ])
1536
+ @result = @transitions.perform
1537
+ end
1538
+
1539
+ def test_should_succeed
1540
+ assert_equal true, @result
1541
+ end
1542
+
1543
+ def test_should_persist_states
1544
+ assert_equal 'idling', @object.state
1545
+ assert_equal 'second_gear', @object.status
1546
+ end
1547
+
1548
+ def test_should_clear_events
1549
+ assert_nil @object.state_event
1550
+ assert_nil @object.status_event
1551
+ end
1552
+
1553
+ def test_should_not_write_event_transitions
1554
+ assert_nil @object.send(:state_event_transition)
1555
+ assert_nil @object.send(:status_event_transition)
1556
+ end
1557
+ end
1558
+
1559
+ class AttributeTransitionCollectionWithEventTransitionsTest < Test::Unit::TestCase
1560
+ def setup
1561
+ @klass = Class.new
1562
+
1563
+ @state = StateMachine::Machine.new(@klass, :initial => :parked, :action => :save)
1564
+ @state.state :idling
1565
+ @state.event :ignite
1566
+
1567
+ @status = StateMachine::Machine.new(@klass, :status, :initial => :first_gear, :action => :save)
1568
+ @status.state :second_gear
1569
+ @status.event :shift_up
1570
+
1571
+ @object = @klass.new
1572
+ @object.send(:state_event_transition=, @state_transition = StateMachine::Transition.new(@object, @state, :ignite, :parked, :idling))
1573
+ @object.send(:status_event_transition=, @status_transition = StateMachine::Transition.new(@object, @status, :shift_up, :first_gear, :second_gear))
1574
+
1575
+ @transitions = StateMachine::AttributeTransitionCollection.new([@state_transition, @status_transition])
1576
+ @result = @transitions.perform
1577
+ end
1578
+
1579
+ def test_should_succeed
1580
+ assert_equal true, @result
1581
+ end
1582
+
1583
+ def test_should_persist_states
1584
+ assert_equal 'idling', @object.state
1585
+ assert_equal 'second_gear', @object.status
1586
+ end
1587
+
1588
+ def test_should_not_write_events
1589
+ assert_nil @object.state_event
1590
+ assert_nil @object.status_event
1591
+ end
1592
+
1593
+ def test_should_clear_event_transitions
1594
+ assert_nil @object.send(:state_event_transition)
1595
+ assert_nil @object.send(:status_event_transition)
1596
+ end
1597
+ end
1598
+
1599
+ class AttributeTransitionCollectionWithActionFailedTest < Test::Unit::TestCase
1600
+ def setup
1601
+ @klass = Class.new
1602
+
1603
+ @state = StateMachine::Machine.new(@klass, :initial => :parked, :action => :save)
1604
+ @state.state :idling
1605
+ @state.event :ignite
1606
+
1607
+ @status = StateMachine::Machine.new(@klass, :status, :initial => :first_gear, :action => :save)
1608
+ @status.state :second_gear
1609
+ @status.event :shift_up
1610
+
1611
+ @object = @klass.new
1612
+ @object.state_event = 'ignite'
1613
+ @object.status_event = 'shift_up'
1614
+
1615
+ @transitions = StateMachine::AttributeTransitionCollection.new([
1616
+ @state_transition = StateMachine::Transition.new(@object, @state, :ignite, :parked, :idling),
1617
+ @status_transition = StateMachine::Transition.new(@object, @status, :shift_up, :first_gear, :second_gear)
1618
+ ])
1619
+ @result = @transitions.perform { false }
1620
+ end
1621
+
1622
+ def test_should_not_succeed
1623
+ assert_equal false, @result
1624
+ end
1625
+
1626
+ def test_should_not_persist_states
1627
+ assert_equal 'parked', @object.state
1628
+ assert_equal 'first_gear', @object.status
1629
+ end
1630
+
1631
+ def test_should_not_clear_events
1632
+ assert_equal :ignite, @object.state_event
1633
+ assert_equal :shift_up, @object.status_event
1634
+ end
1635
+
1636
+ def test_should_not_write_event_transitions
1637
+ assert_nil @object.send(:state_event_transition)
1638
+ assert_nil @object.send(:status_event_transition)
1639
+ end
1640
+ end
1641
+
1642
+ class AttributeTransitionCollectionWithActionErrorTest < Test::Unit::TestCase
1643
+ def setup
1644
+ @klass = Class.new
1645
+
1646
+ @state = StateMachine::Machine.new(@klass, :initial => :parked, :action => :save)
1647
+ @state.state :idling
1648
+ @state.event :ignite
1649
+
1650
+ @status = StateMachine::Machine.new(@klass, :status, :initial => :first_gear, :action => :save)
1651
+ @status.state :second_gear
1652
+ @status.event :shift_up
1653
+
1654
+ @object = @klass.new
1655
+ @object.state_event = 'ignite'
1656
+ @object.status_event = 'shift_up'
1657
+
1658
+ @transitions = StateMachine::AttributeTransitionCollection.new([
1659
+ @state_transition = StateMachine::Transition.new(@object, @state, :ignite, :parked, :idling),
1660
+ @status_transition = StateMachine::Transition.new(@object, @status, :shift_up, :first_gear, :second_gear)
1661
+ ])
1662
+
1663
+ begin; @transitions.perform { raise ArgumentError }; rescue; end
1664
+ end
1665
+
1666
+ def test_should_not_persist_states
1667
+ assert_equal 'parked', @object.state
1668
+ assert_equal 'first_gear', @object.status
1669
+ end
1670
+
1671
+ def test_should_not_clear_events
1672
+ assert_equal :ignite, @object.state_event
1673
+ assert_equal :shift_up, @object.status_event
1674
+ end
1675
+
1676
+ def test_should_not_write_event_transitions
1677
+ assert_nil @object.send(:state_event_transition)
1678
+ assert_nil @object.send(:status_event_transition)
1679
+ end
1680
+ end
1681
+
1682
+ class AttributeTransitionCollectionWithCallbacksTest < Test::Unit::TestCase
1683
+ def setup
1684
+ @klass = Class.new
1685
+
1686
+ @state = StateMachine::Machine.new(@klass, :initial => :parked, :action => :save)
1687
+ @state.state :idling
1688
+ @state.event :ignite
1689
+
1690
+ @status = StateMachine::Machine.new(@klass, :status, :initial => :first_gear, :action => :save)
1691
+ @status.state :second_gear
1692
+ @status.event :shift_up
1693
+
1694
+ @object = @klass.new
1695
+
1696
+ @transitions = StateMachine::AttributeTransitionCollection.new([
1697
+ @state_transition = StateMachine::Transition.new(@object, @state, :ignite, :parked, :idling),
1698
+ @status_transition = StateMachine::Transition.new(@object, @status, :shift_up, :first_gear, :second_gear)
1699
+ ])
1700
+ end
1701
+
1702
+ def test_should_not_have_events_during_before_callbacks
1703
+ @state.before_transition {|object, transition| @before_state_event = object.state_event }
1704
+ @state.around_transition {|object, transition, block| @around_state_event = object.state_event; block.call }
1705
+ @transitions.perform
1706
+
1707
+ assert_nil @before_state_event
1708
+ assert_nil @around_state_event
1709
+ end
1710
+
1711
+ def test_should_not_have_events_during_action
1712
+ @transitions.perform { @state_event = @object.state_event }
1713
+
1714
+ assert_nil @state_event
1715
+ end
1716
+
1717
+ def test_should_not_have_events_during_after_callbacks
1718
+ @state.after_transition {|object, transition| @after_state_event = object.state_event }
1719
+ @state.around_transition {|object, transition, block| block.call; @around_state_event = object.state_event }
1720
+ @transitions.perform
1721
+
1722
+ assert_nil @state_event
1723
+ end
1724
+
1725
+ def test_should_not_have_event_transitions_during_before_callbacks
1726
+ @state.before_transition {|object, transition| @state_event_transition = object.send(:state_event_transition) }
1727
+ @transitions.perform
1728
+
1729
+ assert_nil @state_event_transition
1730
+ end
1731
+
1732
+ def test_should_not_have_event_transitions_during_action
1733
+ @transitions.perform { @state_event_transition = @object.send(:state_event_transition) }
1734
+
1735
+ assert_nil @state_event_transition
1736
+ end
1737
+
1738
+ def test_should_not_have_event_transitions_during_after_callbacks
1739
+ @state.after_transition {|object, transition| @after_state_event_transition = object.send(:state_event_transition) }
1740
+ @state.around_transition {|object, transition, block| block.call; @around_state_event_transition = object.send(:state_event_transition) }
1741
+ @transitions.perform
1742
+
1743
+ assert_nil @after_state_event_transition
1744
+ assert_nil @around_state_event_transition
1745
+ end
1746
+ end
1747
+
1748
+ class AttributeTransitionCollectionWithBeforeCallbackHaltTest < Test::Unit::TestCase
1749
+ def setup
1750
+ @klass = Class.new
1751
+
1752
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked, :action => :save)
1753
+ @machine.state :idling
1754
+ @machine.event :ignite
1755
+
1756
+ @machine.before_transition {throw :halt}
1757
+
1758
+ @object = @klass.new
1759
+ @object.state_event = 'ignite'
1760
+
1761
+ @transitions = StateMachine::AttributeTransitionCollection.new([
1762
+ StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
1763
+ ])
1764
+ @result = @transitions.perform
1765
+ end
1766
+
1767
+ def test_should_not_succeed
1768
+ assert_equal false, @result
1769
+ end
1770
+
1771
+ def test_should_not_clear_event
1772
+ assert_equal :ignite, @object.state_event
1773
+ end
1774
+
1775
+ def test_should_not_write_event_transition
1776
+ assert_nil @object.send(:state_event_transition)
1777
+ end
1778
+ end
1779
+
1780
+ class AttributeTransitionCollectionWithBeforeCallbackErrorTest < Test::Unit::TestCase
1781
+ def setup
1782
+ @klass = Class.new
1783
+
1784
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked, :action => :save)
1785
+ @machine.state :idling
1786
+ @machine.event :ignite
1787
+
1788
+ @machine.before_transition {raise ArgumentError}
1789
+
1790
+ @object = @klass.new
1791
+ @object.state_event = 'ignite'
1792
+
1793
+ @transitions = StateMachine::AttributeTransitionCollection.new([
1794
+ StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
1795
+ ])
1796
+ begin; @transitions.perform; rescue; end
1797
+ end
1798
+
1799
+ def test_should_not_clear_event
1800
+ assert_equal :ignite, @object.state_event
1801
+ end
1802
+
1803
+ def test_should_not_write_event_transition
1804
+ assert_nil @object.send(:state_event_transition)
1805
+ end
1806
+ end
1807
+
1808
+ class AttributeTransitionCollectionWithAroundCallbackBeforeYieldHaltTest < Test::Unit::TestCase
1809
+ def setup
1810
+ @klass = Class.new
1811
+
1812
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked, :action => :save)
1813
+ @machine.state :idling
1814
+ @machine.event :ignite
1815
+
1816
+ @machine.around_transition {throw :halt}
1817
+
1818
+ @object = @klass.new
1819
+ @object.state_event = 'ignite'
1820
+
1821
+ @transitions = StateMachine::AttributeTransitionCollection.new([
1822
+ StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
1823
+ ])
1824
+ @result = @transitions.perform
1825
+ end
1826
+
1827
+ def test_should_not_succeed
1828
+ assert_equal false, @result
1829
+ end
1830
+
1831
+ def test_should_not_clear_event
1832
+ assert_equal :ignite, @object.state_event
1833
+ end
1834
+
1835
+ def test_should_not_write_event_transition
1836
+ assert_nil @object.send(:state_event_transition)
1837
+ end
1838
+ end
1839
+
1840
+ class AttributeTransitionCollectionWithAroundAfterYieldCallbackErrorTest < Test::Unit::TestCase
1841
+ def setup
1842
+ @klass = Class.new
1843
+
1844
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked, :action => :save)
1845
+ @machine.state :idling
1846
+ @machine.event :ignite
1847
+
1848
+ @machine.before_transition {raise ArgumentError}
1849
+
1850
+ @object = @klass.new
1851
+ @object.state_event = 'ignite'
1852
+
1853
+ @transitions = StateMachine::AttributeTransitionCollection.new([
1854
+ StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
1855
+ ])
1856
+ begin; @transitions.perform; rescue; end
1857
+ end
1858
+
1859
+ def test_should_not_clear_event
1860
+ assert_equal :ignite, @object.state_event
1861
+ end
1862
+
1863
+ def test_should_not_write_event_transition
1864
+ assert_nil @object.send(:state_event_transition)
1865
+ end
1866
+ end
1867
+
1868
+ class AttributeTransitionCollectionWithSkippedAfterCallbacksTest < Test::Unit::TestCase
1869
+ def setup
1870
+ @klass = Class.new
1871
+
1872
+ @state = StateMachine::Machine.new(@klass, :initial => :parked, :action => :save)
1873
+ @state.state :idling
1874
+ @state.event :ignite
1875
+
1876
+ @status = StateMachine::Machine.new(@klass, :status, :initial => :first_gear, :action => :save)
1877
+ @status.state :second_gear
1878
+ @status.event :shift_up
1879
+
1880
+ @object = @klass.new
1881
+ @object.state_event = 'ignite'
1882
+ @object.status_event = 'shift_up'
1883
+
1884
+ @transitions = StateMachine::AttributeTransitionCollection.new([
1885
+ @state_transition = StateMachine::Transition.new(@object, @state, :ignite, :parked, :idling),
1886
+ @status_transition = StateMachine::Transition.new(@object, @status, :shift_up, :first_gear, :second_gear)
1887
+ ], :after => false)
1888
+ end
1889
+
1890
+ def test_should_clear_events
1891
+ @transitions.perform
1892
+ assert_nil @object.state_event
1893
+ assert_nil @object.status_event
1894
+ end
1895
+
1896
+ def test_should_write_event_transitions_if_success
1897
+ @transitions.perform { true }
1898
+ assert_equal @state_transition, @object.send(:state_event_transition)
1899
+ assert_equal @status_transition, @object.send(:status_event_transition)
1900
+ end
1901
+
1902
+ def test_should_not_write_event_transitions_if_failed
1903
+ @transitions.perform { false }
1904
+ assert_nil @object.send(:state_event_transition)
1905
+ assert_nil @object.send(:status_event_transition)
1906
+ end
1907
+ end
1908
+
1909
+ class AttributeTransitionCollectionWithAfterCallbackHaltTest < Test::Unit::TestCase
1910
+ def setup
1911
+ @klass = Class.new
1912
+
1913
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked, :action => :save)
1914
+ @machine.state :idling
1915
+ @machine.event :ignite
1916
+
1917
+ @machine.after_transition {throw :halt}
1918
+
1919
+ @object = @klass.new
1920
+ @object.state_event = 'ignite'
1921
+
1922
+ @transitions = StateMachine::AttributeTransitionCollection.new([
1923
+ StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
1924
+ ])
1925
+ @result = @transitions.perform
1926
+ end
1927
+
1928
+ def test_should_succeed
1929
+ assert_equal true, @result
1930
+ end
1931
+
1932
+ def test_should_clear_event
1933
+ assert_nil @object.state_event
1934
+ end
1935
+
1936
+ def test_should_not_write_event_transition
1937
+ assert_nil @object.send(:state_event_transition)
1938
+ end
1939
+ end
1940
+
1941
+ class AttributeTransitionCollectionWithAfterCallbackErrorTest < Test::Unit::TestCase
1942
+ def setup
1943
+ @klass = Class.new
1944
+
1945
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked, :action => :save)
1946
+ @machine.state :idling
1947
+ @machine.event :ignite
1948
+
1949
+ @machine.after_transition {raise ArgumentError}
1950
+
1951
+ @object = @klass.new
1952
+ @object.state_event = 'ignite'
1953
+
1954
+ @transitions = StateMachine::AttributeTransitionCollection.new([
1955
+ StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
1956
+ ])
1957
+ begin; @transitions.perform; rescue; end
1958
+ end
1959
+
1960
+ def test_should_clear_event
1961
+ assert_nil @object.state_event
1962
+ end
1963
+
1964
+ def test_should_not_write_event_transition
1965
+ assert_nil @object.send(:state_event_transition)
1966
+ end
1967
+ end
1968
+
1969
+ class AttributeTransitionCollectionWithAroundCallbackAfterYieldHaltTest < Test::Unit::TestCase
1970
+ def setup
1971
+ @klass = Class.new
1972
+
1973
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked, :action => :save)
1974
+ @machine.state :idling
1975
+ @machine.event :ignite
1976
+
1977
+ @machine.around_transition {|block| block.call; throw :halt}
1978
+
1979
+ @object = @klass.new
1980
+ @object.state_event = 'ignite'
1981
+
1982
+ @transitions = StateMachine::AttributeTransitionCollection.new([
1983
+ StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
1984
+ ])
1985
+ @result = @transitions.perform
1986
+ end
1987
+
1988
+ def test_should_succeed
1989
+ assert_equal true, @result
1990
+ end
1991
+
1992
+ def test_should_clear_event
1993
+ assert_nil @object.state_event
1994
+ end
1995
+
1996
+ def test_should_not_write_event_transition
1997
+ assert_nil @object.send(:state_event_transition)
1998
+ end
1999
+ end
2000
+
2001
+ class AttributeTransitionCollectionWithAfterCallbackErrorTest < Test::Unit::TestCase
2002
+ def setup
2003
+ @klass = Class.new
2004
+
2005
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked, :action => :save)
2006
+ @machine.state :idling
2007
+ @machine.event :ignite
2008
+
2009
+ @machine.around_transition {|block| block.call; raise ArgumentError}
2010
+
2011
+ @object = @klass.new
2012
+ @object.state_event = 'ignite'
2013
+
2014
+ @transitions = StateMachine::AttributeTransitionCollection.new([
2015
+ StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
2016
+ ])
2017
+ begin; @transitions.perform; rescue; end
2018
+ end
2019
+
2020
+ def test_should_clear_event
2021
+ assert_nil @object.state_event
2022
+ end
2023
+
2024
+ def test_should_not_write_event_transition
2025
+ assert_nil @object.send(:state_event_transition)
2026
+ end
2027
+ end
2028
+
2029
+ class AttributeTransitionCollectionMarshallingTest < Test::Unit::TestCase
2030
+ def setup
2031
+ @klass = Class.new
2032
+ self.class.const_set('Example', @klass)
2033
+
2034
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked, :action => :save)
2035
+ @machine.state :idling
2036
+ @machine.event :ignite
2037
+
2038
+ @object = @klass.new
2039
+ @object.state_event = 'ignite'
2040
+ end
2041
+
2042
+ def test_should_marshal_during_before_callbacks
2043
+ @machine.before_transition {|object, transition| Marshal.dump(object)}
2044
+ assert_nothing_raised do
2045
+ transitions(:after => false).perform { true }
2046
+ transitions.perform { true }
2047
+ end
2048
+ end
2049
+
2050
+ def test_should_marshal_during_action
2051
+ assert_nothing_raised do
2052
+ transitions(:after => false).perform do
2053
+ Marshal.dump(@object)
2054
+ true
2055
+ end
2056
+
2057
+ transitions.perform do
2058
+ Marshal.dump(@object)
2059
+ true
2060
+ end
2061
+ end
2062
+ end
2063
+
2064
+ def test_should_marshal_during_after_callbacks
2065
+ @machine.after_transition {|object, transition| Marshal.dump(object)}
2066
+ assert_nothing_raised do
2067
+ transitions(:after => false).perform { true }
2068
+ transitions.perform { true }
2069
+ end
2070
+ end
2071
+
2072
+ def test_should_marshal_during_around_callbacks_before_yield
2073
+ @machine.around_transition {|object, transition, block| Marshal.dump(object); block.call}
2074
+ assert_nothing_raised do
2075
+ transitions(:after => false).perform { true }
2076
+ transitions.perform { true }
2077
+ end
2078
+ end
2079
+
2080
+ def test_should_marshal_during_around_callbacks_after_yield
2081
+ @machine.around_transition {|object, transition, block| block.call; Marshal.dump(object)}
2082
+ assert_nothing_raised do
2083
+ transitions(:after => false).perform { true }
2084
+ transitions.perform { true }
2085
+ end
2086
+ end
2087
+
2088
+ def teardown
2089
+ self.class.send(:remove_const, 'Example')
2090
+ end
2091
+
2092
+ private
2093
+ def transitions(options = {})
2094
+ StateMachine::AttributeTransitionCollection.new([
2095
+ StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
2096
+ ], options)
2097
+ end
2098
+ end