pluginaweek-state_machine 0.7.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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