state_machine 0.3.1 → 0.4.0

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