hsume2-state_machine 1.0.1

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