hsume2-state_machine 1.0.1

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 (110) hide show
  1. data/CHANGELOG.rdoc +413 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +717 -0
  4. data/Rakefile +77 -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 +448 -0
  28. data/lib/state_machine/alternate_machine.rb +79 -0
  29. data/lib/state_machine/assertions.rb +36 -0
  30. data/lib/state_machine/branch.rb +224 -0
  31. data/lib/state_machine/callback.rb +236 -0
  32. data/lib/state_machine/condition_proxy.rb +94 -0
  33. data/lib/state_machine/error.rb +13 -0
  34. data/lib/state_machine/eval_helpers.rb +86 -0
  35. data/lib/state_machine/event.rb +304 -0
  36. data/lib/state_machine/event_collection.rb +139 -0
  37. data/lib/state_machine/extensions.rb +149 -0
  38. data/lib/state_machine/initializers.rb +4 -0
  39. data/lib/state_machine/initializers/merb.rb +1 -0
  40. data/lib/state_machine/initializers/rails.rb +25 -0
  41. data/lib/state_machine/integrations.rb +110 -0
  42. data/lib/state_machine/integrations/active_model.rb +502 -0
  43. data/lib/state_machine/integrations/active_model/locale.rb +11 -0
  44. data/lib/state_machine/integrations/active_model/observer.rb +45 -0
  45. data/lib/state_machine/integrations/active_model/versions.rb +31 -0
  46. data/lib/state_machine/integrations/active_record.rb +424 -0
  47. data/lib/state_machine/integrations/active_record/locale.rb +20 -0
  48. data/lib/state_machine/integrations/active_record/versions.rb +143 -0
  49. data/lib/state_machine/integrations/base.rb +91 -0
  50. data/lib/state_machine/integrations/data_mapper.rb +392 -0
  51. data/lib/state_machine/integrations/data_mapper/observer.rb +210 -0
  52. data/lib/state_machine/integrations/data_mapper/versions.rb +62 -0
  53. data/lib/state_machine/integrations/mongo_mapper.rb +272 -0
  54. data/lib/state_machine/integrations/mongo_mapper/locale.rb +4 -0
  55. data/lib/state_machine/integrations/mongo_mapper/versions.rb +110 -0
  56. data/lib/state_machine/integrations/mongoid.rb +357 -0
  57. data/lib/state_machine/integrations/mongoid/locale.rb +4 -0
  58. data/lib/state_machine/integrations/mongoid/versions.rb +18 -0
  59. data/lib/state_machine/integrations/sequel.rb +428 -0
  60. data/lib/state_machine/integrations/sequel/versions.rb +36 -0
  61. data/lib/state_machine/machine.rb +1873 -0
  62. data/lib/state_machine/machine_collection.rb +87 -0
  63. data/lib/state_machine/matcher.rb +123 -0
  64. data/lib/state_machine/matcher_helpers.rb +54 -0
  65. data/lib/state_machine/node_collection.rb +157 -0
  66. data/lib/state_machine/path.rb +120 -0
  67. data/lib/state_machine/path_collection.rb +90 -0
  68. data/lib/state_machine/state.rb +271 -0
  69. data/lib/state_machine/state_collection.rb +112 -0
  70. data/lib/state_machine/transition.rb +458 -0
  71. data/lib/state_machine/transition_collection.rb +244 -0
  72. data/lib/tasks/state_machine.rake +1 -0
  73. data/lib/tasks/state_machine.rb +27 -0
  74. data/test/files/en.yml +17 -0
  75. data/test/files/switch.rb +11 -0
  76. data/test/functional/alternate_state_machine_test.rb +122 -0
  77. data/test/functional/state_machine_test.rb +993 -0
  78. data/test/test_helper.rb +4 -0
  79. data/test/unit/assertions_test.rb +40 -0
  80. data/test/unit/branch_test.rb +890 -0
  81. data/test/unit/callback_test.rb +701 -0
  82. data/test/unit/condition_proxy_test.rb +328 -0
  83. data/test/unit/error_test.rb +43 -0
  84. data/test/unit/eval_helpers_test.rb +222 -0
  85. data/test/unit/event_collection_test.rb +358 -0
  86. data/test/unit/event_test.rb +985 -0
  87. data/test/unit/integrations/active_model_test.rb +1097 -0
  88. data/test/unit/integrations/active_record_test.rb +2021 -0
  89. data/test/unit/integrations/base_test.rb +99 -0
  90. data/test/unit/integrations/data_mapper_test.rb +1909 -0
  91. data/test/unit/integrations/mongo_mapper_test.rb +1611 -0
  92. data/test/unit/integrations/mongoid_test.rb +1591 -0
  93. data/test/unit/integrations/sequel_test.rb +1523 -0
  94. data/test/unit/integrations_test.rb +61 -0
  95. data/test/unit/invalid_event_test.rb +20 -0
  96. data/test/unit/invalid_parallel_transition_test.rb +18 -0
  97. data/test/unit/invalid_transition_test.rb +77 -0
  98. data/test/unit/machine_collection_test.rb +599 -0
  99. data/test/unit/machine_test.rb +3043 -0
  100. data/test/unit/matcher_helpers_test.rb +37 -0
  101. data/test/unit/matcher_test.rb +155 -0
  102. data/test/unit/node_collection_test.rb +217 -0
  103. data/test/unit/path_collection_test.rb +266 -0
  104. data/test/unit/path_test.rb +485 -0
  105. data/test/unit/state_collection_test.rb +310 -0
  106. data/test/unit/state_machine_test.rb +31 -0
  107. data/test/unit/state_test.rb +924 -0
  108. data/test/unit/transition_collection_test.rb +2102 -0
  109. data/test/unit/transition_test.rb +1541 -0
  110. metadata +207 -0
@@ -0,0 +1,358 @@
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
+ machine.events.concat(@events)
33
+ end
34
+
35
+ def test_should_index_by_name
36
+ assert_equal @open, @events[:enable, :name]
37
+ end
38
+
39
+ def test_should_index_by_name_by_default
40
+ assert_equal @open, @events[:enable]
41
+ end
42
+
43
+ def test_should_index_by_qualified_name
44
+ assert_equal @open, @events[:enable_alarm, :qualified_name]
45
+ end
46
+ end
47
+
48
+ class EventCollectionWithEventsWithTransitionsTest < Test::Unit::TestCase
49
+ def setup
50
+ @klass = Class.new
51
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
52
+ @events = StateMachine::EventCollection.new(@machine)
53
+
54
+ @machine.state :idling, :first_gear
55
+
56
+ @events << @ignite = StateMachine::Event.new(@machine, :ignite)
57
+ @ignite.transition :parked => :idling
58
+
59
+ @events << @park = StateMachine::Event.new(@machine, :park)
60
+ @park.transition :idling => :parked
61
+
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}
65
+
66
+ @machine.events.concat(@events)
67
+
68
+ @object = @klass.new
69
+ end
70
+
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)
120
+ end
121
+ end
122
+
123
+ class EventCollectionWithMultipleEventsTest < Test::Unit::TestCase
124
+ def setup
125
+ @klass = Class.new
126
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
127
+ @events = StateMachine::EventCollection.new(@machine)
128
+
129
+ @machine.state :first_gear
130
+ @machine.event :park, :shift_down
131
+
132
+ @events << @park = StateMachine::Event.new(@machine, :park)
133
+ @park.transition :first_gear => :parked
134
+
135
+ @events << @shift_down = StateMachine::Event.new(@machine, :shift_down)
136
+ @shift_down.transition :first_gear => :parked
137
+
138
+ @machine.events.concat(@events)
139
+ end
140
+
141
+ def test_should_only_include_all_valid_events_for_an_object
142
+ object = @klass.new
143
+ object.state = 'first_gear'
144
+ assert_equal [@park, @shift_down], @events.valid_for(object)
145
+ end
146
+ end
147
+
148
+ class EventCollectionWithoutMachineActionTest < Test::Unit::TestCase
149
+ def setup
150
+ @klass = Class.new
151
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
152
+ @events = StateMachine::EventCollection.new(@machine)
153
+ @events << StateMachine::Event.new(@machine, :ignite)
154
+ @machine.events.concat(@events)
155
+
156
+ @object = @klass.new
157
+ end
158
+
159
+ def test_should_not_have_an_attribute_transition
160
+ assert_nil @events.attribute_transition_for(@object)
161
+ end
162
+ end
163
+
164
+ class EventCollectionAttributeWithMachineActionTest < Test::Unit::TestCase
165
+ def setup
166
+ @klass = Class.new do
167
+ def save
168
+ end
169
+ end
170
+
171
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked, :action => :save)
172
+ @events = StateMachine::EventCollection.new(@machine)
173
+
174
+ @machine.state :parked, :idling
175
+ @events << @ignite = StateMachine::Event.new(@machine, :ignite)
176
+ @machine.events.concat(@events)
177
+
178
+ @object = @klass.new
179
+ end
180
+
181
+ def test_should_not_have_transition_if_nil
182
+ @object.state_event = nil
183
+ assert_nil @events.attribute_transition_for(@object)
184
+ end
185
+
186
+ def test_should_not_have_transition_if_empty
187
+ @object.state_event = ''
188
+ assert_nil @events.attribute_transition_for(@object)
189
+ end
190
+
191
+ def test_should_have_invalid_transition_if_invalid_event_specified
192
+ @object.state_event = 'invalid'
193
+ assert_equal false, @events.attribute_transition_for(@object)
194
+ end
195
+
196
+ def test_should_have_invalid_transition_if_event_cannot_be_fired
197
+ @object.state_event = 'ignite'
198
+ assert_equal false, @events.attribute_transition_for(@object)
199
+ end
200
+
201
+ def test_should_have_valid_transition_if_event_can_be_fired
202
+ @ignite.transition :parked => :idling
203
+ @object.state_event = 'ignite'
204
+
205
+ assert_instance_of StateMachine::Transition, @events.attribute_transition_for(@object)
206
+ end
207
+
208
+ def test_should_have_valid_transition_if_already_defined_in_transition_cache
209
+ @ignite.transition :parked => :idling
210
+ @object.state_event = nil
211
+ @object.send(:state_event_transition=, transition = @ignite.transition_for(@object))
212
+
213
+ assert_equal transition, @events.attribute_transition_for(@object)
214
+ end
215
+
216
+ def test_should_use_transition_cache_if_both_event_and_transition_are_present
217
+ @ignite.transition :parked => :idling
218
+ @object.state_event = 'ignite'
219
+ @object.send(:state_event_transition=, transition = @ignite.transition_for(@object))
220
+
221
+ assert_equal transition, @events.attribute_transition_for(@object)
222
+ end
223
+ end
224
+
225
+ class EventCollectionAttributeWithNamespacedMachineTest < Test::Unit::TestCase
226
+ def setup
227
+ @klass = Class.new do
228
+ def save
229
+ end
230
+ end
231
+
232
+ @machine = StateMachine::Machine.new(@klass, :namespace => 'alarm', :initial => :active, :action => :save)
233
+ @events = StateMachine::EventCollection.new(@machine)
234
+
235
+ @machine.state :active, :off
236
+ @events << @disable = StateMachine::Event.new(@machine, :disable)
237
+ @machine.events.concat(@events)
238
+
239
+ @object = @klass.new
240
+ end
241
+
242
+ def test_should_not_have_transition_if_nil
243
+ @object.state_event = nil
244
+ assert_nil @events.attribute_transition_for(@object)
245
+ end
246
+
247
+ def test_should_have_invalid_transition_if_event_cannot_be_fired
248
+ @object.state_event = 'disable'
249
+ assert_equal false, @events.attribute_transition_for(@object)
250
+ end
251
+
252
+ def test_should_have_valid_transition_if_event_can_be_fired
253
+ @disable.transition :active => :off
254
+ @object.state_event = 'disable'
255
+
256
+ assert_instance_of StateMachine::Transition, @events.attribute_transition_for(@object)
257
+ end
258
+ end
259
+
260
+ class EventCollectionWithValidationsTest < Test::Unit::TestCase
261
+ def setup
262
+ StateMachine::Integrations.const_set('Custom', Module.new do
263
+ include StateMachine::Integrations::Base
264
+
265
+ def invalidate(object, attribute, message, values = [])
266
+ (object.errors ||= []) << generate_message(message, values)
267
+ end
268
+
269
+ def reset(object)
270
+ object.errors = []
271
+ end
272
+ end)
273
+
274
+ @klass = Class.new do
275
+ attr_accessor :errors
276
+
277
+ def initialize
278
+ @errors = []
279
+ super
280
+ end
281
+ end
282
+
283
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked, :action => :save, :integration => :custom)
284
+ @events = StateMachine::EventCollection.new(@machine)
285
+
286
+ @parked, @idling = @machine.state :parked, :idling
287
+ @events << @ignite = StateMachine::Event.new(@machine, :ignite)
288
+ @machine.events.concat(@events)
289
+
290
+ @object = @klass.new
291
+ end
292
+
293
+ def test_should_invalidate_if_invalid_event_specified
294
+ @object.state_event = 'invalid'
295
+ @events.attribute_transition_for(@object, true)
296
+
297
+ assert_equal ['is invalid'], @object.errors
298
+ end
299
+
300
+ def test_should_invalidate_if_event_cannot_be_fired
301
+ @object.state = 'idling'
302
+ @object.state_event = 'ignite'
303
+ @events.attribute_transition_for(@object, true)
304
+
305
+ assert_equal ['cannot transition when idling'], @object.errors
306
+ end
307
+
308
+ def test_should_invalidate_with_human_name_if_invalid_event_specified
309
+ @idling.human_name = 'waiting'
310
+ @object.state = 'idling'
311
+ @object.state_event = 'ignite'
312
+ @events.attribute_transition_for(@object, true)
313
+
314
+ assert_equal ['cannot transition when waiting'], @object.errors
315
+ end
316
+
317
+ def test_should_not_invalidate_event_can_be_fired
318
+ @ignite.transition :parked => :idling
319
+ @object.state_event = 'ignite'
320
+ @events.attribute_transition_for(@object, true)
321
+
322
+ assert_equal [], @object.errors
323
+ end
324
+
325
+ def teardown
326
+ StateMachine::Integrations.send(:remove_const, 'Custom')
327
+ end
328
+ end
329
+
330
+ class EventCollectionWithCustomMachineAttributeTest < Test::Unit::TestCase
331
+ def setup
332
+ @klass = Class.new do
333
+ def save
334
+ end
335
+ end
336
+
337
+ @machine = StateMachine::Machine.new(@klass, :state, :attribute => :state_id, :initial => :parked, :action => :save)
338
+ @events = StateMachine::EventCollection.new(@machine)
339
+
340
+ @machine.state :parked, :idling
341
+ @events << @ignite = StateMachine::Event.new(@machine, :ignite)
342
+ @machine.events.concat(@events)
343
+
344
+ @object = @klass.new
345
+ end
346
+
347
+ def test_should_not_have_transition_if_nil
348
+ @object.state_event = nil
349
+ assert_nil @events.attribute_transition_for(@object)
350
+ end
351
+
352
+ def test_should_have_valid_transition_if_event_can_be_fired
353
+ @ignite.transition :parked => :idling
354
+ @object.state_event = 'ignite'
355
+
356
+ assert_instance_of StateMachine::Transition, @events.attribute_transition_for(@object)
357
+ end
358
+ end
@@ -0,0 +1,985 @@
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
+ @machine.events << @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_have_a_human_name
25
+ assert_equal 'ignite', @event.human_name
26
+ end
27
+
28
+ def test_should_not_have_any_branches
29
+ assert @event.branches.empty?
30
+ end
31
+
32
+ def test_should_have_no_known_states
33
+ assert @event.known_states.empty?
34
+ end
35
+
36
+ def test_should_not_be_able_to_fire
37
+ assert !@event.can_fire?(@object)
38
+ end
39
+
40
+ def test_should_not_have_a_transition
41
+ assert_nil @event.transition_for(@object)
42
+ end
43
+
44
+ def test_should_define_a_predicate
45
+ assert @object.respond_to?(:can_ignite?)
46
+ end
47
+
48
+ def test_should_define_a_transition_accessor
49
+ assert @object.respond_to?(:ignite_transition)
50
+ end
51
+
52
+ def test_should_define_an_action
53
+ assert @object.respond_to?(:ignite)
54
+ end
55
+
56
+ def test_should_define_a_bang_action
57
+ assert @object.respond_to?(:ignite!)
58
+ end
59
+ end
60
+
61
+ class EventTest < Test::Unit::TestCase
62
+ def setup
63
+ @machine = StateMachine::Machine.new(Class.new)
64
+ @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
65
+ @event.transition :parked => :idling
66
+ end
67
+
68
+ def test_should_allow_changing_machine
69
+ new_machine = StateMachine::Machine.new(Class.new)
70
+ @event.machine = new_machine
71
+ assert_equal new_machine, @event.machine
72
+ end
73
+
74
+ def test_should_allow_changing_human_name
75
+ @event.human_name = 'Stop'
76
+ assert_equal 'Stop', @event.human_name
77
+ end
78
+
79
+ def test_should_provide_matcher_helpers_during_initialization
80
+ matchers = []
81
+
82
+ @event.instance_eval do
83
+ matchers = [all, any, same]
84
+ end
85
+
86
+ assert_equal [StateMachine::AllMatcher.instance, StateMachine::AllMatcher.instance, StateMachine::LoopbackMatcher.instance], matchers
87
+ end
88
+
89
+ def test_should_use_pretty_inspect
90
+ assert_match "#<StateMachine::Event name=:ignite transitions=[:parked => :idling]>", @event.inspect
91
+ end
92
+ end
93
+
94
+ class EventWithHumanNameTest < Test::Unit::TestCase
95
+ def setup
96
+ @klass = Class.new
97
+ @machine = StateMachine::Machine.new(@klass)
98
+ @machine.events << @event = StateMachine::Event.new(@machine, :ignite, :human_name => 'start')
99
+ end
100
+
101
+ def test_should_use_custom_human_name
102
+ assert_equal 'start', @event.human_name
103
+ end
104
+ end
105
+
106
+ class EventWithDynamicHumanNameTest < Test::Unit::TestCase
107
+ def setup
108
+ @klass = Class.new
109
+ @machine = StateMachine::Machine.new(@klass)
110
+ @machine.events << @event = StateMachine::Event.new(@machine, :ignite, :human_name => lambda {|event, object| ['start', object]})
111
+ end
112
+
113
+ def test_should_use_custom_human_name
114
+ human_name, klass = @event.human_name
115
+ assert_equal 'start', human_name
116
+ assert_equal @klass, klass
117
+ end
118
+
119
+ def test_should_allow_custom_class_to_be_passed_through
120
+ human_name, klass = @event.human_name(1)
121
+ assert_equal 'start', human_name
122
+ assert_equal 1, klass
123
+ end
124
+
125
+ def test_should_not_cache_value
126
+ assert_not_same @event.human_name, @event.human_name
127
+ end
128
+ end
129
+
130
+ class EventWithConflictingHelpersBeforeDefinitionTest < Test::Unit::TestCase
131
+ def setup
132
+ require 'stringio'
133
+ @original_stderr, $stderr = $stderr, StringIO.new
134
+
135
+ @superclass = Class.new do
136
+ def can_ignite?
137
+ 0
138
+ end
139
+
140
+ def ignite_transition
141
+ 0
142
+ end
143
+
144
+ def ignite
145
+ 0
146
+ end
147
+
148
+ def ignite!
149
+ 0
150
+ end
151
+ end
152
+ @klass = Class.new(@superclass)
153
+ @machine = StateMachine::Machine.new(@klass)
154
+ @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
155
+ @object = @klass.new
156
+ end
157
+
158
+ def test_should_not_redefine_predicate
159
+ assert_equal 0, @object.can_ignite?
160
+ end
161
+
162
+ def test_should_not_redefine_transition_accessor
163
+ assert_equal 0, @object.ignite_transition
164
+ end
165
+
166
+ def test_should_not_redefine_action
167
+ assert_equal 0, @object.ignite
168
+ end
169
+
170
+ def test_should_not_redefine_bang_action
171
+ assert_equal 0, @object.ignite!
172
+ end
173
+
174
+ def test_should_output_warning
175
+ expected = %w(can_ignite? ignite_transition ignite ignite!).map do |method|
176
+ "Instance method \"#{method}\" is already defined in #{@superclass.to_s}, use generic helper instead.\n"
177
+ end.join
178
+
179
+ assert_equal expected, $stderr.string
180
+ end
181
+
182
+ def teardown
183
+ $stderr = @original_stderr
184
+ end
185
+ end
186
+
187
+ class EventWithConflictingHelpersAfterDefinitionTest < Test::Unit::TestCase
188
+ def setup
189
+ require 'stringio'
190
+ @original_stderr, $stderr = $stderr, StringIO.new
191
+
192
+ @klass = Class.new do
193
+ def can_ignite?
194
+ 0
195
+ end
196
+
197
+ def ignite_transition
198
+ 0
199
+ end
200
+
201
+ def ignite
202
+ 0
203
+ end
204
+
205
+ def ignite!
206
+ 0
207
+ end
208
+ end
209
+ @machine = StateMachine::Machine.new(@klass)
210
+ @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
211
+ @object = @klass.new
212
+ end
213
+
214
+ def test_should_not_redefine_predicate
215
+ assert_equal 0, @object.can_ignite?
216
+ end
217
+
218
+ def test_should_not_redefine_transition_accessor
219
+ assert_equal 0, @object.ignite_transition
220
+ end
221
+
222
+ def test_should_not_redefine_action
223
+ assert_equal 0, @object.ignite
224
+ end
225
+
226
+ def test_should_not_redefine_bang_action
227
+ assert_equal 0, @object.ignite!
228
+ end
229
+
230
+ def test_should_allow_super_chaining
231
+ @klass.class_eval do
232
+ def can_ignite?
233
+ super
234
+ end
235
+
236
+ def ignite_transition
237
+ super
238
+ end
239
+
240
+ def ignite
241
+ super
242
+ end
243
+
244
+ def ignite!
245
+ super
246
+ end
247
+ end
248
+
249
+ assert_equal false, @object.can_ignite?
250
+ assert_equal nil, @object.ignite_transition
251
+ assert_equal false, @object.ignite
252
+ assert_raise(StateMachine::InvalidTransition) { @object.ignite! }
253
+ end
254
+
255
+ def test_should_not_output_warning
256
+ assert_equal '', $stderr.string
257
+ end
258
+
259
+ def teardown
260
+ $stderr = @original_stderr
261
+ end
262
+ end
263
+
264
+ class EventWithConflictingMachineTest < Test::Unit::TestCase
265
+ def setup
266
+ require 'stringio'
267
+ @original_stderr, $stderr = $stderr, StringIO.new
268
+
269
+ @klass = Class.new
270
+ @state_machine = StateMachine::Machine.new(@klass, :state)
271
+ @state_machine.state :parked, :idling
272
+ @state_machine.events << @state_event = StateMachine::Event.new(@state_machine, :ignite)
273
+ end
274
+
275
+ def test_should_not_overwrite_first_event
276
+ @status_machine = StateMachine::Machine.new(@klass, :status)
277
+ @status_machine.state :first_gear, :second_gear
278
+ @status_machine.events << @status_event = StateMachine::Event.new(@status_machine, :ignite)
279
+
280
+ @object = @klass.new
281
+ @object.state = 'parked'
282
+ @object.status = 'first_gear'
283
+
284
+ @state_event.transition(:parked => :idling)
285
+ @status_event.transition(:parked => :first_gear)
286
+
287
+ @object.ignite
288
+ assert_equal 'idling', @object.state
289
+ assert_equal 'first_gear', @object.status
290
+ end
291
+
292
+ def test_should_output_warning
293
+ @status_machine = StateMachine::Machine.new(@klass, :status)
294
+ @status_machine.events << @status_event = StateMachine::Event.new(@status_machine, :ignite)
295
+
296
+ assert_equal "Event :ignite for :status is already defined in :state\n", $stderr.string
297
+ end
298
+
299
+ def test_should_not_output_warning_if_using_different_namespace
300
+ @status_machine = StateMachine::Machine.new(@klass, :status, :namespace => 'alarm')
301
+ @status_machine.events << @status_event = StateMachine::Event.new(@status_machine, :ignite)
302
+
303
+ assert_equal '', $stderr.string
304
+ end
305
+
306
+ def teardown
307
+ $stderr = @original_stderr
308
+ end
309
+ end
310
+
311
+ class EventWithNamespaceTest < Test::Unit::TestCase
312
+ def setup
313
+ @klass = Class.new
314
+ @machine = StateMachine::Machine.new(@klass, :namespace => 'alarm')
315
+ @machine.events << @event = StateMachine::Event.new(@machine, :enable)
316
+ @object = @klass.new
317
+ end
318
+
319
+ def test_should_have_a_name
320
+ assert_equal :enable, @event.name
321
+ end
322
+
323
+ def test_should_have_a_qualified_name
324
+ assert_equal :enable_alarm, @event.qualified_name
325
+ end
326
+
327
+ def test_should_namespace_predicate
328
+ assert @object.respond_to?(:can_enable_alarm?)
329
+ end
330
+
331
+ def test_should_namespace_transition_accessor
332
+ assert @object.respond_to?(:enable_alarm_transition)
333
+ end
334
+
335
+ def test_should_namespace_action
336
+ assert @object.respond_to?(:enable_alarm)
337
+ end
338
+
339
+ def test_should_namespace_bang_action
340
+ assert @object.respond_to?(:enable_alarm!)
341
+ end
342
+ end
343
+
344
+ class EventTransitionsTest < Test::Unit::TestCase
345
+ def setup
346
+ @machine = StateMachine::Machine.new(Class.new)
347
+ @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
348
+ end
349
+
350
+ def test_should_not_raise_exception_if_implicit_option_specified
351
+ assert_nothing_raised {@event.transition(:invalid => :valid)}
352
+ end
353
+
354
+ def test_should_not_allow_on_option
355
+ exception = assert_raise(ArgumentError) {@event.transition(:on => :ignite)}
356
+ assert_equal 'Invalid key(s): on', exception.message
357
+ end
358
+
359
+ def test_should_automatically_set_on_option
360
+ branch = @event.transition(:to => :idling)
361
+ assert_instance_of StateMachine::WhitelistMatcher, branch.event_requirement
362
+ assert_equal [:ignite], branch.event_requirement.values
363
+ end
364
+
365
+ def test_should_not_allow_except_to_option
366
+ exception = assert_raise(ArgumentError) {@event.transition(:except_to => :parked)}
367
+ assert_equal 'Invalid key(s): except_to', exception.message
368
+ end
369
+
370
+ def test_should_not_allow_except_on_option
371
+ exception = assert_raise(ArgumentError) {@event.transition(:except_on => :ignite)}
372
+ assert_equal 'Invalid key(s): except_on', exception.message
373
+ end
374
+
375
+ def test_should_allow_transitioning_without_a_to_state
376
+ assert_nothing_raised {@event.transition(:from => :parked)}
377
+ end
378
+
379
+ def test_should_allow_transitioning_without_a_from_state
380
+ assert_nothing_raised {@event.transition(:to => :idling)}
381
+ end
382
+
383
+ def test_should_allow_except_from_option
384
+ assert_nothing_raised {@event.transition(:except_from => :idling)}
385
+ end
386
+
387
+ def test_should_allow_transitioning_from_a_single_state
388
+ assert @event.transition(:parked => :idling)
389
+ end
390
+
391
+ def test_should_allow_transitioning_from_multiple_states
392
+ assert @event.transition([:parked, :idling] => :idling)
393
+ end
394
+
395
+ def test_should_have_transitions
396
+ branch = @event.transition(:to => :idling)
397
+ assert_equal [branch], @event.branches
398
+ end
399
+ end
400
+
401
+ class EventAfterBeingCopiedTest < Test::Unit::TestCase
402
+ def setup
403
+ @machine = StateMachine::Machine.new(Class.new)
404
+ @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
405
+ @copied_event = @event.dup
406
+ end
407
+
408
+ def test_should_not_have_the_same_collection_of_branches
409
+ assert_not_same @event.branches, @copied_event.branches
410
+ end
411
+
412
+ def test_should_not_have_the_same_collection_of_known_states
413
+ assert_not_same @event.known_states, @copied_event.known_states
414
+ end
415
+ end
416
+
417
+ class EventWithoutTransitionsTest < Test::Unit::TestCase
418
+ def setup
419
+ @klass = Class.new
420
+ @machine = StateMachine::Machine.new(@klass)
421
+ @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
422
+ @object = @klass.new
423
+ end
424
+
425
+ def test_should_not_be_able_to_fire
426
+ assert !@event.can_fire?(@object)
427
+ end
428
+
429
+ def test_should_not_have_a_transition
430
+ assert_nil @event.transition_for(@object)
431
+ end
432
+
433
+ def test_should_not_fire
434
+ assert !@event.fire(@object)
435
+ end
436
+
437
+ def test_should_not_change_the_current_state
438
+ @event.fire(@object)
439
+ assert_nil @object.state
440
+ end
441
+ end
442
+
443
+ class EventWithTransitionsTest < Test::Unit::TestCase
444
+ def setup
445
+ @klass = Class.new
446
+ @machine = StateMachine::Machine.new(@klass)
447
+ @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
448
+ @event.transition(:parked => :idling)
449
+ @event.transition(:first_gear => :idling)
450
+ end
451
+
452
+ def test_should_include_all_transition_states_in_known_states
453
+ assert_equal [:parked, :idling, :first_gear], @event.known_states
454
+ end
455
+
456
+ def test_should_include_new_transition_states_after_calling_known_states
457
+ @event.known_states
458
+ @event.transition(:stalled => :idling)
459
+
460
+ assert_equal [:parked, :idling, :first_gear, :stalled], @event.known_states
461
+ end
462
+
463
+ def test_should_use_pretty_inspect
464
+ assert_match "#<StateMachine::Event name=:ignite transitions=[:parked => :idling, :first_gear => :idling]>", @event.inspect
465
+ end
466
+ end
467
+
468
+ class EventWithoutMatchingTransitionsTest < Test::Unit::TestCase
469
+ def setup
470
+ @klass = Class.new
471
+ @machine = StateMachine::Machine.new(@klass)
472
+ @machine.state :parked, :idling
473
+
474
+ @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
475
+ @event.transition(:parked => :idling)
476
+
477
+ @object = @klass.new
478
+ @object.state = 'idling'
479
+ end
480
+
481
+ def test_should_not_be_able_to_fire
482
+ assert !@event.can_fire?(@object)
483
+ end
484
+
485
+ def test_should_be_able_to_fire_with_custom_from_state
486
+ assert @event.can_fire?(@object, :from => :parked)
487
+ end
488
+
489
+ def test_should_not_have_a_transition
490
+ assert_nil @event.transition_for(@object)
491
+ end
492
+
493
+ def test_should_have_a_transition_with_custom_from_state
494
+ assert_not_nil @event.transition_for(@object, :from => :parked)
495
+ end
496
+
497
+ def test_should_not_fire
498
+ assert !@event.fire(@object)
499
+ end
500
+
501
+ def test_should_not_change_the_current_state
502
+ @event.fire(@object)
503
+ assert_equal 'idling', @object.state
504
+ end
505
+ end
506
+
507
+ class EventWithMatchingDisabledTransitionsTest < Test::Unit::TestCase
508
+ def setup
509
+ StateMachine::Integrations.const_set('Custom', Module.new do
510
+ include StateMachine::Integrations::Base
511
+
512
+ def invalidate(object, attribute, message, values = [])
513
+ (object.errors ||= []) << generate_message(message, values)
514
+ end
515
+
516
+ def reset(object)
517
+ object.errors = []
518
+ end
519
+ end)
520
+
521
+ @klass = Class.new do
522
+ attr_accessor :errors
523
+ end
524
+
525
+ @machine = StateMachine::Machine.new(@klass, :integration => :custom)
526
+ @machine.state :parked, :idling
527
+
528
+ @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
529
+ @event.transition(:parked => :idling, :if => lambda {false})
530
+
531
+ @object = @klass.new
532
+ @object.state = 'parked'
533
+ end
534
+
535
+ def test_should_not_be_able_to_fire
536
+ assert !@event.can_fire?(@object)
537
+ end
538
+
539
+ def test_should_be_able_to_fire_with_disabled_guards
540
+ assert @event.can_fire?(@object, :guard => false)
541
+ end
542
+
543
+ def test_should_not_have_a_transition
544
+ assert_nil @event.transition_for(@object)
545
+ end
546
+
547
+ def test_should_have_a_transition_with_disabled_guards
548
+ assert_not_nil @event.transition_for(@object, :guard => false)
549
+ end
550
+
551
+ def test_should_not_fire
552
+ assert !@event.fire(@object)
553
+ end
554
+
555
+ def test_should_not_change_the_current_state
556
+ @event.fire(@object)
557
+ assert_equal 'parked', @object.state
558
+ end
559
+
560
+ def test_should_invalidate_the_state
561
+ @event.fire(@object)
562
+ assert_equal ['cannot transition via "ignite"'], @object.errors
563
+ end
564
+
565
+ def test_should_invalidate_with_human_event_name
566
+ @event.human_name = 'start'
567
+ @event.fire(@object)
568
+ assert_equal ['cannot transition via "start"'], @object.errors
569
+ end
570
+
571
+ def test_should_reset_existing_error
572
+ @object.errors = ['invalid']
573
+
574
+ @event.fire(@object)
575
+ assert_equal ['cannot transition via "ignite"'], @object.errors
576
+ end
577
+
578
+ def test_should_run_failure_callbacks
579
+ callback_args = nil
580
+ @machine.after_failure {|*args| callback_args = args}
581
+
582
+ @event.fire(@object)
583
+
584
+ object, transition = callback_args
585
+ assert_equal @object, object
586
+ assert_not_nil transition
587
+ assert_equal @object, transition.object
588
+ assert_equal @machine, transition.machine
589
+ assert_equal :ignite, transition.event
590
+ assert_equal :parked, transition.from_name
591
+ assert_equal :parked, transition.to_name
592
+ end
593
+
594
+ def teardown
595
+ StateMachine::Integrations.send(:remove_const, 'Custom')
596
+ end
597
+ end
598
+
599
+ class EventWithMatchingEnabledTransitionsTest < Test::Unit::TestCase
600
+ def setup
601
+ StateMachine::Integrations.const_set('Custom', Module.new do
602
+ include StateMachine::Integrations::Base
603
+
604
+ def invalidate(object, attribute, message, values = [])
605
+ (object.errors ||= []) << generate_message(message, values)
606
+ end
607
+
608
+ def reset(object)
609
+ object.errors = []
610
+ end
611
+ end)
612
+
613
+ @klass = Class.new do
614
+ attr_accessor :errors
615
+ end
616
+
617
+ @machine = StateMachine::Machine.new(@klass, :integration => :custom)
618
+ @machine.state :parked, :idling
619
+
620
+ @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
621
+ @event.transition(:parked => :idling)
622
+
623
+ @object = @klass.new
624
+ @object.state = 'parked'
625
+ end
626
+
627
+ def test_should_be_able_to_fire
628
+ assert @event.can_fire?(@object)
629
+ end
630
+
631
+ def test_should_have_a_transition
632
+ transition = @event.transition_for(@object)
633
+ assert_not_nil transition
634
+ assert_equal 'parked', transition.from
635
+ assert_equal 'idling', transition.to
636
+ assert_equal :ignite, transition.event
637
+ end
638
+
639
+ def test_should_fire
640
+ assert @event.fire(@object)
641
+ end
642
+
643
+ def test_should_change_the_current_state
644
+ @event.fire(@object)
645
+ assert_equal 'idling', @object.state
646
+ end
647
+
648
+ def test_should_reset_existing_error
649
+ @object.errors = ['invalid']
650
+
651
+ @event.fire(@object)
652
+ assert_equal [], @object.errors
653
+ end
654
+
655
+ def test_should_not_invalidate_the_state
656
+ @event.fire(@object)
657
+ assert_equal [], @object.errors
658
+ end
659
+
660
+ def teardown
661
+ StateMachine::Integrations.send(:remove_const, 'Custom')
662
+ end
663
+ end
664
+
665
+ class EventWithTransitionWithoutToStateTest < Test::Unit::TestCase
666
+ def setup
667
+ @klass = Class.new
668
+ @machine = StateMachine::Machine.new(@klass)
669
+ @machine.state :parked
670
+
671
+ @machine.events << @event = StateMachine::Event.new(@machine, :park)
672
+ @event.transition(:from => :parked)
673
+
674
+ @object = @klass.new
675
+ @object.state = 'parked'
676
+ end
677
+
678
+ def test_should_be_able_to_fire
679
+ assert @event.can_fire?(@object)
680
+ end
681
+
682
+ def test_should_have_a_transition
683
+ transition = @event.transition_for(@object)
684
+ assert_not_nil transition
685
+ assert_equal 'parked', transition.from
686
+ assert_equal 'parked', transition.to
687
+ assert_equal :park, transition.event
688
+ end
689
+
690
+ def test_should_fire
691
+ assert @event.fire(@object)
692
+ end
693
+
694
+ def test_should_not_change_the_current_state
695
+ @event.fire(@object)
696
+ assert_equal 'parked', @object.state
697
+ end
698
+ end
699
+
700
+ class EventWithTransitionWithNilToStateTest < Test::Unit::TestCase
701
+ def setup
702
+ @klass = Class.new
703
+ @machine = StateMachine::Machine.new(@klass)
704
+ @machine.state nil, :idling
705
+
706
+ @machine.events << @event = StateMachine::Event.new(@machine, :park)
707
+ @event.transition(:idling => nil)
708
+
709
+ @object = @klass.new
710
+ @object.state = 'idling'
711
+ end
712
+
713
+ def test_should_be_able_to_fire
714
+ assert @event.can_fire?(@object)
715
+ end
716
+
717
+ def test_should_have_a_transition
718
+ transition = @event.transition_for(@object)
719
+ assert_not_nil transition
720
+ assert_equal 'idling', transition.from
721
+ assert_equal nil, transition.to
722
+ assert_equal :park, transition.event
723
+ end
724
+
725
+ def test_should_fire
726
+ assert @event.fire(@object)
727
+ end
728
+
729
+ def test_should_not_change_the_current_state
730
+ @event.fire(@object)
731
+ assert_equal nil, @object.state
732
+ end
733
+ end
734
+
735
+ class EventWithMultipleTransitionsTest < Test::Unit::TestCase
736
+ def setup
737
+ @klass = Class.new
738
+ @machine = StateMachine::Machine.new(@klass)
739
+ @machine.state :parked, :idling
740
+
741
+ @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
742
+ @event.transition(:idling => :idling)
743
+ @event.transition(:parked => :idling) # This one should get used
744
+ @event.transition(:parked => :parked)
745
+
746
+ @object = @klass.new
747
+ @object.state = 'parked'
748
+ end
749
+
750
+ def test_should_be_able_to_fire
751
+ assert @event.can_fire?(@object)
752
+ end
753
+
754
+ def test_should_have_a_transition
755
+ transition = @event.transition_for(@object)
756
+ assert_not_nil transition
757
+ assert_equal 'parked', transition.from
758
+ assert_equal 'idling', transition.to
759
+ assert_equal :ignite, transition.event
760
+ end
761
+
762
+ def test_should_allow_specific_transition_selection_using_from
763
+ transition = @event.transition_for(@object, :from => :idling)
764
+
765
+ assert_not_nil transition
766
+ assert_equal 'idling', transition.from
767
+ assert_equal 'idling', transition.to
768
+ assert_equal :ignite, transition.event
769
+ end
770
+
771
+ def test_should_allow_specific_transition_selection_using_to
772
+ transition = @event.transition_for(@object, :from => :parked, :to => :parked)
773
+
774
+ assert_not_nil transition
775
+ assert_equal 'parked', transition.from
776
+ assert_equal 'parked', transition.to
777
+ assert_equal :ignite, transition.event
778
+ end
779
+
780
+ def test_should_not_allow_specific_transition_selection_using_on
781
+ exception = assert_raise(ArgumentError) { @event.transition_for(@object, :on => :park) }
782
+ assert_equal 'Invalid key(s): on', exception.message
783
+ end
784
+
785
+ def test_should_fire
786
+ assert @event.fire(@object)
787
+ end
788
+
789
+ def test_should_change_the_current_state
790
+ @event.fire(@object)
791
+ assert_equal 'idling', @object.state
792
+ end
793
+ end
794
+
795
+ class EventWithMachineActionTest < Test::Unit::TestCase
796
+ def setup
797
+ @klass = Class.new do
798
+ attr_reader :saved
799
+
800
+ def save
801
+ @saved = true
802
+ end
803
+ end
804
+
805
+ @machine = StateMachine::Machine.new(@klass, :action => :save)
806
+ @machine.state :parked, :idling
807
+
808
+ @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
809
+ @event.transition(:parked => :idling)
810
+
811
+ @object = @klass.new
812
+ @object.state = 'parked'
813
+ end
814
+
815
+ def test_should_run_action_on_fire
816
+ @event.fire(@object)
817
+ assert @object.saved
818
+ end
819
+
820
+ def test_should_not_run_action_if_configured_to_skip
821
+ @event.fire(@object, false)
822
+ assert !@object.saved
823
+ end
824
+ end
825
+
826
+ class EventWithInvalidCurrentStateTest < Test::Unit::TestCase
827
+ def setup
828
+ @klass = Class.new
829
+ @machine = StateMachine::Machine.new(@klass)
830
+ @machine.state :parked, :idling
831
+
832
+ @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
833
+ @event.transition(:parked => :idling)
834
+
835
+ @object = @klass.new
836
+ @object.state = 'invalid'
837
+ end
838
+
839
+ def test_should_raise_exception_when_checking_availability
840
+ exception = assert_raise(ArgumentError) { @event.can_fire?(@object) }
841
+ assert_equal '"invalid" is not a known state value', exception.message
842
+ end
843
+
844
+ def test_should_raise_exception_when_finding_transition
845
+ exception = assert_raise(ArgumentError) { @event.transition_for(@object) }
846
+ assert_equal '"invalid" is not a known state value', exception.message
847
+ end
848
+
849
+ def test_should_raise_exception_when_firing
850
+ exception = assert_raise(ArgumentError) { @event.fire(@object) }
851
+ assert_equal '"invalid" is not a known state value', exception.message
852
+ end
853
+ end
854
+
855
+ class EventOnFailureTest < Test::Unit::TestCase
856
+ def setup
857
+ StateMachine::Integrations.const_set('Custom', Module.new do
858
+ include StateMachine::Integrations::Base
859
+
860
+ def invalidate(object, attribute, message, values = [])
861
+ (object.errors ||= []) << generate_message(message, values)
862
+ end
863
+
864
+ def reset(object)
865
+ object.errors = []
866
+ end
867
+ end)
868
+
869
+ @klass = Class.new do
870
+ attr_accessor :errors
871
+ end
872
+
873
+ @machine = StateMachine::Machine.new(@klass, :integration => :custom)
874
+ @machine.state :parked
875
+ @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
876
+
877
+ @object = @klass.new
878
+ @object.state = 'parked'
879
+ end
880
+
881
+ def test_should_invalidate_the_state
882
+ @event.fire(@object)
883
+ assert_equal ['cannot transition via "ignite"'], @object.errors
884
+ end
885
+
886
+ def test_should_run_failure_callbacks
887
+ callback_args = nil
888
+ @machine.after_failure {|*args| callback_args = args}
889
+
890
+ @event.fire(@object)
891
+
892
+ object, transition = callback_args
893
+ assert_equal @object, object
894
+ assert_not_nil transition
895
+ assert_equal @object, transition.object
896
+ assert_equal @machine, transition.machine
897
+ assert_equal :ignite, transition.event
898
+ assert_equal :parked, transition.from_name
899
+ assert_equal :parked, transition.to_name
900
+ end
901
+
902
+ def teardown
903
+ StateMachine::Integrations.send(:remove_const, 'Custom')
904
+ end
905
+ end
906
+
907
+ class EventWithMarshallingTest < Test::Unit::TestCase
908
+ def setup
909
+ @klass = Class.new do
910
+ def save
911
+ true
912
+ end
913
+ end
914
+ self.class.const_set('Example', @klass)
915
+
916
+ @machine = StateMachine::Machine.new(@klass, :action => :save)
917
+ @machine.state :parked, :idling
918
+
919
+ @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
920
+ @event.transition(:parked => :idling)
921
+
922
+ @object = @klass.new
923
+ @object.state = 'parked'
924
+ end
925
+
926
+ def test_should_marshal_during_before_callbacks
927
+ @machine.before_transition {|object, transition| Marshal.dump(object)}
928
+ assert_nothing_raised { @event.fire(@object) }
929
+ end
930
+
931
+ def test_should_marshal_during_action
932
+ @klass.class_eval do
933
+ def save
934
+ Marshal.dump(self)
935
+ end
936
+ end
937
+
938
+ assert_nothing_raised { @event.fire(@object) }
939
+ end
940
+
941
+ def test_should_marshal_during_after_callbacks
942
+ @machine.after_transition {|object, transition| Marshal.dump(object)}
943
+ assert_nothing_raised { @event.fire(@object) }
944
+ end
945
+
946
+ def teardown
947
+ self.class.send(:remove_const, 'Example')
948
+ end
949
+ end
950
+
951
+ begin
952
+ # Load library
953
+ require 'rubygems'
954
+ gem 'ruby-graphviz', '>=0.9.0'
955
+ require 'graphviz'
956
+
957
+ class EventDrawingTest < Test::Unit::TestCase
958
+ def setup
959
+ states = [:parked, :idling, :first_gear]
960
+
961
+ @machine = StateMachine::Machine.new(Class.new, :initial => :parked)
962
+ @machine.other_states(*states)
963
+
964
+ graph = GraphViz.new('G')
965
+ states.each {|state| graph.add_node(state.to_s)}
966
+
967
+ @machine.events << @event = StateMachine::Event.new(@machine , :park)
968
+ @event.transition :parked => :idling
969
+ @event.transition :first_gear => :parked
970
+ @event.transition :except_from => :parked, :to => :parked
971
+
972
+ @edges = @event.draw(graph)
973
+ end
974
+
975
+ def test_should_generate_edges_for_each_transition
976
+ assert_equal 4, @edges.size
977
+ end
978
+
979
+ def test_should_use_event_name_for_edge_label
980
+ assert_equal 'park', @edges.first['label'].to_s.gsub('"', '')
981
+ end
982
+ end
983
+ rescue LoadError
984
+ $stderr.puts 'Skipping GraphViz StateMachine::Event tests. `gem install ruby-graphviz` >= v0.9.0 and try again.'
985
+ end