state_machine 0.8.1 → 0.9.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 (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