state_machine 0.9.4 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. data/CHANGELOG.rdoc +20 -0
  2. data/LICENSE +1 -1
  3. data/README.rdoc +74 -4
  4. data/Rakefile +3 -3
  5. data/lib/state_machine.rb +51 -24
  6. data/lib/state_machine/{guard.rb → branch.rb} +34 -40
  7. data/lib/state_machine/callback.rb +13 -18
  8. data/lib/state_machine/error.rb +13 -0
  9. data/lib/state_machine/eval_helpers.rb +3 -0
  10. data/lib/state_machine/event.rb +67 -30
  11. data/lib/state_machine/event_collection.rb +20 -3
  12. data/lib/state_machine/extensions.rb +3 -3
  13. data/lib/state_machine/integrations.rb +7 -0
  14. data/lib/state_machine/integrations/active_model.rb +149 -59
  15. data/lib/state_machine/integrations/active_model/versions.rb +30 -0
  16. data/lib/state_machine/integrations/active_record.rb +74 -148
  17. data/lib/state_machine/integrations/active_record/locale.rb +0 -7
  18. data/lib/state_machine/integrations/active_record/versions.rb +149 -0
  19. data/lib/state_machine/integrations/base.rb +64 -0
  20. data/lib/state_machine/integrations/data_mapper.rb +50 -39
  21. data/lib/state_machine/integrations/data_mapper/observer.rb +47 -12
  22. data/lib/state_machine/integrations/data_mapper/versions.rb +62 -0
  23. data/lib/state_machine/integrations/mongo_mapper.rb +37 -64
  24. data/lib/state_machine/integrations/mongo_mapper/locale.rb +4 -0
  25. data/lib/state_machine/integrations/mongo_mapper/versions.rb +102 -0
  26. data/lib/state_machine/integrations/mongoid.rb +297 -0
  27. data/lib/state_machine/integrations/mongoid/locale.rb +4 -0
  28. data/lib/state_machine/integrations/mongoid/versions.rb +18 -0
  29. data/lib/state_machine/integrations/sequel.rb +99 -55
  30. data/lib/state_machine/integrations/sequel/versions.rb +40 -0
  31. data/lib/state_machine/machine.rb +273 -136
  32. data/lib/state_machine/machine_collection.rb +21 -13
  33. data/lib/state_machine/node_collection.rb +6 -1
  34. data/lib/state_machine/path.rb +120 -0
  35. data/lib/state_machine/path_collection.rb +90 -0
  36. data/lib/state_machine/state.rb +28 -9
  37. data/lib/state_machine/state_collection.rb +1 -1
  38. data/lib/state_machine/transition.rb +65 -6
  39. data/lib/state_machine/transition_collection.rb +1 -1
  40. data/test/files/en.yml +8 -0
  41. data/test/functional/state_machine_test.rb +15 -2
  42. data/test/unit/branch_test.rb +890 -0
  43. data/test/unit/callback_test.rb +9 -36
  44. data/test/unit/error_test.rb +43 -0
  45. data/test/unit/event_collection_test.rb +67 -33
  46. data/test/unit/event_test.rb +165 -38
  47. data/test/unit/integrations/active_model_test.rb +103 -3
  48. data/test/unit/integrations/active_record_test.rb +90 -43
  49. data/test/unit/integrations/base_test.rb +87 -0
  50. data/test/unit/integrations/data_mapper_test.rb +105 -44
  51. data/test/unit/integrations/mongo_mapper_test.rb +261 -64
  52. data/test/unit/integrations/mongoid_test.rb +1529 -0
  53. data/test/unit/integrations/sequel_test.rb +33 -49
  54. data/test/unit/integrations_test.rb +4 -0
  55. data/test/unit/invalid_event_test.rb +15 -2
  56. data/test/unit/invalid_parallel_transition_test.rb +18 -0
  57. data/test/unit/invalid_transition_test.rb +72 -2
  58. data/test/unit/machine_collection_test.rb +55 -61
  59. data/test/unit/machine_test.rb +388 -26
  60. data/test/unit/node_collection_test.rb +14 -4
  61. data/test/unit/path_collection_test.rb +266 -0
  62. data/test/unit/path_test.rb +485 -0
  63. data/test/unit/state_collection_test.rb +30 -0
  64. data/test/unit/state_test.rb +82 -35
  65. data/test/unit/transition_collection_test.rb +48 -44
  66. data/test/unit/transition_test.rb +198 -41
  67. metadata +111 -74
  68. data/test/unit/guard_test.rb +0 -909
@@ -3,7 +3,7 @@ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
3
3
  class CallbackTest < Test::Unit::TestCase
4
4
  def test_should_raise_exception_if_invalid_type_specified
5
5
  exception = assert_raise(ArgumentError) { StateMachine::Callback.new(:invalid) {} }
6
- assert_equal 'Type must be :before, :after, or :around', exception.message
6
+ assert_equal 'Type must be :before, :after, :around, or :failure', exception.message
7
7
  end
8
8
 
9
9
  def test_should_not_raise_exception_if_using_before_type
@@ -18,6 +18,10 @@ class CallbackTest < Test::Unit::TestCase
18
18
  assert_nothing_raised { StateMachine::Callback.new(:around) {} }
19
19
  end
20
20
 
21
+ def test_should_not_raise_exception_if_using_failure_type
22
+ assert_nothing_raised { StateMachine::Callback.new(:failure) {} }
23
+ end
24
+
21
25
  def test_should_raise_exception_if_no_methods_specified
22
26
  exception = assert_raise(ArgumentError) { StateMachine::Callback.new(:before) }
23
27
  assert_equal 'Method(s) for callback must be specified', exception.message
@@ -61,10 +65,10 @@ class CallbackByDefaultTest < Test::Unit::TestCase
61
65
  assert_nil @callback.terminator
62
66
  end
63
67
 
64
- def test_should_have_a_guard_with_all_matcher_requirements
65
- assert_equal StateMachine::AllMatcher.instance, @callback.guard.event_requirement
66
- assert_equal StateMachine::AllMatcher.instance, @callback.guard.state_requirements.first[:from]
67
- assert_equal StateMachine::AllMatcher.instance, @callback.guard.state_requirements.first[:to]
68
+ def test_should_have_a_branch_with_all_matcher_requirements
69
+ assert_equal StateMachine::AllMatcher.instance, @callback.branch.event_requirement
70
+ assert_equal StateMachine::AllMatcher.instance, @callback.branch.state_requirements.first[:from]
71
+ assert_equal StateMachine::AllMatcher.instance, @callback.branch.state_requirements.first[:to]
68
72
  end
69
73
 
70
74
  def test_should_not_have_any_known_states
@@ -695,34 +699,3 @@ class CallbackWithAroundTypeAndBoundMethodTest < Test::Unit::TestCase
695
699
  assert_equal [1, 2, 3], context
696
700
  end
697
701
  end
698
-
699
- class CallbackSuccessMatcherTest < Test::Unit::TestCase
700
- def setup
701
- @object = Object.new
702
- end
703
-
704
- def test_should_match_if_not_specified
705
- callback = StateMachine::Callback.new(:before) {}
706
- assert callback.matches_success?(true)
707
- end
708
-
709
- def test_should_match_if_true_and_not_including_failures
710
- callback = StateMachine::Callback.new(:before, :include_failures => false) {}
711
- assert callback.matches_success?(true)
712
- end
713
-
714
- def test_should_match_if_true_and_including_failures
715
- callback = StateMachine::Callback.new(:before, :include_failures => true) {}
716
- assert callback.matches_success?(true)
717
- end
718
-
719
- def test_should_not_match_if_false_and_not_including_failures
720
- callback = StateMachine::Callback.new(:before, :include_failures => false) {}
721
- assert !callback.matches_success?(false)
722
- end
723
-
724
- def test_Should_match_if_false_and_including_failures
725
- callback = StateMachine::Callback.new(:before, :include_failures => true) {}
726
- assert callback.matches_success?(false)
727
- end
728
- end
@@ -0,0 +1,43 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ class ErrorByDefaultTest < Test::Unit::TestCase
4
+ def setup
5
+ @machine = StateMachine::Machine.new(Class.new)
6
+ @collection = StateMachine::NodeCollection.new(@machine)
7
+ end
8
+
9
+ def test_should_not_have_any_nodes
10
+ assert_equal 0, @collection.length
11
+ end
12
+
13
+ def test_should_have_a_machine
14
+ assert_equal @machine, @collection.machine
15
+ end
16
+
17
+ def test_should_index_by_name
18
+ @collection << object = Struct.new(:name).new(:parked)
19
+ assert_equal object, @collection[:parked]
20
+ end
21
+ end
22
+
23
+ class ErrorWithMessageTest < Test::Unit::TestCase
24
+ def setup
25
+ @machine = StateMachine::Machine.new(Class.new)
26
+ @collection = StateMachine::NodeCollection.new(@machine)
27
+ end
28
+
29
+ def test_should_raise_exception_if_invalid_option_specified
30
+ exception = assert_raise(ArgumentError) { StateMachine::NodeCollection.new(@machine, :invalid => true) }
31
+ assert_equal 'Invalid key(s): invalid', exception.message
32
+ end
33
+
34
+ def test_should_raise_exception_on_lookup_if_invalid_index_specified
35
+ exception = assert_raise(ArgumentError) { @collection[:something, :invalid] }
36
+ assert_equal 'Invalid index: :invalid', exception.message
37
+ end
38
+
39
+ def test_should_raise_exception_on_fetch_if_invalid_index_specified
40
+ exception = assert_raise(ArgumentError) { @collection.fetch(:something, :invalid) }
41
+ assert_equal 'Invalid index: :invalid', exception.message
42
+ end
43
+ end
@@ -29,6 +29,7 @@ class EventCollectionTest < Test::Unit::TestCase
29
29
  @events = StateMachine::EventCollection.new(machine)
30
30
 
31
31
  @events << @open = StateMachine::Event.new(machine, :enable)
32
+ machine.events.concat(@events)
32
33
  end
33
34
 
34
35
  def test_should_index_by_name
@@ -50,42 +51,72 @@ class EventCollectionWithEventsWithTransitionsTest < Test::Unit::TestCase
50
51
  @machine = StateMachine::Machine.new(@klass, :initial => :parked)
51
52
  @events = StateMachine::EventCollection.new(@machine)
52
53
 
53
- @machine.state :idling, :stalled
54
- @machine.event :ignite
54
+ @machine.state :idling, :first_gear
55
55
 
56
56
  @events << @ignite = StateMachine::Event.new(@machine, :ignite)
57
57
  @ignite.transition :parked => :idling
58
- @ignite.transition :stalled => :idling
59
- end
60
-
61
- def test_should_only_include_valid_events_for_an_object
62
- object = @klass.new
63
- object.state = 'parked'
64
- assert_equal [@ignite], @events.valid_for(object)
65
58
 
66
- object.state = 'stalled'
67
- assert_equal [@ignite], @events.valid_for(object)
59
+ @events << @park = StateMachine::Event.new(@machine, :park)
60
+ @park.transition :idling => :parked
68
61
 
69
- object.state = 'idling'
70
- assert_equal [], @events.valid_for(object)
71
- end
72
-
73
- def test_should_only_include_valid_transitions_for_an_object
74
- object = @klass.new
75
- object.state = 'parked'
76
- assert_equal [{:object => object, :attribute => :state, :event => :ignite, :from => 'parked', :to => 'idling'}], @events.transitions_for(object).map {|transition| transition.attributes}
62
+ @events << @shift_up = StateMachine::Event.new(@machine, :shift_up)
63
+ @shift_up.transition :parked => :first_gear
64
+ @shift_up.transition :idling => :first_gear, :if => lambda{false}
77
65
 
78
- object.state = 'stalled'
79
- assert_equal [{:object => object, :attribute => :state, :event => :ignite, :from => 'stalled', :to => 'idling'}], @events.transitions_for(object).map {|transition| transition.attributes}
66
+ @machine.events.concat(@events)
80
67
 
81
- object.state = 'idling'
82
- assert_equal [], @events.transitions_for(object)
68
+ @object = @klass.new
83
69
  end
84
70
 
85
- def test_should_filter_valid_transitions_for_an_object_if_requirements_specified
86
- object = @klass.new
87
- assert_equal [{:object => object, :attribute => :state, :event => :ignite, :from => 'stalled', :to => 'idling'}], @events.transitions_for(object, :from => :stalled).map {|transition| transition.attributes}
88
- assert_equal [], @events.transitions_for(object, :from => :idling).map {|transition| transition.attributes}
71
+ def test_should_find_valid_events_based_on_current_state
72
+ assert_equal [@ignite, @shift_up], @events.valid_for(@object)
73
+ end
74
+
75
+ def test_should_filter_valid_events_by_from_state
76
+ assert_equal [@park], @events.valid_for(@object, :from => :idling)
77
+ end
78
+
79
+ def test_should_filter_valid_events_by_to_state
80
+ assert_equal [@shift_up], @events.valid_for(@object, :to => :first_gear)
81
+ end
82
+
83
+ def test_should_filter_valid_events_by_event
84
+ assert_equal [@ignite], @events.valid_for(@object, :on => :ignite)
85
+ end
86
+
87
+ def test_should_filter_valid_events_by_multiple_requirements
88
+ assert_equal [], @events.valid_for(@object, :from => :idling, :to => :first_gear)
89
+ end
90
+
91
+ def test_should_allow_finding_valid_events_without_guards
92
+ assert_equal [@shift_up], @events.valid_for(@object, :from => :idling, :to => :first_gear, :guard => false)
93
+ end
94
+
95
+ def test_should_find_valid_transitions_based_on_current_state
96
+ assert_equal [
97
+ StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling),
98
+ StateMachine::Transition.new(@object, @machine, :shift_up, :parked, :first_gear)
99
+ ], @events.transitions_for(@object)
100
+ end
101
+
102
+ def test_should_filter_valid_transitions_by_from_state
103
+ assert_equal [StateMachine::Transition.new(@object, @machine, :park, :idling, :parked)], @events.transitions_for(@object, :from => :idling)
104
+ end
105
+
106
+ def test_should_filter_valid_transitions_by_to_state
107
+ assert_equal [StateMachine::Transition.new(@object, @machine, :shift_up, :parked, :first_gear)], @events.transitions_for(@object, :to => :first_gear)
108
+ end
109
+
110
+ def test_should_filter_valid_transitions_by_event
111
+ assert_equal [StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)], @events.transitions_for(@object, :on => :ignite)
112
+ end
113
+
114
+ def test_should_filter_valid_transitions_by_multiple_requirements
115
+ assert_equal [], @events.transitions_for(@object, :from => :idling, :to => :first_gear)
116
+ end
117
+
118
+ def test_should_allow_finding_valid_transitions_without_guards
119
+ assert_equal [StateMachine::Transition.new(@object, @machine, :shift_up, :idling, :first_gear)], @events.transitions_for(@object, :from => :idling, :to => :first_gear, :guard => false)
89
120
  end
90
121
  end
91
122
 
@@ -103,6 +134,8 @@ class EventCollectionWithMultipleEventsTest < Test::Unit::TestCase
103
134
 
104
135
  @events << @shift_down = StateMachine::Event.new(@machine, :shift_down)
105
136
  @shift_down.transition :first_gear => :parked
137
+
138
+ @machine.events.concat(@events)
106
139
  end
107
140
 
108
141
  def test_should_only_include_all_valid_events_for_an_object
@@ -117,9 +150,8 @@ class EventCollectionWithoutMachineActionTest < Test::Unit::TestCase
117
150
  @klass = Class.new
118
151
  @machine = StateMachine::Machine.new(@klass, :initial => :parked)
119
152
  @events = StateMachine::EventCollection.new(@machine)
120
-
121
- @machine.event :ignite
122
153
  @events << StateMachine::Event.new(@machine, :ignite)
154
+ @machine.events.concat(@events)
123
155
 
124
156
  @object = @klass.new
125
157
  end
@@ -139,9 +171,9 @@ class EventCollectionAttributeWithMachineActionTest < Test::Unit::TestCase
139
171
  @machine = StateMachine::Machine.new(@klass, :initial => :parked, :action => :save)
140
172
  @events = StateMachine::EventCollection.new(@machine)
141
173
 
142
- @machine.event :ignite
143
174
  @machine.state :parked, :idling
144
175
  @events << @ignite = StateMachine::Event.new(@machine, :ignite)
176
+ @machine.events.concat(@events)
145
177
 
146
178
  @object = @klass.new
147
179
  end
@@ -200,9 +232,9 @@ class EventCollectionAttributeWithNamespacedMachineTest < Test::Unit::TestCase
200
232
  @machine = StateMachine::Machine.new(@klass, :namespace => 'alarm', :initial => :active, :action => :save)
201
233
  @events = StateMachine::EventCollection.new(@machine)
202
234
 
203
- @machine.event :disable
204
235
  @machine.state :active, :off
205
236
  @events << @disable = StateMachine::Event.new(@machine, :disable)
237
+ @machine.events.concat(@events)
206
238
 
207
239
  @object = @klass.new
208
240
  end
@@ -228,6 +260,8 @@ end
228
260
  class EventCollectionWithValidationsTest < Test::Unit::TestCase
229
261
  def setup
230
262
  StateMachine::Integrations.const_set('Custom', Module.new do
263
+ include StateMachine::Integrations::Base
264
+
231
265
  def invalidate(object, attribute, message, values = [])
232
266
  (object.errors ||= []) << generate_message(message, values)
233
267
  end
@@ -249,9 +283,9 @@ class EventCollectionWithValidationsTest < Test::Unit::TestCase
249
283
  @machine = StateMachine::Machine.new(@klass, :initial => :parked, :action => :save, :integration => :custom)
250
284
  @events = StateMachine::EventCollection.new(@machine)
251
285
 
252
- @machine.event :ignite
253
286
  @parked, @idling = @machine.state :parked, :idling
254
287
  @events << @ignite = StateMachine::Event.new(@machine, :ignite)
288
+ @machine.events.concat(@events)
255
289
 
256
290
  @object = @klass.new
257
291
  end
@@ -303,9 +337,9 @@ class EventCollectionWithCustomMachineAttributeTest < Test::Unit::TestCase
303
337
  @machine = StateMachine::Machine.new(@klass, :state, :attribute => :state_id, :initial => :parked, :action => :save)
304
338
  @events = StateMachine::EventCollection.new(@machine)
305
339
 
306
- @machine.event :ignite
307
340
  @machine.state :parked, :idling
308
341
  @events << @ignite = StateMachine::Event.new(@machine, :ignite)
342
+ @machine.events.concat(@events)
309
343
 
310
344
  @object = @klass.new
311
345
  end
@@ -4,7 +4,7 @@ class EventByDefaultTest < Test::Unit::TestCase
4
4
  def setup
5
5
  @klass = Class.new
6
6
  @machine = StateMachine::Machine.new(@klass)
7
- @event = StateMachine::Event.new(@machine, :ignite)
7
+ @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
8
8
 
9
9
  @object = @klass.new
10
10
  end
@@ -25,8 +25,8 @@ class EventByDefaultTest < Test::Unit::TestCase
25
25
  assert_equal 'ignite', @event.human_name
26
26
  end
27
27
 
28
- def test_should_not_have_any_guards
29
- assert @event.guards.empty?
28
+ def test_should_not_have_any_branches
29
+ assert @event.branches.empty?
30
30
  end
31
31
 
32
32
  def test_should_have_no_known_states
@@ -61,7 +61,7 @@ end
61
61
  class EventTest < Test::Unit::TestCase
62
62
  def setup
63
63
  @machine = StateMachine::Machine.new(Class.new)
64
- @event = StateMachine::Event.new(@machine, :ignite)
64
+ @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
65
65
  @event.transition :parked => :idling
66
66
  end
67
67
 
@@ -95,7 +95,7 @@ class EventWithHumanNameTest < Test::Unit::TestCase
95
95
  def setup
96
96
  @klass = Class.new
97
97
  @machine = StateMachine::Machine.new(@klass)
98
- @event = StateMachine::Event.new(@machine, :ignite, :human_name => 'start')
98
+ @machine.events << @event = StateMachine::Event.new(@machine, :ignite, :human_name => 'start')
99
99
  end
100
100
 
101
101
  def test_should_use_custom_human_name
@@ -107,7 +107,7 @@ class EventWithDynamicHumanNameTest < Test::Unit::TestCase
107
107
  def setup
108
108
  @klass = Class.new
109
109
  @machine = StateMachine::Machine.new(@klass)
110
- @event = StateMachine::Event.new(@machine, :ignite, :human_name => lambda {|event, object| ['start', object]})
110
+ @machine.events << @event = StateMachine::Event.new(@machine, :ignite, :human_name => lambda {|event, object| ['start', object]})
111
111
  end
112
112
 
113
113
  def test_should_use_custom_human_name
@@ -147,7 +147,7 @@ class EventWithConflictingHelpersTest < Test::Unit::TestCase
147
147
  end
148
148
  end
149
149
  @machine = StateMachine::Machine.new(@klass)
150
- @state = StateMachine::Event.new(@machine, :ignite)
150
+ @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
151
151
  @object = @klass.new
152
152
  end
153
153
 
@@ -198,11 +198,58 @@ class EventWithConflictingHelpersTest < Test::Unit::TestCase
198
198
  end
199
199
  end
200
200
 
201
+ class EventWithConflictingMachineTest < Test::Unit::TestCase
202
+ def setup
203
+ require 'stringio'
204
+ @original_stderr, $stderr = $stderr, StringIO.new
205
+
206
+ @klass = Class.new
207
+ @state_machine = StateMachine::Machine.new(@klass, :state)
208
+ @state_machine.state :parked, :idling
209
+ @state_machine.events << @state_event = StateMachine::Event.new(@state_machine, :ignite)
210
+ end
211
+
212
+ def test_should_not_overwrite_first_event
213
+ @status_machine = StateMachine::Machine.new(@klass, :status)
214
+ @status_machine.state :first_gear, :second_gear
215
+ @status_machine.events << @status_event = StateMachine::Event.new(@status_machine, :ignite)
216
+
217
+ @object = @klass.new
218
+ @object.state = 'parked'
219
+ @object.status = 'first_gear'
220
+
221
+ @state_event.transition(:parked => :idling)
222
+ @status_event.transition(:parked => :first_gear)
223
+
224
+ @object.ignite
225
+ assert_equal 'idling', @object.state
226
+ assert_equal 'first_gear', @object.status
227
+ end
228
+
229
+ def test_should_output_warning
230
+ @status_machine = StateMachine::Machine.new(@klass, :status)
231
+ @status_machine.events << @status_event = StateMachine::Event.new(@status_machine, :ignite)
232
+
233
+ assert_equal "Event :ignite for :status is already defined in :state\n", $stderr.string
234
+ end
235
+
236
+ def test_should_not_output_warning_if_using_different_namespace
237
+ @status_machine = StateMachine::Machine.new(@klass, :status, :namespace => 'alarm')
238
+ @status_machine.events << @status_event = StateMachine::Event.new(@status_machine, :ignite)
239
+
240
+ assert_equal '', $stderr.string
241
+ end
242
+
243
+ def teardown
244
+ $stderr = @original_stderr
245
+ end
246
+ end
247
+
201
248
  class EventWithNamespaceTest < Test::Unit::TestCase
202
249
  def setup
203
250
  @klass = Class.new
204
251
  @machine = StateMachine::Machine.new(@klass, :namespace => 'alarm')
205
- @event = StateMachine::Event.new(@machine, :enable)
252
+ @machine.events << @event = StateMachine::Event.new(@machine, :enable)
206
253
  @object = @klass.new
207
254
  end
208
255
 
@@ -234,7 +281,7 @@ end
234
281
  class EventTransitionsTest < Test::Unit::TestCase
235
282
  def setup
236
283
  @machine = StateMachine::Machine.new(Class.new)
237
- @event = StateMachine::Event.new(@machine, :ignite)
284
+ @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
238
285
  end
239
286
 
240
287
  def test_should_not_raise_exception_if_implicit_option_specified
@@ -247,9 +294,9 @@ class EventTransitionsTest < Test::Unit::TestCase
247
294
  end
248
295
 
249
296
  def test_should_automatically_set_on_option
250
- guard = @event.transition(:to => :idling)
251
- assert_instance_of StateMachine::WhitelistMatcher, guard.event_requirement
252
- assert_equal [:ignite], guard.event_requirement.values
297
+ branch = @event.transition(:to => :idling)
298
+ assert_instance_of StateMachine::WhitelistMatcher, branch.event_requirement
299
+ assert_equal [:ignite], branch.event_requirement.values
253
300
  end
254
301
 
255
302
  def test_should_not_allow_except_to_option
@@ -283,20 +330,20 @@ class EventTransitionsTest < Test::Unit::TestCase
283
330
  end
284
331
 
285
332
  def test_should_have_transitions
286
- guard = @event.transition(:to => :idling)
287
- assert_equal [guard], @event.guards
333
+ branch = @event.transition(:to => :idling)
334
+ assert_equal [branch], @event.branches
288
335
  end
289
336
  end
290
337
 
291
338
  class EventAfterBeingCopiedTest < Test::Unit::TestCase
292
339
  def setup
293
340
  @machine = StateMachine::Machine.new(Class.new)
294
- @event = StateMachine::Event.new(@machine, :ignite)
341
+ @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
295
342
  @copied_event = @event.dup
296
343
  end
297
344
 
298
- def test_should_not_have_the_same_collection_of_guards
299
- assert_not_same @event.guards, @copied_event.guards
345
+ def test_should_not_have_the_same_collection_of_branches
346
+ assert_not_same @event.branches, @copied_event.branches
300
347
  end
301
348
 
302
349
  def test_should_not_have_the_same_collection_of_known_states
@@ -308,7 +355,7 @@ class EventWithoutTransitionsTest < Test::Unit::TestCase
308
355
  def setup
309
356
  @klass = Class.new
310
357
  @machine = StateMachine::Machine.new(@klass)
311
- @event = StateMachine::Event.new(@machine, :ignite)
358
+ @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
312
359
  @object = @klass.new
313
360
  end
314
361
 
@@ -334,7 +381,7 @@ class EventWithTransitionsTest < Test::Unit::TestCase
334
381
  def setup
335
382
  @klass = Class.new
336
383
  @machine = StateMachine::Machine.new(@klass)
337
- @event = StateMachine::Event.new(@machine, :ignite)
384
+ @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
338
385
  @event.transition(:parked => :idling)
339
386
  @event.transition(:first_gear => :idling)
340
387
  end
@@ -361,7 +408,7 @@ class EventWithoutMatchingTransitionsTest < Test::Unit::TestCase
361
408
  @machine = StateMachine::Machine.new(@klass)
362
409
  @machine.state :parked, :idling
363
410
 
364
- @event = StateMachine::Event.new(@machine, :ignite)
411
+ @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
365
412
  @event.transition(:parked => :idling)
366
413
 
367
414
  @object = @klass.new
@@ -372,10 +419,18 @@ class EventWithoutMatchingTransitionsTest < Test::Unit::TestCase
372
419
  assert !@event.can_fire?(@object)
373
420
  end
374
421
 
422
+ def test_should_be_able_to_fire_with_custom_from_state
423
+ assert @event.can_fire?(@object, :from => :parked)
424
+ end
425
+
375
426
  def test_should_not_have_a_transition
376
427
  assert_nil @event.transition_for(@object)
377
428
  end
378
429
 
430
+ def test_should_have_a_transition_with_custom_from_state
431
+ assert_not_nil @event.transition_for(@object, :from => :parked)
432
+ end
433
+
379
434
  def test_should_not_fire
380
435
  assert !@event.fire(@object)
381
436
  end
@@ -389,6 +444,8 @@ end
389
444
  class EventWithMatchingDisabledTransitionsTest < Test::Unit::TestCase
390
445
  def setup
391
446
  StateMachine::Integrations.const_set('Custom', Module.new do
447
+ include StateMachine::Integrations::Base
448
+
392
449
  def invalidate(object, attribute, message, values = [])
393
450
  (object.errors ||= []) << generate_message(message, values)
394
451
  end
@@ -405,7 +462,7 @@ class EventWithMatchingDisabledTransitionsTest < Test::Unit::TestCase
405
462
  @machine = StateMachine::Machine.new(@klass, :integration => :custom)
406
463
  @machine.state :parked, :idling
407
464
 
408
- @event = StateMachine::Event.new(@machine, :ignite)
465
+ @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
409
466
  @event.transition(:parked => :idling, :if => lambda {false})
410
467
 
411
468
  @object = @klass.new
@@ -416,10 +473,18 @@ class EventWithMatchingDisabledTransitionsTest < Test::Unit::TestCase
416
473
  assert !@event.can_fire?(@object)
417
474
  end
418
475
 
476
+ def test_should_be_able_to_fire_with_disabled_guards
477
+ assert @event.can_fire?(@object, :guard => false)
478
+ end
479
+
419
480
  def test_should_not_have_a_transition
420
481
  assert_nil @event.transition_for(@object)
421
482
  end
422
483
 
484
+ def test_should_have_a_transition_with_disabled_guards
485
+ assert_not_nil @event.transition_for(@object, :guard => false)
486
+ end
487
+
423
488
  def test_should_not_fire
424
489
  assert !@event.fire(@object)
425
490
  end
@@ -447,6 +512,22 @@ class EventWithMatchingDisabledTransitionsTest < Test::Unit::TestCase
447
512
  assert_equal ['cannot transition via "ignite"'], @object.errors
448
513
  end
449
514
 
515
+ def test_should_run_failure_callbacks
516
+ callback_args = nil
517
+ @machine.after_failure {|*args| callback_args = args}
518
+
519
+ @event.fire(@object)
520
+
521
+ object, transition = callback_args
522
+ assert_equal @object, object
523
+ assert_not_nil transition
524
+ assert_equal @object, transition.object
525
+ assert_equal @machine, transition.machine
526
+ assert_equal :ignite, transition.event
527
+ assert_equal :parked, transition.from_name
528
+ assert_equal :parked, transition.to_name
529
+ end
530
+
450
531
  def teardown
451
532
  StateMachine::Integrations.send(:remove_const, 'Custom')
452
533
  end
@@ -455,6 +536,8 @@ end
455
536
  class EventWithMatchingEnabledTransitionsTest < Test::Unit::TestCase
456
537
  def setup
457
538
  StateMachine::Integrations.const_set('Custom', Module.new do
539
+ include StateMachine::Integrations::Base
540
+
458
541
  def invalidate(object, attribute, message, values = [])
459
542
  (object.errors ||= []) << generate_message(message, values)
460
543
  end
@@ -470,9 +553,8 @@ class EventWithMatchingEnabledTransitionsTest < Test::Unit::TestCase
470
553
 
471
554
  @machine = StateMachine::Machine.new(@klass, :integration => :custom)
472
555
  @machine.state :parked, :idling
473
- @machine.event :ignite
474
556
 
475
- @event = StateMachine::Event.new(@machine, :ignite)
557
+ @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
476
558
  @event.transition(:parked => :idling)
477
559
 
478
560
  @object = @klass.new
@@ -522,9 +604,8 @@ class EventWithTransitionWithoutToStateTest < Test::Unit::TestCase
522
604
  @klass = Class.new
523
605
  @machine = StateMachine::Machine.new(@klass)
524
606
  @machine.state :parked
525
- @machine.event :park
526
607
 
527
- @event = StateMachine::Event.new(@machine, :park)
608
+ @machine.events << @event = StateMachine::Event.new(@machine, :park)
528
609
  @event.transition(:from => :parked)
529
610
 
530
611
  @object = @klass.new
@@ -558,9 +639,8 @@ class EventWithTransitionWithNilToStateTest < Test::Unit::TestCase
558
639
  @klass = Class.new
559
640
  @machine = StateMachine::Machine.new(@klass)
560
641
  @machine.state nil, :idling
561
- @machine.event :park
562
642
 
563
- @event = StateMachine::Event.new(@machine, :park)
643
+ @machine.events << @event = StateMachine::Event.new(@machine, :park)
564
644
  @event.transition(:idling => nil)
565
645
 
566
646
  @object = @klass.new
@@ -594,9 +674,8 @@ class EventWithMultipleTransitionsTest < Test::Unit::TestCase
594
674
  @klass = Class.new
595
675
  @machine = StateMachine::Machine.new(@klass)
596
676
  @machine.state :parked, :idling
597
- @machine.event :ignite
598
677
 
599
- @event = StateMachine::Event.new(@machine, :ignite)
678
+ @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
600
679
  @event.transition(:idling => :idling)
601
680
  @event.transition(:parked => :idling) # This one should get used
602
681
  @event.transition(:parked => :parked)
@@ -635,12 +714,9 @@ class EventWithMultipleTransitionsTest < Test::Unit::TestCase
635
714
  assert_equal :ignite, transition.event
636
715
  end
637
716
 
638
- def test_should_allow_specific_transition_selection_using_on
639
- transition = @event.transition_for(@object, :on => :park)
640
- assert_nil transition
641
-
642
- transition = @event.transition_for(@object, :on => :ignite)
643
- assert_not_nil transition
717
+ def test_should_not_allow_specific_transition_selection_using_on
718
+ exception = assert_raise(ArgumentError) { @event.transition_for(@object, :on => :park) }
719
+ assert_equal 'Invalid key(s): on', exception.message
644
720
  end
645
721
 
646
722
  def test_should_fire
@@ -689,9 +765,8 @@ class EventWithInvalidCurrentStateTest < Test::Unit::TestCase
689
765
  @klass = Class.new
690
766
  @machine = StateMachine::Machine.new(@klass)
691
767
  @machine.state :parked, :idling
692
- @machine.event :ignite
693
768
 
694
- @event = StateMachine::Event.new(@machine, :ignite)
769
+ @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
695
770
  @event.transition(:parked => :idling)
696
771
 
697
772
  @object = @klass.new
@@ -714,6 +789,58 @@ class EventWithInvalidCurrentStateTest < Test::Unit::TestCase
714
789
  end
715
790
  end
716
791
 
792
+ class EventOnFailureTest < Test::Unit::TestCase
793
+ def setup
794
+ StateMachine::Integrations.const_set('Custom', Module.new do
795
+ include StateMachine::Integrations::Base
796
+
797
+ def invalidate(object, attribute, message, values = [])
798
+ (object.errors ||= []) << generate_message(message, values)
799
+ end
800
+
801
+ def reset(object)
802
+ object.errors = []
803
+ end
804
+ end)
805
+
806
+ @klass = Class.new do
807
+ attr_accessor :errors
808
+ end
809
+
810
+ @machine = StateMachine::Machine.new(@klass, :integration => :custom)
811
+ @machine.state :parked
812
+ @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
813
+
814
+ @object = @klass.new
815
+ @object.state = 'parked'
816
+ end
817
+
818
+ def test_should_invalidate_the_state
819
+ @event.fire(@object)
820
+ assert_equal ['cannot transition via "ignite"'], @object.errors
821
+ end
822
+
823
+ def test_should_run_failure_callbacks
824
+ callback_args = nil
825
+ @machine.after_failure {|*args| callback_args = args}
826
+
827
+ @event.fire(@object)
828
+
829
+ object, transition = callback_args
830
+ assert_equal @object, object
831
+ assert_not_nil transition
832
+ assert_equal @object, transition.object
833
+ assert_equal @machine, transition.machine
834
+ assert_equal :ignite, transition.event
835
+ assert_equal :parked, transition.from_name
836
+ assert_equal :parked, transition.to_name
837
+ end
838
+
839
+ def teardown
840
+ StateMachine::Integrations.send(:remove_const, 'Custom')
841
+ end
842
+ end
843
+
717
844
  class EventWithMarshallingTest < Test::Unit::TestCase
718
845
  def setup
719
846
  @klass = Class.new do
@@ -774,7 +901,7 @@ begin
774
901
  graph = GraphViz.new('G')
775
902
  states.each {|state| graph.add_node(state.to_s)}
776
903
 
777
- @event = StateMachine::Event.new(@machine , :park)
904
+ @machine.events << @event = StateMachine::Event.new(@machine , :park)
778
905
  @event.transition :parked => :idling
779
906
  @event.transition :first_gear => :parked
780
907
  @event.transition :except_from => :parked, :to => :parked