pluginaweek-state_machine 0.7.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. data/CHANGELOG.rdoc +273 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +466 -0
  4. data/Rakefile +98 -0
  5. data/examples/AutoShop_state.png +0 -0
  6. data/examples/Car_state.png +0 -0
  7. data/examples/TrafficLight_state.png +0 -0
  8. data/examples/Vehicle_state.png +0 -0
  9. data/examples/auto_shop.rb +11 -0
  10. data/examples/car.rb +19 -0
  11. data/examples/merb-rest/controller.rb +51 -0
  12. data/examples/merb-rest/model.rb +28 -0
  13. data/examples/merb-rest/view_edit.html.erb +24 -0
  14. data/examples/merb-rest/view_index.html.erb +23 -0
  15. data/examples/merb-rest/view_new.html.erb +13 -0
  16. data/examples/merb-rest/view_show.html.erb +17 -0
  17. data/examples/rails-rest/controller.rb +43 -0
  18. data/examples/rails-rest/migration.rb +11 -0
  19. data/examples/rails-rest/model.rb +23 -0
  20. data/examples/rails-rest/view_edit.html.erb +25 -0
  21. data/examples/rails-rest/view_index.html.erb +23 -0
  22. data/examples/rails-rest/view_new.html.erb +14 -0
  23. data/examples/rails-rest/view_show.html.erb +17 -0
  24. data/examples/traffic_light.rb +7 -0
  25. data/examples/vehicle.rb +31 -0
  26. data/init.rb +1 -0
  27. data/lib/state_machine.rb +429 -0
  28. data/lib/state_machine/assertions.rb +36 -0
  29. data/lib/state_machine/callback.rb +189 -0
  30. data/lib/state_machine/condition_proxy.rb +94 -0
  31. data/lib/state_machine/eval_helpers.rb +67 -0
  32. data/lib/state_machine/event.rb +251 -0
  33. data/lib/state_machine/event_collection.rb +113 -0
  34. data/lib/state_machine/extensions.rb +158 -0
  35. data/lib/state_machine/guard.rb +219 -0
  36. data/lib/state_machine/integrations.rb +68 -0
  37. data/lib/state_machine/integrations/active_record.rb +444 -0
  38. data/lib/state_machine/integrations/active_record/locale.rb +10 -0
  39. data/lib/state_machine/integrations/active_record/observer.rb +41 -0
  40. data/lib/state_machine/integrations/data_mapper.rb +325 -0
  41. data/lib/state_machine/integrations/data_mapper/observer.rb +139 -0
  42. data/lib/state_machine/integrations/sequel.rb +292 -0
  43. data/lib/state_machine/machine.rb +1431 -0
  44. data/lib/state_machine/machine_collection.rb +146 -0
  45. data/lib/state_machine/matcher.rb +123 -0
  46. data/lib/state_machine/matcher_helpers.rb +54 -0
  47. data/lib/state_machine/node_collection.rb +152 -0
  48. data/lib/state_machine/state.rb +249 -0
  49. data/lib/state_machine/state_collection.rb +112 -0
  50. data/lib/state_machine/transition.rb +367 -0
  51. data/tasks/state_machine.rake +1 -0
  52. data/tasks/state_machine.rb +30 -0
  53. data/test/classes/switch.rb +11 -0
  54. data/test/functional/state_machine_test.rb +941 -0
  55. data/test/test_helper.rb +4 -0
  56. data/test/unit/assertions_test.rb +40 -0
  57. data/test/unit/callback_test.rb +455 -0
  58. data/test/unit/condition_proxy_test.rb +328 -0
  59. data/test/unit/eval_helpers_test.rb +129 -0
  60. data/test/unit/event_collection_test.rb +293 -0
  61. data/test/unit/event_test.rb +605 -0
  62. data/test/unit/guard_test.rb +862 -0
  63. data/test/unit/integrations/active_record_test.rb +1001 -0
  64. data/test/unit/integrations/data_mapper_test.rb +694 -0
  65. data/test/unit/integrations/sequel_test.rb +486 -0
  66. data/test/unit/integrations_test.rb +42 -0
  67. data/test/unit/invalid_event_test.rb +7 -0
  68. data/test/unit/invalid_transition_test.rb +7 -0
  69. data/test/unit/machine_collection_test.rb +710 -0
  70. data/test/unit/machine_test.rb +1910 -0
  71. data/test/unit/matcher_helpers_test.rb +37 -0
  72. data/test/unit/matcher_test.rb +155 -0
  73. data/test/unit/node_collection_test.rb +207 -0
  74. data/test/unit/state_collection_test.rb +280 -0
  75. data/test/unit/state_machine_test.rb +31 -0
  76. data/test/unit/state_test.rb +795 -0
  77. data/test/unit/transition_test.rb +1113 -0
  78. metadata +161 -0
@@ -0,0 +1,293 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ class EventCollectionByDefaultTest < Test::Unit::TestCase
4
+ def setup
5
+ @machine = StateMachine::Machine.new(Class.new)
6
+ @events = StateMachine::EventCollection.new(@machine)
7
+ end
8
+
9
+ def test_should_not_have_any_nodes
10
+ assert_equal 0, @events.length
11
+ end
12
+
13
+ def test_should_have_a_machine
14
+ assert_equal @machine, @events.machine
15
+ end
16
+
17
+ def test_should_not_have_any_valid_events_for_an_object
18
+ assert @events.valid_for(@object).empty?
19
+ end
20
+
21
+ def test_should_not_have_any_transitions_for_an_object
22
+ assert @events.transitions_for(@object).empty?
23
+ end
24
+ end
25
+
26
+ class EventCollectionTest < Test::Unit::TestCase
27
+ def setup
28
+ machine = StateMachine::Machine.new(Class.new, :namespace => 'alarm')
29
+ @events = StateMachine::EventCollection.new(machine)
30
+
31
+ @events << @open = StateMachine::Event.new(machine, :enable)
32
+ end
33
+
34
+ def test_should_index_by_name
35
+ assert_equal @open, @events[:enable, :name]
36
+ end
37
+
38
+ def test_should_index_by_name_by_default
39
+ assert_equal @open, @events[:enable]
40
+ end
41
+
42
+ def test_should_index_by_qualified_name
43
+ assert_equal @open, @events[:enable_alarm, :qualified_name]
44
+ end
45
+ end
46
+
47
+ class EventCollectionWithEventsWithTransitionsTest < Test::Unit::TestCase
48
+ def setup
49
+ @klass = Class.new
50
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
51
+ @events = StateMachine::EventCollection.new(@machine)
52
+
53
+ @machine.state :idling, :stalled
54
+ @machine.event :ignite
55
+
56
+ @events << @ignite = StateMachine::Event.new(@machine, :ignite)
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
+
66
+ object.state = 'stalled'
67
+ assert_equal [@ignite], @events.valid_for(object)
68
+
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}
77
+
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}
80
+
81
+ object.state = 'idling'
82
+ assert_equal [], @events.transitions_for(object)
83
+ end
84
+ end
85
+
86
+ class EventCollectionWithMultipleEventsTest < Test::Unit::TestCase
87
+ def setup
88
+ @klass = Class.new
89
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
90
+ @events = StateMachine::EventCollection.new(@machine)
91
+
92
+ @machine.state :first_gear
93
+ @machine.event :park, :shift_down
94
+
95
+ @events << @park = StateMachine::Event.new(@machine, :park)
96
+ @park.transition :first_gear => :parked
97
+
98
+ @events << @shift_down = StateMachine::Event.new(@machine, :shift_down)
99
+ @shift_down.transition :first_gear => :parked
100
+ end
101
+
102
+ def test_should_only_include_all_valid_events_for_an_object
103
+ object = @klass.new
104
+ object.state = 'first_gear'
105
+ assert_equal [@park, @shift_down], @events.valid_for(object)
106
+ end
107
+ end
108
+
109
+ class EventCollectionWithoutMachineActionTest < Test::Unit::TestCase
110
+ def setup
111
+ @klass = Class.new
112
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
113
+ @events = StateMachine::EventCollection.new(@machine)
114
+
115
+ @machine.event :ignite
116
+ @events << StateMachine::Event.new(@machine, :ignite)
117
+
118
+ @object = @klass.new
119
+ end
120
+
121
+ def test_should_not_have_an_attribute_transition
122
+ assert_nil @events.attribute_transition_for(@object)
123
+ end
124
+ end
125
+
126
+ class EventCollectionAttributeWithMachineActionTest < Test::Unit::TestCase
127
+ def setup
128
+ @klass = Class.new do
129
+ def save
130
+ end
131
+ end
132
+
133
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked, :action => :save)
134
+ @events = StateMachine::EventCollection.new(@machine)
135
+
136
+ @machine.event :ignite
137
+ @machine.state :parked, :idling
138
+ @events << @ignite = StateMachine::Event.new(@machine, :ignite)
139
+
140
+ @object = @klass.new
141
+ end
142
+
143
+ def test_should_not_have_transition_if_nil
144
+ @object.state_event = nil
145
+ assert_nil @events.attribute_transition_for(@object)
146
+ end
147
+
148
+ def test_should_not_have_transition_if_empty
149
+ @object.state_event = ''
150
+ assert_nil @events.attribute_transition_for(@object)
151
+ end
152
+
153
+ def test_should_have_invalid_transition_if_invalid_event_specified
154
+ @object.state_event = 'invalid'
155
+ assert_equal false, @events.attribute_transition_for(@object)
156
+ end
157
+
158
+ def test_should_have_invalid_transition_if_event_cannot_be_fired
159
+ @object.state_event = 'ignite'
160
+ assert_equal false, @events.attribute_transition_for(@object)
161
+ end
162
+
163
+ def test_should_have_valid_transition_if_event_can_be_fired
164
+ @ignite.transition :parked => :idling
165
+ @object.state_event = 'ignite'
166
+
167
+ assert_instance_of StateMachine::Transition, @events.attribute_transition_for(@object)
168
+ end
169
+ end
170
+
171
+ class EventCollectionAttributeWithNamespacedMachineTest < Test::Unit::TestCase
172
+ def setup
173
+ @klass = Class.new do
174
+ def save
175
+ end
176
+ end
177
+
178
+ @machine = StateMachine::Machine.new(@klass, :namespace => 'alarm', :initial => :active, :action => :save)
179
+ @events = StateMachine::EventCollection.new(@machine)
180
+
181
+ @machine.event :disable
182
+ @machine.state :active, :off
183
+ @events << @disable = StateMachine::Event.new(@machine, :disable)
184
+
185
+ @object = @klass.new
186
+ end
187
+
188
+ def test_should_not_have_transition_if_nil
189
+ @object.state_event = nil
190
+ assert_nil @events.attribute_transition_for(@object)
191
+ end
192
+
193
+ def test_should_have_invalid_transition_if_event_cannot_be_fired
194
+ @object.state_event = 'disable'
195
+ assert_equal false, @events.attribute_transition_for(@object)
196
+ end
197
+
198
+ def test_should_have_valid_transition_if_event_can_be_fired
199
+ @disable.transition :active => :off
200
+ @object.state_event = 'disable'
201
+
202
+ assert_instance_of StateMachine::Transition, @events.attribute_transition_for(@object)
203
+ end
204
+ end
205
+
206
+ class EventCollectionWithValidationsTest < Test::Unit::TestCase
207
+ def setup
208
+ StateMachine::Integrations.const_set('Custom', Module.new do
209
+ def invalidate(object, attribute, message, values = [])
210
+ (object.errors ||= []) << generate_message(message, values)
211
+ end
212
+
213
+ def reset(object)
214
+ object.errors = []
215
+ end
216
+ end)
217
+
218
+ @klass = Class.new do
219
+ attr_accessor :errors
220
+
221
+ def initialize
222
+ @errors = []
223
+ super
224
+ end
225
+ end
226
+
227
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked, :action => :save, :integration => :custom)
228
+ @events = StateMachine::EventCollection.new(@machine)
229
+
230
+ @machine.event :ignite
231
+ @machine.state :parked, :idling
232
+ @events << @ignite = StateMachine::Event.new(@machine, :ignite)
233
+
234
+ @object = @klass.new
235
+ end
236
+
237
+ def test_should_invalidate_if_invalid_event_specified
238
+ @object.state_event = 'invalid'
239
+ @events.attribute_transition_for(@object, true)
240
+
241
+ assert_equal ['is invalid'], @object.errors
242
+ end
243
+
244
+ def test_should_invalidate_if_event_cannot_be_fired
245
+ @object.state = 'idling'
246
+ @object.state_event = 'ignite'
247
+ @events.attribute_transition_for(@object, true)
248
+
249
+ assert_equal ['cannot transition when idling'], @object.errors
250
+ end
251
+
252
+ def test_should_not_invalidate_event_can_be_fired
253
+ @ignite.transition :parked => :idling
254
+ @object.state_event = 'ignite'
255
+ @events.attribute_transition_for(@object, true)
256
+
257
+ assert_equal [], @object.errors
258
+ end
259
+
260
+ def teardown
261
+ StateMachine::Integrations.send(:remove_const, 'Custom')
262
+ end
263
+ end
264
+
265
+ class EventCollectionWithCustomMachineNameTest < Test::Unit::TestCase
266
+ def setup
267
+ @klass = Class.new do
268
+ def save
269
+ end
270
+ end
271
+
272
+ @machine = StateMachine::Machine.new(@klass, :state_id, :as => 'state', :initial => :parked, :action => :save)
273
+ @events = StateMachine::EventCollection.new(@machine)
274
+
275
+ @machine.event :ignite
276
+ @machine.state :parked, :idling
277
+ @events << @ignite = StateMachine::Event.new(@machine, :ignite)
278
+
279
+ @object = @klass.new
280
+ end
281
+
282
+ def test_should_not_have_transition_if_nil
283
+ @object.state_event = nil
284
+ assert_nil @events.attribute_transition_for(@object)
285
+ end
286
+
287
+ def test_should_have_valid_transition_if_event_can_be_fired
288
+ @ignite.transition :parked => :idling
289
+ @object.state_event = 'ignite'
290
+
291
+ assert_instance_of StateMachine::Transition, @events.attribute_transition_for(@object)
292
+ end
293
+ end
@@ -0,0 +1,605 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ class EventByDefaultTest < Test::Unit::TestCase
4
+ def setup
5
+ @klass = Class.new
6
+ @machine = StateMachine::Machine.new(@klass)
7
+ @event = StateMachine::Event.new(@machine, :ignite)
8
+
9
+ @object = @klass.new
10
+ end
11
+
12
+ def test_should_have_a_machine
13
+ assert_equal @machine, @event.machine
14
+ end
15
+
16
+ def test_should_have_a_name
17
+ assert_equal :ignite, @event.name
18
+ end
19
+
20
+ def test_should_have_a_qualified_name
21
+ assert_equal :ignite, @event.qualified_name
22
+ end
23
+
24
+ def test_should_not_have_any_guards
25
+ assert @event.guards.empty?
26
+ end
27
+
28
+ def test_should_have_no_known_states
29
+ assert @event.known_states.empty?
30
+ end
31
+
32
+ def test_should_not_be_able_to_fire
33
+ assert !@event.can_fire?(@object)
34
+ end
35
+
36
+ def test_should_not_have_a_transition
37
+ assert_nil @event.transition_for(@object)
38
+ end
39
+
40
+ def test_should_define_a_predicate
41
+ assert @object.respond_to?(:can_ignite?)
42
+ end
43
+
44
+ def test_should_define_a_transition_accessor
45
+ assert @object.respond_to?(:ignite_transition)
46
+ end
47
+
48
+ def test_should_define_an_action
49
+ assert @object.respond_to?(:ignite)
50
+ end
51
+
52
+ def test_should_define_a_bang_action
53
+ assert @object.respond_to?(:ignite!)
54
+ end
55
+ end
56
+
57
+ class EventTest < Test::Unit::TestCase
58
+ def setup
59
+ @machine = StateMachine::Machine.new(Class.new)
60
+ @event = StateMachine::Event.new(@machine, :ignite)
61
+ @event.transition :parked => :idling
62
+ end
63
+
64
+ def test_should_allow_changing_machine
65
+ new_machine = StateMachine::Machine.new(Class.new)
66
+ @event.machine = new_machine
67
+ assert_equal new_machine, @event.machine
68
+ end
69
+
70
+ def test_should_provide_matcher_helpers_during_initialization
71
+ matchers = []
72
+
73
+ @event.instance_eval do
74
+ matchers = [all, any, same]
75
+ end
76
+
77
+ assert_equal [StateMachine::AllMatcher.instance, StateMachine::AllMatcher.instance, StateMachine::LoopbackMatcher.instance], matchers
78
+ end
79
+
80
+ def test_should_use_pretty_inspect
81
+ assert_match "#<StateMachine::Event name=:ignite transitions=[:parked => :idling]>", @event.inspect
82
+ end
83
+ end
84
+
85
+ class EventWithConflictingHelpersTest < Test::Unit::TestCase
86
+ def setup
87
+ @klass = Class.new do
88
+ def can_ignite?
89
+ 0
90
+ end
91
+
92
+ def ignite_transition
93
+ 0
94
+ end
95
+
96
+ def ignite
97
+ 0
98
+ end
99
+
100
+ def ignite!
101
+ 0
102
+ end
103
+ end
104
+ @machine = StateMachine::Machine.new(@klass)
105
+ @state = StateMachine::Event.new(@machine, :ignite)
106
+ @object = @klass.new
107
+ end
108
+
109
+ def test_should_not_redefine_predicate
110
+ assert_equal 0, @object.can_ignite?
111
+ end
112
+
113
+ def test_should_not_redefine_transition_accessor
114
+ assert_equal 0, @object.ignite_transition
115
+ end
116
+
117
+ def test_should_not_redefine_action
118
+ assert_equal 0, @object.ignite
119
+ end
120
+
121
+ def test_should_not_redefine_bang_action
122
+ assert_equal 0, @object.ignite!
123
+ end
124
+
125
+ def test_should_allow_super_chaining
126
+ @klass.class_eval do
127
+ def can_ignite?
128
+ super ? 1 : 0
129
+ end
130
+
131
+ def ignite_transition
132
+ super ? 1 : 0
133
+ end
134
+
135
+ def ignite
136
+ super ? 1 : 0
137
+ end
138
+
139
+ def ignite!
140
+ begin
141
+ super
142
+ 1
143
+ rescue Exception => ex
144
+ 0
145
+ end
146
+ end
147
+ end
148
+
149
+ assert_equal 0, @object.can_ignite?
150
+ assert_equal 0, @object.ignite_transition
151
+ assert_equal 0, @object.ignite
152
+ assert_equal 1, @object.ignite!
153
+ end
154
+ end
155
+
156
+ class EventWithNamespaceTest < Test::Unit::TestCase
157
+ def setup
158
+ @klass = Class.new
159
+ @machine = StateMachine::Machine.new(@klass, :namespace => 'alarm')
160
+ @event = StateMachine::Event.new(@machine, :enable)
161
+ @object = @klass.new
162
+ end
163
+
164
+ def test_should_have_a_name
165
+ assert_equal :enable, @event.name
166
+ end
167
+
168
+ def test_should_have_a_qualified_name
169
+ assert_equal :enable_alarm, @event.qualified_name
170
+ end
171
+
172
+ def test_should_namespace_predicate
173
+ assert @object.respond_to?(:can_enable_alarm?)
174
+ end
175
+
176
+ def test_should_namespace_transition_accessor
177
+ assert @object.respond_to?(:enable_alarm_transition)
178
+ end
179
+
180
+ def test_should_namespace_action
181
+ assert @object.respond_to?(:enable_alarm)
182
+ end
183
+
184
+ def test_should_namespace_bang_action
185
+ assert @object.respond_to?(:enable_alarm!)
186
+ end
187
+ end
188
+
189
+ class EventTransitionsTest < Test::Unit::TestCase
190
+ def setup
191
+ @machine = StateMachine::Machine.new(Class.new)
192
+ @event = StateMachine::Event.new(@machine, :ignite)
193
+ end
194
+
195
+ def test_should_not_raise_exception_if_implicit_option_specified
196
+ assert_nothing_raised {@event.transition(:invalid => :valid)}
197
+ end
198
+
199
+ def test_should_not_allow_on_option
200
+ exception = assert_raise(ArgumentError) {@event.transition(:on => :ignite)}
201
+ assert_equal 'Invalid key(s): on', exception.message
202
+ end
203
+
204
+ def test_should_not_allow_except_to_option
205
+ exception = assert_raise(ArgumentError) {@event.transition(:except_to => :parked)}
206
+ assert_equal 'Invalid key(s): except_to', exception.message
207
+ end
208
+
209
+ def test_should_not_allow_except_on_option
210
+ exception = assert_raise(ArgumentError) {@event.transition(:except_on => :ignite)}
211
+ assert_equal 'Invalid key(s): except_on', exception.message
212
+ end
213
+
214
+ def test_should_allow_transitioning_without_a_to_state
215
+ assert_nothing_raised {@event.transition(:from => :parked)}
216
+ end
217
+
218
+ def test_should_allow_transitioning_without_a_from_state
219
+ assert_nothing_raised {@event.transition(:to => :idling)}
220
+ end
221
+
222
+ def test_should_allow_except_from_option
223
+ assert_nothing_raised {@event.transition(:except_from => :idling)}
224
+ end
225
+
226
+ def test_should_allow_transitioning_from_a_single_state
227
+ assert @event.transition(:parked => :idling)
228
+ end
229
+
230
+ def test_should_allow_transitioning_from_multiple_states
231
+ assert @event.transition([:parked, :idling] => :idling)
232
+ end
233
+
234
+ def test_should_have_transitions
235
+ guard = @event.transition(:to => :idling)
236
+ assert_equal [guard], @event.guards
237
+ end
238
+ end
239
+
240
+ class EventAfterBeingCopiedTest < Test::Unit::TestCase
241
+ def setup
242
+ @machine = StateMachine::Machine.new(Class.new)
243
+ @event = StateMachine::Event.new(@machine, :ignite)
244
+ @copied_event = @event.dup
245
+ end
246
+
247
+ def test_should_not_have_the_same_collection_of_guards
248
+ assert_not_same @event.guards, @copied_event.guards
249
+ end
250
+
251
+ def test_should_not_have_the_same_collection_of_known_states
252
+ assert_not_same @event.known_states, @copied_event.known_states
253
+ end
254
+ end
255
+
256
+ class EventWithoutTransitionsTest < Test::Unit::TestCase
257
+ def setup
258
+ @klass = Class.new
259
+ @machine = StateMachine::Machine.new(@klass)
260
+ @event = StateMachine::Event.new(@machine, :ignite)
261
+ @object = @klass.new
262
+ end
263
+
264
+ def test_should_not_be_able_to_fire
265
+ assert !@event.can_fire?(@object)
266
+ end
267
+
268
+ def test_should_not_have_a_transition
269
+ assert_nil @event.transition_for(@object)
270
+ end
271
+
272
+ def test_should_not_fire
273
+ assert !@event.fire(@object)
274
+ end
275
+
276
+ def test_should_not_change_the_current_state
277
+ @event.fire(@object)
278
+ assert_nil @object.state
279
+ end
280
+ end
281
+
282
+ class EventWithTransitionsTest < Test::Unit::TestCase
283
+ def setup
284
+ @klass = Class.new
285
+ @machine = StateMachine::Machine.new(@klass)
286
+ @event = StateMachine::Event.new(@machine, :ignite)
287
+ @event.transition(:parked => :idling)
288
+ @event.transition(:first_gear => :idling)
289
+ end
290
+
291
+ def test_should_include_all_transition_states_in_known_states
292
+ assert_equal [:parked, :idling, :first_gear], @event.known_states
293
+ end
294
+
295
+ def test_should_include_new_transition_states_after_calling_known_states
296
+ @event.known_states
297
+ @event.transition(:stalled => :idling)
298
+
299
+ assert_equal [:parked, :idling, :first_gear, :stalled], @event.known_states
300
+ end
301
+
302
+ def test_should_use_pretty_inspect
303
+ assert_match "#<StateMachine::Event name=:ignite transitions=[:parked => :idling, :first_gear => :idling]>", @event.inspect
304
+ end
305
+ end
306
+
307
+ class EventWithoutMatchingTransitionsTest < Test::Unit::TestCase
308
+ def setup
309
+ @klass = Class.new
310
+ @machine = StateMachine::Machine.new(@klass)
311
+ @machine.state :parked, :idling
312
+
313
+ @event = StateMachine::Event.new(@machine, :ignite)
314
+ @event.transition(:parked => :idling)
315
+
316
+ @object = @klass.new
317
+ @object.state = 'idling'
318
+ end
319
+
320
+ def test_should_not_be_able_to_fire
321
+ assert !@event.can_fire?(@object)
322
+ end
323
+
324
+ def test_should_not_have_a_transition
325
+ assert_nil @event.transition_for(@object)
326
+ end
327
+
328
+ def test_should_not_fire
329
+ assert !@event.fire(@object)
330
+ end
331
+
332
+ def test_should_not_change_the_current_state
333
+ @event.fire(@object)
334
+ assert_equal 'idling', @object.state
335
+ end
336
+ end
337
+
338
+ class EventWithMatchingDisabledTransitionsTest < Test::Unit::TestCase
339
+ def setup
340
+ StateMachine::Integrations.const_set('Custom', Module.new do
341
+ def invalidate(object, attribute, message, values = [])
342
+ (object.errors ||= []) << generate_message(message, values)
343
+ end
344
+
345
+ def reset(object)
346
+ object.errors = []
347
+ end
348
+ end)
349
+
350
+ @klass = Class.new do
351
+ attr_accessor :errors
352
+ end
353
+
354
+ @machine = StateMachine::Machine.new(@klass, :integration => :custom)
355
+ @machine.state :parked, :idling
356
+
357
+ @event = StateMachine::Event.new(@machine, :ignite)
358
+ @event.transition(:parked => :idling, :if => lambda {false})
359
+
360
+ @object = @klass.new
361
+ @object.state = 'parked'
362
+ end
363
+
364
+ def test_should_not_be_able_to_fire
365
+ assert !@event.can_fire?(@object)
366
+ end
367
+
368
+ def test_should_not_have_a_transition
369
+ assert_nil @event.transition_for(@object)
370
+ end
371
+
372
+ def test_should_not_fire
373
+ assert !@event.fire(@object)
374
+ end
375
+
376
+ def test_should_not_change_the_current_state
377
+ @event.fire(@object)
378
+ assert_equal 'parked', @object.state
379
+ end
380
+
381
+ def test_should_invalidate_the_state
382
+ @event.fire(@object)
383
+ assert_equal ['cannot transition via "ignite"'], @object.errors
384
+ end
385
+
386
+ def test_should_reset_existing_error
387
+ @object.errors = ['invalid']
388
+
389
+ @event.fire(@object)
390
+ assert_equal ['cannot transition via "ignite"'], @object.errors
391
+ end
392
+
393
+ def teardown
394
+ StateMachine::Integrations.send(:remove_const, 'Custom')
395
+ end
396
+ end
397
+
398
+ class EventWithMatchingEnabledTransitionsTest < Test::Unit::TestCase
399
+ def setup
400
+ StateMachine::Integrations.const_set('Custom', Module.new do
401
+ def invalidate(object, attribute, message, values = [])
402
+ (object.errors ||= []) << generate_message(message, values)
403
+ end
404
+
405
+ def reset(object)
406
+ object.errors = []
407
+ end
408
+ end)
409
+
410
+ @klass = Class.new do
411
+ attr_accessor :errors
412
+ end
413
+
414
+ @machine = StateMachine::Machine.new(@klass, :integration => :custom)
415
+ @machine.state :parked, :idling
416
+ @machine.event :ignite
417
+
418
+ @event = StateMachine::Event.new(@machine, :ignite)
419
+ @event.transition(:parked => :idling)
420
+
421
+ @object = @klass.new
422
+ @object.state = 'parked'
423
+ end
424
+
425
+ def test_should_be_able_to_fire
426
+ assert @event.can_fire?(@object)
427
+ end
428
+
429
+ def test_should_have_a_transition
430
+ transition = @event.transition_for(@object)
431
+ assert_not_nil transition
432
+ assert_equal 'parked', transition.from
433
+ assert_equal 'idling', transition.to
434
+ assert_equal :ignite, transition.event
435
+ end
436
+
437
+ def test_should_fire
438
+ assert @event.fire(@object)
439
+ end
440
+
441
+ def test_should_change_the_current_state
442
+ @event.fire(@object)
443
+ assert_equal 'idling', @object.state
444
+ end
445
+
446
+ def test_should_reset_existing_error
447
+ @object.errors = ['invalid']
448
+
449
+ @event.fire(@object)
450
+ assert_equal [], @object.errors
451
+ end
452
+
453
+ def test_should_not_invalidate_the_state
454
+ @event.fire(@object)
455
+ assert_equal [], @object.errors
456
+ end
457
+
458
+ def teardown
459
+ StateMachine::Integrations.send(:remove_const, 'Custom')
460
+ end
461
+ end
462
+
463
+ class EventWithTransitionWithoutToStateTest < Test::Unit::TestCase
464
+ def setup
465
+ @klass = Class.new
466
+ @machine = StateMachine::Machine.new(@klass)
467
+ @machine.state :parked
468
+ @machine.event :park
469
+
470
+ @event = StateMachine::Event.new(@machine, :park)
471
+ @event.transition(:from => :parked)
472
+
473
+ @object = @klass.new
474
+ @object.state = 'parked'
475
+ end
476
+
477
+ def test_should_be_able_to_fire
478
+ assert @event.can_fire?(@object)
479
+ end
480
+
481
+ def test_should_have_a_transition
482
+ transition = @event.transition_for(@object)
483
+ assert_not_nil transition
484
+ assert_equal 'parked', transition.from
485
+ assert_equal 'parked', transition.to
486
+ assert_equal :park, transition.event
487
+ end
488
+
489
+ def test_should_fire
490
+ assert @event.fire(@object)
491
+ end
492
+
493
+ def test_should_not_change_the_current_state
494
+ @event.fire(@object)
495
+ assert_equal 'parked', @object.state
496
+ end
497
+ end
498
+
499
+ class EventWithTransitionWithNilToStateTest < Test::Unit::TestCase
500
+ def setup
501
+ @klass = Class.new
502
+ @machine = StateMachine::Machine.new(@klass)
503
+ @machine.state nil, :idling
504
+ @machine.event :park
505
+
506
+ @event = StateMachine::Event.new(@machine, :park)
507
+ @event.transition(:idling => nil)
508
+
509
+ @object = @klass.new
510
+ @object.state = 'idling'
511
+ end
512
+
513
+ def test_should_be_able_to_fire
514
+ assert @event.can_fire?(@object)
515
+ end
516
+
517
+ def test_should_have_a_transition
518
+ transition = @event.transition_for(@object)
519
+ assert_not_nil transition
520
+ assert_equal 'idling', transition.from
521
+ assert_equal nil, transition.to
522
+ assert_equal :park, transition.event
523
+ end
524
+
525
+ def test_should_fire
526
+ assert @event.fire(@object)
527
+ end
528
+
529
+ def test_should_not_change_the_current_state
530
+ @event.fire(@object)
531
+ assert_equal nil, @object.state
532
+ end
533
+ end
534
+
535
+ class EventWithMultipleTransitionsTest < Test::Unit::TestCase
536
+ def setup
537
+ @klass = Class.new
538
+ @machine = StateMachine::Machine.new(@klass)
539
+ @machine.state :parked, :idling
540
+ @machine.event :ignite
541
+
542
+ @event = StateMachine::Event.new(@machine, :ignite)
543
+ @event.transition(:idling => :idling)
544
+ @event.transition(:parked => :idling) # This one should get used
545
+
546
+ @object = @klass.new
547
+ @object.state = 'parked'
548
+ end
549
+
550
+ def test_should_be_able_to_fire
551
+ assert @event.can_fire?(@object)
552
+ end
553
+
554
+ def test_should_have_a_transition
555
+ transition = @event.transition_for(@object)
556
+ assert_not_nil transition
557
+ assert_equal 'parked', transition.from
558
+ assert_equal 'idling', transition.to
559
+ assert_equal :ignite, transition.event
560
+ end
561
+
562
+ def test_should_fire
563
+ assert @event.fire(@object)
564
+ end
565
+
566
+ def test_should_change_the_current_state
567
+ @event.fire(@object)
568
+ assert_equal 'idling', @object.state
569
+ end
570
+ end
571
+
572
+ begin
573
+ # Load library
574
+ require 'rubygems'
575
+ require 'graphviz'
576
+
577
+ class EventDrawingTest < Test::Unit::TestCase
578
+ def setup
579
+ states = [:parked, :idling, :first_gear]
580
+
581
+ @machine = StateMachine::Machine.new(Class.new, :initial => :parked)
582
+ @machine.other_states(*states)
583
+
584
+ graph = GraphViz.new('G')
585
+ states.each {|state| graph.add_node(state.to_s)}
586
+
587
+ @event = StateMachine::Event.new(@machine , :park)
588
+ @event.transition :parked => :idling
589
+ @event.transition :first_gear => :parked
590
+ @event.transition :except_from => :parked, :to => :parked
591
+
592
+ @edges = @event.draw(graph)
593
+ end
594
+
595
+ def test_should_generate_edges_for_each_transition
596
+ assert_equal 4, @edges.size
597
+ end
598
+
599
+ def test_should_use_event_name_for_edge_label
600
+ assert_equal 'park', @edges.first['label']
601
+ end
602
+ end
603
+ rescue LoadError
604
+ $stderr.puts 'Skipping GraphViz StateMachine::Event tests. `gem install ruby-graphviz` and try again.'
605
+ end