state_machine 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. data/CHANGELOG.rdoc +26 -0
  2. data/README.rdoc +254 -46
  3. data/Rakefile +29 -3
  4. data/examples/AutoShop_state.png +0 -0
  5. data/examples/Car_state.jpg +0 -0
  6. data/examples/Vehicle_state.png +0 -0
  7. data/lib/state_machine.rb +161 -116
  8. data/lib/state_machine/assertions.rb +21 -0
  9. data/lib/state_machine/callback.rb +168 -0
  10. data/lib/state_machine/eval_helpers.rb +67 -0
  11. data/lib/state_machine/event.rb +135 -101
  12. data/lib/state_machine/extensions.rb +83 -0
  13. data/lib/state_machine/guard.rb +115 -0
  14. data/lib/state_machine/integrations/active_record.rb +242 -0
  15. data/lib/state_machine/integrations/data_mapper.rb +198 -0
  16. data/lib/state_machine/integrations/data_mapper/observer.rb +153 -0
  17. data/lib/state_machine/integrations/sequel.rb +169 -0
  18. data/lib/state_machine/machine.rb +746 -352
  19. data/lib/state_machine/transition.rb +104 -212
  20. data/test/active_record.log +34865 -0
  21. data/test/classes/switch.rb +11 -0
  22. data/test/data_mapper.log +14015 -0
  23. data/test/functional/state_machine_test.rb +249 -15
  24. data/test/sequel.log +3835 -0
  25. data/test/test_helper.rb +3 -12
  26. data/test/unit/assertions_test.rb +13 -0
  27. data/test/unit/callback_test.rb +189 -0
  28. data/test/unit/eval_helpers_test.rb +92 -0
  29. data/test/unit/event_test.rb +247 -113
  30. data/test/unit/guard_test.rb +420 -0
  31. data/test/unit/integrations/active_record_test.rb +515 -0
  32. data/test/unit/integrations/data_mapper_test.rb +407 -0
  33. data/test/unit/integrations/sequel_test.rb +244 -0
  34. data/test/unit/invalid_transition_test.rb +1 -1
  35. data/test/unit/machine_test.rb +1056 -98
  36. data/test/unit/state_machine_test.rb +14 -113
  37. data/test/unit/transition_test.rb +269 -495
  38. metadata +44 -30
  39. data/test/app_root/app/models/auto_shop.rb +0 -34
  40. data/test/app_root/app/models/car.rb +0 -19
  41. data/test/app_root/app/models/highway.rb +0 -3
  42. data/test/app_root/app/models/motorcycle.rb +0 -3
  43. data/test/app_root/app/models/switch.rb +0 -23
  44. data/test/app_root/app/models/switch_observer.rb +0 -20
  45. data/test/app_root/app/models/toggle_switch.rb +0 -2
  46. data/test/app_root/app/models/vehicle.rb +0 -78
  47. data/test/app_root/config/environment.rb +0 -7
  48. data/test/app_root/db/migrate/001_create_switches.rb +0 -12
  49. data/test/app_root/db/migrate/002_create_auto_shops.rb +0 -13
  50. data/test/app_root/db/migrate/003_create_highways.rb +0 -11
  51. data/test/app_root/db/migrate/004_create_vehicles.rb +0 -16
  52. data/test/factory.rb +0 -77
data/test/test_helper.rb CHANGED
@@ -1,13 +1,4 @@
1
- # Load the plugin testing framework
2
- $:.unshift("#{File.dirname(__FILE__)}/../../plugin_test_helper/lib")
3
- require 'rubygems'
4
- require 'plugin_test_helper'
1
+ require 'test/unit'
5
2
 
6
- # Run the migrations
7
- ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate")
8
-
9
- # Mixin the factory helper
10
- require File.expand_path("#{File.dirname(__FILE__)}/factory")
11
- class Test::Unit::TestCase #:nodoc:
12
- include Factory
13
- end
3
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
4
+ require File.dirname(__FILE__) + '/../init'
@@ -0,0 +1,13 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ class AssertionsTest < Test::Unit::TestCase
4
+ include StateMachine::Assertions
5
+
6
+ def test_should_not_raise_exception_if_key_is_valid
7
+ assert_nothing_raised { assert_valid_keys({:name => 'foo', :value => 'bar'}, :name, :value, :force) }
8
+ end
9
+
10
+ def test_should_raise_exception_if_key_is_invalid
11
+ assert_raise(ArgumentError) { assert_valid_keys({:name => 'foo', :value => 'bar', :invalid => true}, :name, :value, :force) }
12
+ end
13
+ end
@@ -0,0 +1,189 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ class CallbackTest < Test::Unit::TestCase
4
+ def test_should_raise_exception_if_do_option_not_specified
5
+ assert_raise(ArgumentError) { StateMachine::Callback.new }
6
+ end
7
+
8
+ def test_should_not_raise_exception_if_do_option_specified
9
+ assert_nothing_raised { StateMachine::Callback.new(:do => :run) }
10
+ end
11
+
12
+ def test_should_raise_exception_if_invalid_option_specified
13
+ assert_raise(ArgumentError) { StateMachine::Callback.new(:do => :run, :invalid => true) }
14
+ end
15
+
16
+ def test_should_not_bind_to_objects
17
+ assert !StateMachine::Callback.bind_to_object
18
+ end
19
+ end
20
+
21
+ class CallbackByDefaultTest < Test::Unit::TestCase
22
+ def setup
23
+ @object = Object.new
24
+ @method = lambda {|*args| args.unshift(self)}
25
+ @callback = StateMachine::Callback.new(:do => @method)
26
+ end
27
+
28
+ def test_should_not_have_a_terminator
29
+ assert_nil @callback.terminator
30
+ end
31
+
32
+ def test_should_have_a_guard_with_no_requirements
33
+ expected = {}
34
+ assert_equal expected, @callback.guard.requirements
35
+ end
36
+
37
+ def test_should_not_bind_to_the_object
38
+ assert_equal [self, @object], @callback.call(@object)
39
+ end
40
+
41
+ def test_should_not_have_any_known_states
42
+ assert_equal [], @callback.known_states
43
+ end
44
+ end
45
+
46
+ class CallbackWithOnlyMethodTest < Test::Unit::TestCase
47
+ def setup
48
+ @object = Object.new
49
+ @callback = StateMachine::Callback.new(lambda {true})
50
+ end
51
+
52
+ def test_should_call_with_empty_context
53
+ assert @callback.call(@object)
54
+ end
55
+ end
56
+
57
+ class CallbackWithRequirementsTest < Test::Unit::TestCase
58
+ def setup
59
+ @object = Object.new
60
+ @callback = StateMachine::Callback.new(:from => 'off', :to => 'on', :on => 'turn_on', :do => lambda {true})
61
+ end
62
+
63
+ def test_should_call_with_empty_context
64
+ assert @callback.call(@object, {})
65
+ end
66
+
67
+ def test_should_not_call_if_from_not_included
68
+ assert !@callback.call(@object, :from => 'on')
69
+ end
70
+
71
+ def test_should_not_call_if_to_not_included
72
+ assert !@callback.call(@object, :to => 'off')
73
+ end
74
+
75
+ def test_should_not_call_if_on_not_included
76
+ assert !@callback.call(@object, :on => 'turn_off')
77
+ end
78
+
79
+ def test_should_call_if_all_requirements_met
80
+ assert @callback.call(@object, :from => 'off', :to => 'on', :on => 'turn_on')
81
+ end
82
+
83
+ def test_should_include_in_known_states
84
+ assert_equal %w(off on), @callback.known_states.sort
85
+ end
86
+ end
87
+
88
+ class CallbackWithIfConditionalTest < Test::Unit::TestCase
89
+ def setup
90
+ @object = Object.new
91
+ end
92
+
93
+ def test_should_call_if_true
94
+ callback = StateMachine::Callback.new(:if => lambda {true}, :do => lambda {true})
95
+ assert callback.call(@object)
96
+ end
97
+
98
+ def test_should_not_call_if_false
99
+ callback = StateMachine::Callback.new(:if => lambda {false}, :do => lambda {true})
100
+ assert !callback.call(@object)
101
+ end
102
+ end
103
+
104
+ class CallbackWithUnlessConditionalTest < Test::Unit::TestCase
105
+ def setup
106
+ @object = Object.new
107
+ end
108
+
109
+ def test_should_call_if_false
110
+ callback = StateMachine::Callback.new(:unless => lambda {false}, :do => lambda {true})
111
+ assert callback.call(@object)
112
+ end
113
+
114
+ def test_should_not_call_if_true
115
+ callback = StateMachine::Callback.new(:unless => lambda {true}, :do => lambda {true})
116
+ assert !callback.call(@object)
117
+ end
118
+ end
119
+
120
+ class CallbackWithoutTerminatorTest < Test::Unit::TestCase
121
+ def setup
122
+ @object = Object.new
123
+ end
124
+
125
+ def test_should_not_halt_if_result_is_false
126
+ callback = StateMachine::Callback.new(:do => lambda {false}, :terminator => nil)
127
+ assert_nothing_thrown { callback.call(@object) }
128
+ end
129
+ end
130
+
131
+ class CallbackWithTerminatorTest < Test::Unit::TestCase
132
+ def setup
133
+ @object = Object.new
134
+ end
135
+
136
+ def test_should_not_halt_if_terminator_does_not_match
137
+ callback = StateMachine::Callback.new(:do => lambda {false}, :terminator => lambda {|result| false})
138
+ assert_nothing_thrown { callback.call(@object) }
139
+ end
140
+
141
+ def test_should_halt_if_terminator_matches
142
+ callback = StateMachine::Callback.new(:do => lambda {false}, :terminator => lambda {|result| true})
143
+ assert_throws(:halt) { callback.call(@object) }
144
+ end
145
+ end
146
+
147
+ class CallbackWithoutArgumentsTest < Test::Unit::TestCase
148
+ def setup
149
+ @object = Object.new
150
+ @callback = StateMachine::Callback.new(:do => lambda {|object| object})
151
+ end
152
+
153
+ def test_should_call_method_with_object_as_argument
154
+ assert_equal @object, @callback.call(@object, {}, 1, 2, 3)
155
+ end
156
+ end
157
+
158
+ class CallbackWithArgumentsTest < Test::Unit::TestCase
159
+ def setup
160
+ @object = Object.new
161
+ @callback = StateMachine::Callback.new(:do => lambda {|*args| args})
162
+ end
163
+
164
+ def test_should_call_method_with_all_arguments
165
+ assert_equal [@object, 1, 2, 3], @callback.call(@object, {}, 1, 2, 3)
166
+ end
167
+ end
168
+
169
+ class CallbackWithUnboundObjectTest < Test::Unit::TestCase
170
+ def setup
171
+ @object = Object.new
172
+ @callback = StateMachine::Callback.new(:do => lambda {|*args| args.unshift(self)})
173
+ end
174
+
175
+ def test_should_call_method_outside_the_context_of_the_object
176
+ assert_equal [self, @object, 1, 2, 3], @callback.call(@object, {}, 1, 2, 3)
177
+ end
178
+ end
179
+
180
+ class CallbackWithBoundObjectTest < Test::Unit::TestCase
181
+ def setup
182
+ @object = Object.new
183
+ @callback = StateMachine::Callback.new(:do => lambda {|*args| args.unshift(self)}, :bind_to_object => true)
184
+ end
185
+
186
+ def test_should_call_method_within_the_context_of_the_object
187
+ assert_equal [@object, 1, 2, 3], @callback.call(@object, {}, 1, 2, 3)
188
+ end
189
+ end
@@ -0,0 +1,92 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ class EvalHelperTest < Test::Unit::TestCase
4
+ include StateMachine::EvalHelpers
5
+
6
+ def setup
7
+ @object = Object.new
8
+ end
9
+
10
+ def test_should_raise_exception_if_method_is_not_symbol_string_or_proc
11
+ assert_raise(ArgumentError) { evaluate_method(@object, 1) }
12
+ end
13
+ end
14
+
15
+ class EvalHelpersSymbolTest < Test::Unit::TestCase
16
+ include StateMachine::EvalHelpers
17
+
18
+ def setup
19
+ class << (@object = Object.new)
20
+ def callback
21
+ true
22
+ end
23
+ end
24
+ end
25
+
26
+ def test_should_call_method_on_object_with_no_arguments
27
+ assert evaluate_method(@object, :callback, 1, 2, 3)
28
+ end
29
+ end
30
+
31
+ class EvalHelperSymbolWithArgumentsTest < Test::Unit::TestCase
32
+ include StateMachine::EvalHelpers
33
+
34
+ def setup
35
+ class << (@object = Object.new)
36
+ def callback(*args)
37
+ args
38
+ end
39
+ end
40
+ end
41
+
42
+ def test_should_call_method_with_all_arguments
43
+ assert_equal [1, 2, 3], evaluate_method(@object, :callback, 1, 2, 3)
44
+ end
45
+ end
46
+
47
+ class EvalHelperStringTest < Test::Unit::TestCase
48
+ include StateMachine::EvalHelpers
49
+
50
+ def setup
51
+ @object = Object.new
52
+ end
53
+
54
+ def test_should_evaluate_string
55
+ assert_equal 1, evaluate_method(@object, '1')
56
+ end
57
+
58
+ def test_should_evaluate_string_within_object_context
59
+ @object.instance_variable_set('@value', 1)
60
+ assert_equal 1, evaluate_method(@object, '@value')
61
+ end
62
+
63
+ def test_should_ignore_additional_arguments
64
+ assert_equal 1, evaluate_method(@object, '1', 2, 3, 4)
65
+ end
66
+ end
67
+
68
+ class EvalHelperProcTest < Test::Unit::TestCase
69
+ include StateMachine::EvalHelpers
70
+
71
+ def setup
72
+ @object = Object.new
73
+ @proc = lambda {|obj| obj}
74
+ end
75
+
76
+ def test_should_call_proc_with_object_as_argument
77
+ assert_equal @object, evaluate_method(@object, @proc, 1, 2, 3)
78
+ end
79
+ end
80
+
81
+ class EvalHelperProcWithArgumentsTest < Test::Unit::TestCase
82
+ include StateMachine::EvalHelpers
83
+
84
+ def setup
85
+ @object = Object.new
86
+ @proc = lambda {|*args| args}
87
+ end
88
+
89
+ def test_should_call_method_with_all_arguments
90
+ assert_equal [@object, 1, 2, 3], evaluate_method(@object, @proc, 1, 2, 3)
91
+ end
92
+ end
@@ -2,10 +2,22 @@ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
2
 
3
3
  class EventTest < Test::Unit::TestCase
4
4
  def setup
5
- @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state')
6
- @event = PluginAWeek::StateMachine::Event.new(@machine, 'turn_on')
5
+ @machine = StateMachine::Machine.new(Class.new)
6
+ @event = StateMachine::Event.new(@machine, 'turn_on')
7
+ end
8
+
9
+ def test_should_raise_exception_if_invalid_option_specified
10
+ assert_raise(ArgumentError) {StateMachine::Event.new(@machine, 'turn_on', :invalid => true)}
11
+ end
12
+ end
13
+
14
+ class EventByDefaultTest < Test::Unit::TestCase
15
+ def setup
16
+ @klass = Class.new
17
+ @machine = StateMachine::Machine.new(@klass)
18
+ @event = StateMachine::Event.new(@machine, 'turn_on')
7
19
 
8
- @switch = new_switch
20
+ @object = @klass.new
9
21
  end
10
22
 
11
23
  def test_should_have_a_machine
@@ -16,49 +28,77 @@ class EventTest < Test::Unit::TestCase
16
28
  assert_equal 'turn_on', @event.name
17
29
  end
18
30
 
19
- def test_should_not_have_any_transitions
20
- assert @event.transitions.empty?
31
+ def test_should_not_have_any_guards
32
+ assert @event.guards.empty?
21
33
  end
22
34
 
23
- def test_should_define_an_event_action_on_the_owner_class
24
- assert @switch.respond_to?(:turn_on)
35
+ def test_should_have_no_known_states
36
+ assert @event.known_states.empty?
25
37
  end
26
38
 
27
- def test_should_define_an_event_bang_action_on_the_owner_class
28
- assert @switch.respond_to?(:turn_on!)
39
+ def test_should_not_be_able_to_fire
40
+ assert !@event.can_fire?(@object)
41
+ end
42
+
43
+ def test_should_not_have_a_next_transition
44
+ assert_nil @event.next_transition(@object)
29
45
  end
30
46
 
31
47
  def test_should_define_an_event_predicate_on_the_owner_class
32
- assert @switch.respond_to?(:can_turn_on?)
48
+ assert @object.respond_to?(:can_turn_on?)
33
49
  end
34
50
 
35
- def test_should_raise_exception_if_invalid_option_specified
36
- assert_raise(ArgumentError) {PluginAWeek::StateMachine::Event.new(@machine, 'turn_on', :invalid => true)}
51
+ def test_should_define_an_event_transition_accessor_on_the_owner_class
52
+ assert @object.respond_to?(:next_turn_on_transition)
53
+ end
54
+
55
+ def test_should_define_an_event_action_on_the_owner_class
56
+ assert @object.respond_to?(:turn_on)
57
+ end
58
+
59
+ def test_should_define_an_event_bang_action_on_the_owner_class
60
+ assert @object.respond_to?(:turn_on!)
37
61
  end
38
62
  end
39
63
 
40
- class EventDefiningTransitionsTest < Test::Unit::TestCase
64
+ class EventTransitionsTest < Test::Unit::TestCase
41
65
  def setup
42
- @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state', :initial => 'off')
43
- @event = PluginAWeek::StateMachine::Event.new(@machine, 'turn_on')
66
+ @machine = StateMachine::Machine.new(Class.new)
67
+ @event = StateMachine::Event.new(@machine, 'turn_on')
44
68
  end
45
69
 
46
70
  def test_should_raise_exception_if_invalid_option_specified
47
71
  assert_raise(ArgumentError) {@event.transition(:invalid => true)}
48
72
  end
49
73
 
50
- def test_should_raise_exception_if_to_option_not_specified
51
- assert_raise(ArgumentError) {@event.transition(:from => 'off')}
74
+ def test_should_not_raise_exception_if_to_option_not_specified
75
+ assert_nothing_raised {@event.transition(:from => 'off')}
52
76
  end
53
77
 
54
78
  def test_should_not_raise_exception_if_from_option_not_specified
55
79
  assert_nothing_raised {@event.transition(:to => 'on')}
56
80
  end
57
81
 
82
+ def test_should_not_allow_on_option
83
+ assert_raise(ArgumentError) {@event.transition(:on => 'turn_on')}
84
+ end
85
+
86
+ def test_should_not_allow_except_to_option
87
+ assert_raise(ArgumentError) {@event.transition(:except_to => 'off')}
88
+ end
89
+
90
+ def test_should_not_allow_except_on_option
91
+ assert_raise(ArgumentError) {@event.transition(:except_on => 'turn_on')}
92
+ end
93
+
58
94
  def test_should_allow_transitioning_without_a_from_state
59
95
  assert @event.transition(:to => 'on')
60
96
  end
61
97
 
98
+ def test_should_allow_transitioning_without_a_to_state
99
+ assert @event.transition(:from => 'off')
100
+ end
101
+
62
102
  def test_should_allow_transitioning_from_a_single_state
63
103
  assert @event.transition(:to => 'on', :from => 'off')
64
104
  end
@@ -68,168 +108,262 @@ class EventDefiningTransitionsTest < Test::Unit::TestCase
68
108
  end
69
109
 
70
110
  def test_should_have_transitions
71
- transition = @event.transition(:to => 'on')
72
- assert_equal [transition], @event.transitions
111
+ guard = @event.transition(:to => 'on')
112
+ assert_equal [guard], @event.guards
73
113
  end
74
114
  end
75
115
 
76
116
  class EventAfterBeingCopiedTest < Test::Unit::TestCase
77
117
  def setup
78
- @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state', :initial => 'off')
79
- @event = PluginAWeek::StateMachine::Event.new(@machine, 'turn_on')
118
+ @machine = StateMachine::Machine.new(Class.new)
119
+ @event = StateMachine::Event.new(@machine, 'turn_on')
120
+ @event.known_states # Call so that it's cached
80
121
  @copied_event = @event.dup
81
122
  end
82
123
 
83
- def test_should_not_have_the_same_collection_of_transitions
84
- assert_not_same @copied_event.transitions, @event.transitions
124
+ def test_should_not_have_the_same_collection_of_guards
125
+ assert_not_same @event.guards, @copied_event.guards
126
+ end
127
+
128
+ def test_should_not_have_the_same_collection_of_known_states
129
+ assert_not_same @event.known_states, @copied_event.known_states
85
130
  end
86
131
  end
87
132
 
88
133
  class EventWithoutTransitionsTest < Test::Unit::TestCase
89
134
  def setup
90
- @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state', :initial => 'off')
91
- @event = PluginAWeek::StateMachine::Event.new(@machine, 'turn_on')
92
- @switch = create_switch(:state => 'off')
135
+ @klass = Class.new
136
+ @machine = StateMachine::Machine.new(@klass)
137
+ @event = StateMachine::Event.new(@machine, 'turn_on')
138
+ @object = @klass.new
93
139
  end
94
140
 
95
141
  def test_should_not_be_able_to_fire
96
- assert !@event.can_fire?(@switch)
142
+ assert !@event.can_fire?(@object)
143
+ end
144
+
145
+ def test_should_not_have_a_next_transition
146
+ assert_nil @event.next_transition(@object)
97
147
  end
98
148
 
99
149
  def test_should_not_fire
100
- assert !@event.fire(@switch)
150
+ assert !@event.fire(@object)
101
151
  end
102
152
 
103
153
  def test_should_not_change_the_current_state
104
- @event.fire(@switch)
105
- assert_equal 'off', @switch.state
154
+ @event.fire(@object)
155
+ assert_nil @object.state
156
+ end
157
+ end
158
+
159
+ class EventWithTransitionsTest < Test::Unit::TestCase
160
+ def setup
161
+ @klass = Class.new
162
+ @machine = StateMachine::Machine.new(@klass)
163
+ @event = StateMachine::Event.new(@machine, 'turn_on')
164
+ @event.transition(:to => 'on', :from => 'off')
165
+ @event.transition(:to => 'on', :except_from => 'maybe')
106
166
  end
107
167
 
108
- def test_should_raise_exception_during_fire!
109
- assert_raise(PluginAWeek::StateMachine::InvalidTransition) {@event.fire!(@switch)}
168
+ def test_should_include_all_transition_states_in_known_states
169
+ assert_equal %w(maybe off on), @event.known_states.sort
170
+ end
171
+
172
+ def test_should_include_new_transition_states_after_calling_known_states
173
+ @event.known_states
174
+ @event.transition(:to => 'error')
175
+
176
+ assert_equal %w(error maybe off on), @event.known_states.sort
110
177
  end
111
178
  end
112
179
 
113
- class EventWithTransitionsTest < Test::Unit::TestCase
180
+ class EventWithoutMatchingTransitionsTest < Test::Unit::TestCase
181
+ def setup
182
+ @klass = Class.new
183
+ @machine = StateMachine::Machine.new(@klass)
184
+ @event = StateMachine::Event.new(@machine, 'turn_on')
185
+ @event.transition(:to => 'on', :from => 'off')
186
+
187
+ @object = @klass.new
188
+ @object.state = 'on'
189
+ end
190
+
191
+ def test_should_not_be_able_to_fire
192
+ assert !@event.can_fire?(@object)
193
+ end
194
+
195
+ def test_should_not_have_a_next_transition
196
+ assert_nil @event.next_transition(@object)
197
+ end
198
+
199
+ def test_should_not_fire
200
+ assert !@event.fire(@object)
201
+ end
202
+
203
+ def test_should_not_change_the_current_state
204
+ @event.fire(@object)
205
+ assert_equal 'on', @object.state
206
+ end
207
+ end
208
+
209
+ class EventWithMatchingDisabledTransitionsTest < Test::Unit::TestCase
114
210
  def setup
115
- @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state', :initial => 'off')
116
- @event = PluginAWeek::StateMachine::Event.new(@machine, 'turn_on')
117
- @event.transition :to => 'error', :from => 'on'
118
- @switch = create_switch(:state => 'off')
211
+ @klass = Class.new
212
+ @machine = StateMachine::Machine.new(@klass)
213
+ @event = StateMachine::Event.new(@machine, 'turn_on')
214
+ @event.transition(:to => 'on', :from => 'off', :if => lambda {false})
215
+
216
+ @object = @klass.new
217
+ @object.state = 'off'
119
218
  end
120
219
 
121
- def test_should_not_be_able_to_fire_if_no_transitions_are_matched
122
- assert !@event.can_fire?(@switch)
220
+ def test_should_not_be_able_to_fire
221
+ assert !@event.can_fire?(@object)
123
222
  end
124
223
 
125
- def test_should_not_fire_if_no_transitions_are_matched
126
- assert !@event.fire(@switch)
127
- assert_equal 'off', @switch.state
224
+ def test_should_not_have_a_next_transition
225
+ assert_nil @event.next_transition(@object)
128
226
  end
129
227
 
130
- def test_should_raise_exception_if_no_transitions_are_matched_during_fire!
131
- assert_raise(PluginAWeek::StateMachine::InvalidTransition) {@event.fire!(@switch)}
132
- assert_equal 'off', @switch.state
228
+ def test_should_not_fire
229
+ assert !@event.fire(@object)
133
230
  end
134
231
 
135
- def test_should_be_able_to_fire_if_transition_is_matched
136
- @event.transition :to => 'on'
137
- assert @event.can_fire?(@switch)
232
+ def test_should_not_change_the_current_state
233
+ @event.fire(@object)
234
+ assert_equal 'off', @object.state
235
+ end
236
+ end
237
+
238
+ class EventWithMatchingEnabledTransitionsTest < Test::Unit::TestCase
239
+ def setup
240
+ @klass = Class.new
241
+ @machine = StateMachine::Machine.new(@klass)
242
+ @event = StateMachine::Event.new(@machine, 'turn_on')
243
+ @event.transition(:to => 'on', :from => 'off')
244
+
245
+ @object = @klass.new
246
+ @object.state = 'off'
138
247
  end
139
248
 
140
- def test_should_fire_if_transition_is_matched
141
- @event.transition :to => 'on'
142
- assert @event.fire(@switch)
143
- assert_equal 'on', @switch.state
249
+ def test_should_be_able_to_fire
250
+ assert @event.can_fire?(@object)
144
251
  end
145
252
 
146
- def test_should_fire_if_transition_with_from_state_is_matched
147
- @event.transition :to => 'on', :from => 'off'
148
- assert @event.fire(@switch)
149
- assert_equal 'on', @switch.state
253
+ def test_should_have_a_next_transition
254
+ transition = @event.next_transition(@object)
255
+ assert_not_nil transition
256
+ assert_equal 'off', transition.from
257
+ assert_equal 'on', transition.to
258
+ assert_equal 'turn_on', transition.event
150
259
  end
151
260
 
152
- def test_should_fire_if_transition_with_multiple_from_states_is_matched
153
- @event.transition :to => 'on', :from => %w(off on)
154
- assert @event.fire(@switch)
155
- assert_equal 'on', @switch.state
261
+ def test_should_fire
262
+ assert @event.fire(@object)
156
263
  end
157
264
 
158
- def test_should_not_fire_if_validation_failed
159
- @event.transition :to => 'on', :from => 'off'
160
- @switch.fail_validation = true
161
- assert !@event.fire(@switch)
265
+ def test_should_change_the_current_state
266
+ @event.fire(@object)
267
+ assert_equal 'on', @object.state
268
+ end
269
+ end
270
+
271
+ class EventWithTransitionWithoutToStateTest < Test::Unit::TestCase
272
+ def setup
273
+ @klass = Class.new
274
+ @machine = StateMachine::Machine.new(@klass)
275
+ @event = StateMachine::Event.new(@machine, 'turn_off')
276
+ @event.transition(:from => 'off')
162
277
 
163
- @switch.reload
164
- assert_equal 'off', @switch.state
278
+ @object = @klass.new
279
+ @object.state = 'off'
280
+ end
281
+
282
+ def test_should_be_able_to_fire
283
+ assert @event.can_fire?(@object)
284
+ end
285
+
286
+ def test_should_have_a_next_transition
287
+ transition = @event.next_transition(@object)
288
+ assert_not_nil transition
289
+ assert_equal 'off', transition.from
290
+ assert_equal 'off', transition.to
291
+ assert_equal 'turn_off', transition.event
165
292
  end
166
293
 
167
- def test_should_raise_exception_if_validation_failed_during_fire!
168
- @event.transition :to => 'on', :from => 'off'
169
- @switch.fail_validation = true
170
- assert_raise(ActiveRecord::RecordInvalid) {@event.fire!(@switch)}
294
+ def test_should_fire
295
+ assert @event.fire(@object)
171
296
  end
172
297
 
173
- def test_should_not_fire_if_save_failed
174
- @event.transition :to => 'on', :from => 'off'
175
- @switch.fail_save = true
176
- assert !@event.fire(@switch)
298
+ def test_should_not_change_the_current_state
299
+ @event.fire(@object)
300
+ assert_equal 'off', @object.state
301
+ end
302
+ end
303
+
304
+ class EventWithTransitionWithDynamicToStateTest < Test::Unit::TestCase
305
+ def setup
306
+ @klass = Class.new
307
+ @machine = StateMachine::Machine.new(@klass)
308
+ @event = StateMachine::Event.new(@machine, 'turn_on')
309
+ @event.transition(:to => lambda {'on'}, :from => 'off')
177
310
 
178
- @switch.reload
179
- assert_equal 'off', @switch.state
311
+ @object = @klass.new
312
+ @object.state = 'off'
313
+ end
314
+
315
+ def test_should_be_able_to_fire
316
+ assert @event.can_fire?(@object)
317
+ end
318
+
319
+ def test_should_have_a_next_transition
320
+ transition = @event.next_transition(@object)
321
+ assert_not_nil transition
322
+ assert_equal 'off', transition.from
323
+ assert_equal 'on', transition.to
324
+ assert_equal 'turn_on', transition.event
180
325
  end
181
326
 
182
- def test_should_raise_exception_if_save_failed_during_fire!
183
- @event.transition :to => 'on', :from => 'off'
184
- @switch.fail_save = true
185
- assert_raise(ActiveRecord::RecordNotSaved) {@event.fire!(@switch)}
327
+ def test_should_fire
328
+ assert @event.fire(@object)
186
329
  end
187
330
 
188
- def test_should_not_raise_exception_if_transition_is_matched_during_fire!
189
- @event.transition :to => 'on', :from => 'off'
190
- assert @event.fire!(@switch)
191
- assert_equal 'on', @switch.state
331
+ def test_should_change_the_current_state
332
+ @event.fire(@object)
333
+ assert_equal 'on', @object.state
192
334
  end
193
335
  end
194
336
 
195
- class EventWithinTransactionTest < Test::Unit::TestCase
337
+ class EventWithMultipleTransitionsTest < Test::Unit::TestCase
196
338
  def setup
197
- @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state', :initial => 'off')
198
- @event = PluginAWeek::StateMachine::Event.new(@machine, 'turn_on')
199
- @event.transition :to => 'on', :from => 'off'
200
- @switch = create_switch(:state => 'off')
339
+ @klass = Class.new
340
+ @machine = StateMachine::Machine.new(@klass)
341
+ @event = StateMachine::Event.new(@machine, 'turn_on')
342
+ @event.transition(:to => 'on', :from => 'on')
343
+ @event.transition(:to => 'on', :from => 'off') # This one should get used
201
344
 
202
- Switch.define_callbacks :before_transition_state
345
+ @object = @klass.new
346
+ @object.state = 'off'
203
347
  end
204
348
 
205
- def test_should_save_all_records_within_transaction_if_performed
206
- Switch.before_transition_state lambda {|record| Switch.create(:state => 'pending'); true}, :from => 'off'
207
- assert @event.fire(@switch)
208
- assert_equal 'on', @switch.state
209
- assert_equal 'pending', Switch.find(:all).last.state
349
+ def test_should_be_able_to_fire
350
+ assert @event.can_fire?(@object)
210
351
  end
211
352
 
212
- uses_transaction :test_should_rollback_all_records_within_transaction_if_not_performed
213
- def test_should_rollback_all_records_within_transaction_if_not_performed
214
- Switch.before_transition_state lambda {|record| Switch.create(:state => 'pending'); false}, :from => 'off'
215
- assert !@event.fire(@switch)
216
- assert_equal 1, Switch.count
217
- ensure
218
- Switch.destroy_all
353
+ def test_should_have_a_next_transition
354
+ transition = @event.next_transition(@object)
355
+ assert_not_nil transition
356
+ assert_equal 'off', transition.from
357
+ assert_equal 'on', transition.to
358
+ assert_equal 'turn_on', transition.event
219
359
  end
220
360
 
221
- uses_transaction :test_should_rollback_all_records_within_transaction_if_not_performed!
222
- def test_should_rollback_all_records_within_transaction_if_not_performed!
223
- Switch.before_transition_state lambda {|record| Switch.create(:state => 'pending'); false}, :from => 'off'
224
- assert_raise(PluginAWeek::StateMachine::InvalidTransition) {@event.fire!(@switch)}
225
- assert_equal 1, Switch.count
226
- ensure
227
- Switch.destroy_all
361
+ def test_should_fire
362
+ assert @event.fire(@object)
228
363
  end
229
364
 
230
- def teardown
231
- Switch.class_eval do
232
- @before_transition_state_callbacks = nil
233
- end
365
+ def test_should_change_the_current_state
366
+ @event.fire(@object)
367
+ assert_equal 'on', @object.state
234
368
  end
235
369
  end