state_machines 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +0 -2
  3. data/README.md +25 -0
  4. data/Rakefile +10 -1
  5. data/lib/state_machines/branch.rb +0 -4
  6. data/lib/state_machines/core.rb +23 -5
  7. data/lib/state_machines/error.rb +81 -2
  8. data/lib/state_machines/event.rb +2 -20
  9. data/lib/state_machines/event_collection.rb +25 -27
  10. data/lib/state_machines/extensions.rb +34 -34
  11. data/lib/state_machines/integrations.rb +98 -90
  12. data/lib/state_machines/integrations/base.rb +11 -60
  13. data/lib/state_machines/matcher.rb +0 -2
  14. data/lib/state_machines/node_collection.rb +0 -2
  15. data/lib/state_machines/path_collection.rb +0 -2
  16. data/lib/state_machines/state.rb +0 -3
  17. data/lib/state_machines/state_collection.rb +17 -19
  18. data/lib/state_machines/state_context.rb +1 -6
  19. data/lib/state_machines/transition.rb +0 -56
  20. data/lib/state_machines/version.rb +1 -1
  21. data/spec/spec_helper.rb +1 -0
  22. data/spec/state_machines/assertions_spec.rb +31 -0
  23. data/spec/state_machines/branch_spec.rb +827 -0
  24. data/spec/state_machines/callbacks_spec.rb +706 -0
  25. data/spec/state_machines/errors_spec.rb +1 -0
  26. data/spec/state_machines/event_collection_spec.rb +401 -0
  27. data/spec/state_machines/event_spec.rb +1140 -0
  28. data/spec/{helpers → state_machines}/helper_spec.rb +0 -0
  29. data/spec/state_machines/integration_base_spec.rb +12 -0
  30. data/spec/state_machines/integration_spec.rb +132 -0
  31. data/spec/state_machines/invalid_event_spec.rb +19 -0
  32. data/spec/state_machines/invalid_parallel_transition_spec.rb +18 -0
  33. data/spec/state_machines/invalid_transition_spec.rb +114 -0
  34. data/spec/state_machines/machine_collection_spec.rb +606 -0
  35. data/spec/{machine_spec.rb → state_machines/machine_spec.rb} +11 -2
  36. data/spec/{matcher_helpers_spec.rb → state_machines/matcher_helpers_spec.rb} +0 -0
  37. data/spec/{matcher_spec.rb → state_machines/matcher_spec.rb} +0 -0
  38. data/spec/{node_collection_spec.rb → state_machines/node_collection_spec.rb} +0 -0
  39. data/spec/{path_collection_spec.rb → state_machines/path_collection_spec.rb} +0 -0
  40. data/spec/{path_spec.rb → state_machines/path_spec.rb} +0 -0
  41. data/spec/{state_collection_spec.rb → state_machines/state_collection_spec.rb} +0 -0
  42. data/spec/{state_context_spec.rb → state_machines/state_context_spec.rb} +0 -0
  43. data/spec/{state_machine_spec.rb → state_machines/state_machine_spec.rb} +0 -0
  44. data/spec/{state_spec.rb → state_machines/state_spec.rb} +0 -0
  45. data/spec/{transition_collection_spec.rb → state_machines/transition_collection_spec.rb} +0 -0
  46. data/spec/{transition_spec.rb → state_machines/transition_spec.rb} +0 -0
  47. data/spec/support/migration_helpers.rb +9 -0
  48. data/state_machines.gemspec +3 -1
  49. metadata +68 -45
  50. data/lib/state_machines/yard.rb +0 -8
  51. data/spec/errors/default_spec.rb +0 -14
  52. data/spec/errors/with_message_spec.rb +0 -39
@@ -0,0 +1 @@
1
+ # TODO
@@ -0,0 +1,401 @@
1
+ require 'spec_helper'
2
+
3
+ describe StateMachines::EventCollection do
4
+ context 'ByDefault' do
5
+ before(:each) do
6
+ @klass = Class.new
7
+ @machine = StateMachines::Machine.new(@klass)
8
+ @events = StateMachines::EventCollection.new(@machine)
9
+ @object = @klass.new
10
+ end
11
+
12
+ it 'should_not_have_any_nodes' do
13
+ assert_equal 0, @events.length
14
+ end
15
+
16
+ it 'should_have_a_machine' do
17
+ assert_equal @machine, @events.machine
18
+ end
19
+
20
+ it 'should_not_have_any_valid_events_for_an_object' do
21
+ assert @events.valid_for(@object).empty?
22
+ end
23
+
24
+ it 'should_not_have_any_transitions_for_an_object' do
25
+ assert @events.transitions_for(@object).empty?
26
+ end
27
+ end
28
+
29
+ context '' do
30
+ before(:each) do
31
+ machine = StateMachines::Machine.new(Class.new, :namespace => 'alarm')
32
+ @events = StateMachines::EventCollection.new(machine)
33
+
34
+ @events << @open = StateMachines::Event.new(machine, :enable)
35
+ machine.events.concat(@events)
36
+ end
37
+
38
+ it 'should_index_by_name' do
39
+ assert_equal @open, @events[:enable, :name]
40
+ end
41
+
42
+ it 'should_index_by_name_by_default' do
43
+ assert_equal @open, @events[:enable]
44
+ end
45
+
46
+ it 'should_index_by_string_name' do
47
+ assert_equal @open, @events['enable']
48
+ end
49
+
50
+ it 'should_index_by_qualified_name' do
51
+ assert_equal @open, @events[:enable_alarm, :qualified_name]
52
+ end
53
+
54
+ it 'should_index_by_string_qualified_name' do
55
+ assert_equal @open, @events['enable_alarm', :qualified_name]
56
+ end
57
+ end
58
+
59
+ context 'EventStringCollection' do
60
+ before(:each) do
61
+ machine = StateMachines::Machine.new(Class.new, :namespace => 'alarm')
62
+ @events = StateMachines::EventCollection.new(machine)
63
+
64
+ @events << @open = StateMachines::Event.new(machine, 'enable')
65
+ machine.events.concat(@events)
66
+ end
67
+
68
+ it 'should_index_by_name' do
69
+ assert_equal @open, @events['enable', :name]
70
+ end
71
+
72
+ it 'should_index_by_name_by_default' do
73
+ assert_equal @open, @events['enable']
74
+ end
75
+
76
+ it 'should_index_by_symbol_name' do
77
+ assert_equal @open, @events[:enable]
78
+ end
79
+
80
+ it 'should_index_by_qualified_name' do
81
+ assert_equal @open, @events['enable_alarm', :qualified_name]
82
+ end
83
+
84
+ it 'should_index_by_symbol_qualified_name' do
85
+ assert_equal @open, @events[:enable_alarm, :qualified_name]
86
+ end
87
+ end
88
+
89
+ context 'WithEventsWithTransitions' do
90
+ before(:each) do
91
+ @klass = Class.new
92
+ @machine = StateMachines::Machine.new(@klass, :initial => :parked)
93
+ @events = StateMachines::EventCollection.new(@machine)
94
+
95
+ @machine.state :idling, :first_gear
96
+
97
+ @events << @ignite = StateMachines::Event.new(@machine, :ignite)
98
+ @ignite.transition :parked => :idling
99
+
100
+ @events << @park = StateMachines::Event.new(@machine, :park)
101
+ @park.transition :idling => :parked
102
+
103
+ @events << @shift_up = StateMachines::Event.new(@machine, :shift_up)
104
+ @shift_up.transition :parked => :first_gear
105
+ @shift_up.transition :idling => :first_gear, :if => lambda { false }
106
+
107
+ @machine.events.concat(@events)
108
+
109
+ @object = @klass.new
110
+ end
111
+
112
+ it 'should_find_valid_events_based_on_current_state' do
113
+ assert_equal [@ignite, @shift_up], @events.valid_for(@object)
114
+ end
115
+
116
+ it 'should_filter_valid_events_by_from_state' do
117
+ assert_equal [@park], @events.valid_for(@object, :from => :idling)
118
+ end
119
+
120
+ it 'should_filter_valid_events_by_to_state' do
121
+ assert_equal [@shift_up], @events.valid_for(@object, :to => :first_gear)
122
+ end
123
+
124
+ it 'should_filter_valid_events_by_event' do
125
+ assert_equal [@ignite], @events.valid_for(@object, :on => :ignite)
126
+ end
127
+
128
+ it 'should_filter_valid_events_by_multiple_requirements' do
129
+ assert_equal [], @events.valid_for(@object, :from => :idling, :to => :first_gear)
130
+ end
131
+
132
+ it 'should_allow_finding_valid_events_without_guards' do
133
+ assert_equal [@shift_up], @events.valid_for(@object, :from => :idling, :to => :first_gear, :guard => false)
134
+ end
135
+
136
+ it 'should_find_valid_transitions_based_on_current_state' do
137
+ assert_equal [
138
+ StateMachines::Transition.new(@object, @machine, :ignite, :parked, :idling),
139
+ StateMachines::Transition.new(@object, @machine, :shift_up, :parked, :first_gear)
140
+ ], @events.transitions_for(@object)
141
+ end
142
+
143
+ it 'should_filter_valid_transitions_by_from_state' do
144
+ assert_equal [StateMachines::Transition.new(@object, @machine, :park, :idling, :parked)], @events.transitions_for(@object, :from => :idling)
145
+ end
146
+
147
+ it 'should_filter_valid_transitions_by_to_state' do
148
+ assert_equal [StateMachines::Transition.new(@object, @machine, :shift_up, :parked, :first_gear)], @events.transitions_for(@object, :to => :first_gear)
149
+ end
150
+
151
+ it 'should_filter_valid_transitions_by_event' do
152
+ assert_equal [StateMachines::Transition.new(@object, @machine, :ignite, :parked, :idling)], @events.transitions_for(@object, :on => :ignite)
153
+ end
154
+
155
+ it 'should_filter_valid_transitions_by_multiple_requirements' do
156
+ assert_equal [], @events.transitions_for(@object, :from => :idling, :to => :first_gear)
157
+ end
158
+
159
+ it 'should_allow_finding_valid_transitions_without_guards' do
160
+ assert_equal [StateMachines::Transition.new(@object, @machine, :shift_up, :idling, :first_gear)], @events.transitions_for(@object, :from => :idling, :to => :first_gear, :guard => false)
161
+ end
162
+ end
163
+
164
+ context 'WithMultipleEvents' do
165
+ before(:each) do
166
+ @klass = Class.new
167
+ @machine = StateMachines::Machine.new(@klass, :initial => :parked)
168
+ @events = StateMachines::EventCollection.new(@machine)
169
+
170
+ @machine.state :first_gear
171
+ @park, @shift_down = @machine.event :park, :shift_down
172
+
173
+ @events << @park
174
+ @park.transition :first_gear => :parked
175
+
176
+ @events << @shift_down
177
+ @shift_down.transition :first_gear => :parked
178
+
179
+ @machine.events.concat(@events)
180
+ end
181
+
182
+ it 'should_only_include_all_valid_events_for_an_object' do
183
+ object = @klass.new
184
+ object.state = 'first_gear'
185
+ assert_equal [@park, @shift_down], @events.valid_for(object)
186
+ end
187
+ end
188
+
189
+ context 'WithoutMachineAction' do
190
+ before(:each) do
191
+ @klass = Class.new
192
+ @machine = StateMachines::Machine.new(@klass, :initial => :parked)
193
+ @events = StateMachines::EventCollection.new(@machine)
194
+ @events << StateMachines::Event.new(@machine, :ignite)
195
+ @machine.events.concat(@events)
196
+
197
+ @object = @klass.new
198
+ end
199
+
200
+ it 'should_not_have_an_attribute_transition' do
201
+ assert_nil @events.attribute_transition_for(@object)
202
+ end
203
+ end
204
+
205
+ context 'AttributeWithMachineAction' do
206
+ before(:each) do
207
+ @klass = Class.new do
208
+ def save
209
+ end
210
+ end
211
+
212
+ @machine = StateMachines::Machine.new(@klass, :initial => :parked, :action => :save)
213
+ @events = StateMachines::EventCollection.new(@machine)
214
+
215
+ @machine.state :parked, :idling
216
+ @events << @ignite = StateMachines::Event.new(@machine, :ignite)
217
+ @machine.events.concat(@events)
218
+
219
+ @object = @klass.new
220
+ end
221
+
222
+ it 'should_not_have_transition_if_nil' do
223
+ @object.state_event = nil
224
+ assert_nil @events.attribute_transition_for(@object)
225
+ end
226
+
227
+ it 'should_not_have_transition_if_empty' do
228
+ @object.state_event = ''
229
+ assert_nil @events.attribute_transition_for(@object)
230
+ end
231
+
232
+ it 'should_have_invalid_transition_if_invalid_event_specified' do
233
+ @object.state_event = 'invalid'
234
+ assert_equal false, @events.attribute_transition_for(@object)
235
+ end
236
+
237
+ it 'should_have_invalid_transition_if_event_cannot_be_fired' do
238
+ @object.state_event = 'ignite'
239
+ assert_equal false, @events.attribute_transition_for(@object)
240
+ end
241
+
242
+ it 'should_have_valid_transition_if_event_can_be_fired' do
243
+ @ignite.transition :parked => :idling
244
+ @object.state_event = 'ignite'
245
+
246
+ assert_instance_of StateMachines::Transition, @events.attribute_transition_for(@object)
247
+ end
248
+
249
+ it 'should_have_valid_transition_if_already_defined_in_transition_cache' do
250
+ @ignite.transition :parked => :idling
251
+ @object.state_event = nil
252
+ @object.send(:state_event_transition=, transition = @ignite.transition_for(@object))
253
+
254
+ assert_equal transition, @events.attribute_transition_for(@object)
255
+ end
256
+
257
+ it 'should_use_transition_cache_if_both_event_and_transition_are_present' do
258
+ @ignite.transition :parked => :idling
259
+ @object.state_event = 'ignite'
260
+ @object.send(:state_event_transition=, transition = @ignite.transition_for(@object))
261
+
262
+ assert_equal transition, @events.attribute_transition_for(@object)
263
+ end
264
+ end
265
+
266
+ context 'AttributeWithNamespacedMachine' do
267
+ before(:each) do
268
+ @klass = Class.new do
269
+ def save
270
+ end
271
+ end
272
+
273
+ @machine = StateMachines::Machine.new(@klass, :namespace => 'alarm', :initial => :active, :action => :save)
274
+ @events = StateMachines::EventCollection.new(@machine)
275
+
276
+ @machine.state :active, :off
277
+ @events << @disable = StateMachines::Event.new(@machine, :disable)
278
+ @machine.events.concat(@events)
279
+
280
+ @object = @klass.new
281
+ end
282
+
283
+ it 'should_not_have_transition_if_nil' do
284
+ @object.state_event = nil
285
+ assert_nil @events.attribute_transition_for(@object)
286
+ end
287
+
288
+ it 'should_have_invalid_transition_if_event_cannot_be_fired' do
289
+ @object.state_event = 'disable'
290
+ assert_equal false, @events.attribute_transition_for(@object)
291
+ end
292
+
293
+ it 'should_have_valid_transition_if_event_can_be_fired' do
294
+ @disable.transition :active => :off
295
+ @object.state_event = 'disable'
296
+
297
+ assert_instance_of StateMachines::Transition, @events.attribute_transition_for(@object)
298
+ end
299
+ end
300
+
301
+ context 'WithValidations' do
302
+ before(:each) do
303
+ StateMachines::Integrations.const_set('Custom', Module.new do
304
+ include StateMachines::Integrations::Base
305
+
306
+ def invalidate(object, attribute, message, values = [])
307
+ (object.errors ||= []) << generate_message(message, values)
308
+ end
309
+
310
+ def reset(object)
311
+ object.errors = []
312
+ end
313
+ end)
314
+
315
+ @klass = Class.new do
316
+ attr_accessor :errors
317
+
318
+ def initialize
319
+ @errors = []
320
+ super
321
+ end
322
+ end
323
+
324
+ @machine = StateMachines::Machine.new(@klass, :initial => :parked, :action => :save, :integration => :custom)
325
+ @events = StateMachines::EventCollection.new(@machine)
326
+
327
+ @parked, @idling = @machine.state :parked, :idling
328
+ @events << @ignite = StateMachines::Event.new(@machine, :ignite)
329
+ @machine.events.concat(@events)
330
+
331
+ @object = @klass.new
332
+ end
333
+
334
+ it 'should_invalidate_if_invalid_event_specified' do
335
+ @object.state_event = 'invalid'
336
+ @events.attribute_transition_for(@object, true)
337
+
338
+ assert_equal ['is invalid'], @object.errors
339
+ end
340
+
341
+ it 'should_invalidate_if_event_cannot_be_fired' do
342
+ @object.state = 'idling'
343
+ @object.state_event = 'ignite'
344
+ @events.attribute_transition_for(@object, true)
345
+
346
+ assert_equal ['cannot transition when idling'], @object.errors
347
+ end
348
+
349
+ it 'should_invalidate_with_human_name_if_invalid_event_specified' do
350
+ @idling.human_name = 'waiting'
351
+ @object.state = 'idling'
352
+ @object.state_event = 'ignite'
353
+ @events.attribute_transition_for(@object, true)
354
+
355
+ assert_equal ['cannot transition when waiting'], @object.errors
356
+ end
357
+
358
+ it 'should_not_invalidate_event_can_be_fired' do
359
+ @ignite.transition :parked => :idling
360
+ @object.state_event = 'ignite'
361
+ @events.attribute_transition_for(@object, true)
362
+
363
+ assert_equal [], @object.errors
364
+ end
365
+
366
+ after(:each) do
367
+ StateMachines::Integrations.send(:remove_const, 'Custom')
368
+ StateMachines::Integrations.send(:reset)
369
+ end
370
+ end
371
+
372
+ context 'WithCustomMachineAttribute' do
373
+ before(:each) do
374
+ @klass = Class.new do
375
+ def save
376
+ end
377
+ end
378
+
379
+ @machine = StateMachines::Machine.new(@klass, :state, :attribute => :state_id, :initial => :parked, :action => :save)
380
+ @events = StateMachines::EventCollection.new(@machine)
381
+
382
+ @machine.state :parked, :idling
383
+ @events << @ignite = StateMachines::Event.new(@machine, :ignite)
384
+ @machine.events.concat(@events)
385
+
386
+ @object = @klass.new
387
+ end
388
+
389
+ it 'should_not_have_transition_if_nil' do
390
+ @object.state_event = nil
391
+ assert_nil @events.attribute_transition_for(@object)
392
+ end
393
+
394
+ it 'should_have_valid_transition_if_event_can_be_fired' do
395
+ @ignite.transition :parked => :idling
396
+ @object.state_event = 'ignite'
397
+
398
+ assert_instance_of StateMachines::Transition, @events.attribute_transition_for(@object)
399
+ end
400
+ end
401
+ end
@@ -0,0 +1,1140 @@
1
+ require 'spec_helper'
2
+
3
+ describe StateMachines::Event do
4
+ context 'ByDefault' do
5
+ before(:each) do
6
+ @klass = Class.new
7
+ @machine = StateMachines::Machine.new(@klass)
8
+ @machine.events << @event = StateMachines::Event.new(@machine, :ignite)
9
+
10
+ @object = @klass.new
11
+ end
12
+
13
+ it 'should_have_a_machine' do
14
+ assert_equal @machine, @event.machine
15
+ end
16
+
17
+ it 'should_have_a_name' do
18
+ assert_equal :ignite, @event.name
19
+ end
20
+
21
+ it 'should_have_a_qualified_name' do
22
+ assert_equal :ignite, @event.qualified_name
23
+ end
24
+
25
+ it 'should_have_a_human_name' do
26
+ assert_equal 'ignite', @event.human_name
27
+ end
28
+
29
+ it 'should_not_have_any_branches' do
30
+ assert @event.branches.empty?
31
+ end
32
+
33
+ it 'should_have_no_known_states' do
34
+ assert @event.known_states.empty?
35
+ end
36
+
37
+ it 'should_not_be_able_to_fire' do
38
+ assert !@event.can_fire?(@object)
39
+ end
40
+
41
+ it 'should_not_have_a_transition' do
42
+ assert_nil @event.transition_for(@object)
43
+ end
44
+
45
+ it 'should_define_a_predicate' do
46
+ assert @object.respond_to?(:can_ignite?)
47
+ end
48
+
49
+ it 'should_define_a_transition_accessor' do
50
+ assert @object.respond_to?(:ignite_transition)
51
+ end
52
+
53
+ it 'should_define_an_action' do
54
+ assert @object.respond_to?(:ignite)
55
+ end
56
+
57
+ it 'should_define_a_bang_action' do
58
+ assert @object.respond_to?(:ignite!)
59
+ end
60
+ end
61
+
62
+ context '' do
63
+ before(:each) do
64
+ @machine = StateMachines::Machine.new(Class.new)
65
+ @machine.events << @event = StateMachines::Event.new(@machine, :ignite)
66
+ @event.transition :parked => :idling
67
+ end
68
+
69
+ it 'should_allow_changing_machine' do
70
+ new_machine = StateMachines::Machine.new(Class.new)
71
+ @event.machine = new_machine
72
+ assert_equal new_machine, @event.machine
73
+ end
74
+
75
+ it 'should_allow_changing_human_name' do
76
+ @event.human_name = 'Stop'
77
+ assert_equal 'Stop', @event.human_name
78
+ end
79
+
80
+ it 'should_provide_matcher_helpers_during_initialization' do
81
+ matchers = []
82
+
83
+ @event.instance_eval do
84
+ matchers = [all, any, same]
85
+ end
86
+
87
+ assert_equal [StateMachines::AllMatcher.instance, StateMachines::AllMatcher.instance, StateMachines::LoopbackMatcher.instance], matchers
88
+ end
89
+
90
+ it 'should_use_pretty_inspect' do
91
+ assert_match "#<StateMachines::Event name=:ignite transitions=[:parked => :idling]>", @event.inspect
92
+ end
93
+ end
94
+
95
+ context 'WithHumanName' do
96
+ before(:each) do
97
+ @klass = Class.new
98
+ @machine = StateMachines::Machine.new(@klass)
99
+ @machine.events << @event = StateMachines::Event.new(@machine, :ignite, :human_name => 'start')
100
+ end
101
+
102
+ it 'should_use_custom_human_name' do
103
+ assert_equal 'start', @event.human_name
104
+ end
105
+ end
106
+
107
+ context 'WithDynamicHumanName' do
108
+ before(:each) do
109
+ @klass = Class.new
110
+ @machine = StateMachines::Machine.new(@klass)
111
+ @machine.events << @event = StateMachines::Event.new(@machine, :ignite, :human_name => lambda { |event, object| ['start', object] })
112
+ end
113
+
114
+ it 'should_use_custom_human_name' do
115
+ human_name, klass = @event.human_name
116
+ assert_equal 'start', human_name
117
+ assert_equal @klass, klass
118
+ end
119
+
120
+ it 'should_allow_custom_class_to_be_passed_through' do
121
+ human_name, klass = @event.human_name(1)
122
+ assert_equal 'start', human_name
123
+ assert_equal 1, klass
124
+ end
125
+
126
+ it 'should_not_cache_value' do
127
+ assert_not_same @event.human_name, @event.human_name
128
+ end
129
+ end
130
+
131
+ context 'WithConflictingHelpersBeforeDefinition' do
132
+ before(:each) do
133
+ require 'stringio'
134
+ @original_stderr, $stderr = $stderr, StringIO.new
135
+
136
+ @superclass = Class.new do
137
+ def can_ignite?
138
+ 0
139
+ end
140
+
141
+ def ignite_transition
142
+ 0
143
+ end
144
+
145
+ def ignite
146
+ 0
147
+ end
148
+
149
+ def ignite!
150
+ 0
151
+ end
152
+ end
153
+ @klass = Class.new(@superclass)
154
+ @machine = StateMachines::Machine.new(@klass)
155
+ @machine.events << @event = StateMachines::Event.new(@machine, :ignite)
156
+ @object = @klass.new
157
+ end
158
+
159
+ it 'should_not_redefine_predicate' do
160
+ assert_equal 0, @object.can_ignite?
161
+ end
162
+
163
+ it 'should_not_redefine_transition_accessor' do
164
+ assert_equal 0, @object.ignite_transition
165
+ end
166
+
167
+ it 'should_not_redefine_action' do
168
+ assert_equal 0, @object.ignite
169
+ end
170
+
171
+ it 'should_not_redefine_bang_action' do
172
+ assert_equal 0, @object.ignite!
173
+ end
174
+
175
+ it 'should_output_warning' do
176
+ expected = %w(can_ignite? ignite_transition ignite ignite!).map do |method|
177
+ "Instance method \"#{method}\" is already defined in #{@superclass.to_s}, use generic helper instead or set StateMachines::Machine.ignore_method_conflicts = true.\n"
178
+ end.join
179
+
180
+ assert_equal expected, $stderr.string
181
+ end
182
+
183
+ after(:each) do
184
+ $stderr = @original_stderr
185
+ end
186
+ end
187
+
188
+ context 'WithConflictingHelpersAfterDefinition' do
189
+ before(:each) do
190
+ require 'stringio'
191
+ @original_stderr, $stderr = $stderr, StringIO.new
192
+
193
+ @klass = Class.new do
194
+ def can_ignite?
195
+ 0
196
+ end
197
+
198
+ def ignite_transition
199
+ 0
200
+ end
201
+
202
+ def ignite
203
+ 0
204
+ end
205
+
206
+ def ignite!
207
+ 0
208
+ end
209
+ end
210
+ @machine = StateMachines::Machine.new(@klass)
211
+ @machine.events << @event = StateMachines::Event.new(@machine, :ignite)
212
+ @object = @klass.new
213
+ end
214
+
215
+ it 'should_not_redefine_predicate' do
216
+ assert_equal 0, @object.can_ignite?
217
+ end
218
+
219
+ it 'should_not_redefine_transition_accessor' do
220
+ assert_equal 0, @object.ignite_transition
221
+ end
222
+
223
+ it 'should_not_redefine_action' do
224
+ assert_equal 0, @object.ignite
225
+ end
226
+
227
+ it 'should_not_redefine_bang_action' do
228
+ assert_equal 0, @object.ignite!
229
+ end
230
+
231
+ it 'should_allow_super_chaining' do
232
+ @klass.class_eval do
233
+ def can_ignite?
234
+ super
235
+ end
236
+
237
+ def ignite_transition
238
+ super
239
+ end
240
+
241
+ def ignite
242
+ super
243
+ end
244
+
245
+ def ignite!
246
+ super
247
+ end
248
+ end
249
+
250
+ assert_equal false, @object.can_ignite?
251
+ assert_equal nil, @object.ignite_transition
252
+ assert_equal false, @object.ignite
253
+ assert_raise(StateMachines::InvalidTransition) { @object.ignite! }
254
+ end
255
+
256
+ it 'should_not_output_warning' do
257
+ assert_equal '', $stderr.string
258
+ end
259
+
260
+ after(:each) do
261
+ $stderr = @original_stderr
262
+ end
263
+ end
264
+
265
+ context 'WithConflictingMachine' do
266
+ before(:each) do
267
+ require 'stringio'
268
+ @original_stderr, $stderr = $stderr, StringIO.new
269
+
270
+ @klass = Class.new
271
+ @state_machine = StateMachines::Machine.new(@klass, :state)
272
+ @state_machine.state :parked, :idling
273
+ @state_machine.events << @state_event = StateMachines::Event.new(@state_machine, :ignite)
274
+ end
275
+
276
+ it 'should_not_overwrite_first_event' do
277
+ @status_machine = StateMachines::Machine.new(@klass, :status)
278
+ @status_machine.state :first_gear, :second_gear
279
+ @status_machine.events << @status_event = StateMachines::Event.new(@status_machine, :ignite)
280
+
281
+ @object = @klass.new
282
+ @object.state = 'parked'
283
+ @object.status = 'first_gear'
284
+
285
+ @state_event.transition(:parked => :idling)
286
+ @status_event.transition(:parked => :first_gear)
287
+
288
+ @object.ignite
289
+ assert_equal 'idling', @object.state
290
+ assert_equal 'first_gear', @object.status
291
+ end
292
+
293
+ it 'should_output_warning' do
294
+ @status_machine = StateMachines::Machine.new(@klass, :status)
295
+ @status_machine.events << @status_event = StateMachines::Event.new(@status_machine, :ignite)
296
+
297
+ assert_equal "Event :ignite for :status is already defined in :state\n", $stderr.string
298
+ end
299
+
300
+ it 'should_not_output_warning_if_using_different_namespace' do
301
+ @status_machine = StateMachines::Machine.new(@klass, :status, :namespace => 'alarm')
302
+ @status_machine.events << @status_event = StateMachines::Event.new(@status_machine, :ignite)
303
+
304
+ assert_equal '', $stderr.string
305
+ end
306
+
307
+ after(:each) do
308
+ $stderr = @original_stderr
309
+ end
310
+ end
311
+
312
+ context 'WithNamespace' do
313
+ before(:each) do
314
+ @klass = Class.new
315
+ @machine = StateMachines::Machine.new(@klass, :namespace => 'alarm')
316
+ @machine.events << @event = StateMachines::Event.new(@machine, :enable)
317
+ @object = @klass.new
318
+ end
319
+
320
+ it 'should_have_a_name' do
321
+ assert_equal :enable, @event.name
322
+ end
323
+
324
+ it 'should_have_a_qualified_name' do
325
+ assert_equal :enable_alarm, @event.qualified_name
326
+ end
327
+
328
+ it 'should_namespace_predicate' do
329
+ assert @object.respond_to?(:can_enable_alarm?)
330
+ end
331
+
332
+ it 'should_namespace_transition_accessor' do
333
+ assert @object.respond_to?(:enable_alarm_transition)
334
+ end
335
+
336
+ it 'should_namespace_action' do
337
+ assert @object.respond_to?(:enable_alarm)
338
+ end
339
+
340
+ it 'should_namespace_bang_action' do
341
+ assert @object.respond_to?(:enable_alarm!)
342
+ end
343
+ end
344
+
345
+ context 'Context' do
346
+ before(:each) do
347
+ @klass = Class.new
348
+ @machine = StateMachines::Machine.new(@klass)
349
+ @machine.events << @event = StateMachines::Event.new(@machine, :ignite, :human_name => 'start')
350
+ end
351
+
352
+ it 'should_evaluate_within_the_event' do
353
+ scope = nil
354
+ @event.context { scope = self }
355
+ assert_equal @event, scope
356
+ end
357
+ end
358
+
359
+ context 'Transitions' do
360
+ before(:each) do
361
+ @machine = StateMachines::Machine.new(Class.new)
362
+ @machine.events << @event = StateMachines::Event.new(@machine, :ignite)
363
+ end
364
+
365
+ it 'should_not_raise_exception_if_implicit_option_specified' do
366
+ assert_nothing_raised { @event.transition(:invalid => :valid) }
367
+ end
368
+
369
+ it 'should_not_allow_on_option' do
370
+ assert_raise(ArgumentError) { @event.transition(:on => :ignite) }
371
+ end
372
+
373
+ it 'should_automatically_set_on_option' do
374
+ branch = @event.transition(:to => :idling)
375
+ assert_instance_of StateMachines::WhitelistMatcher, branch.event_requirement
376
+ assert_equal [:ignite], branch.event_requirement.values
377
+ end
378
+
379
+ it 'should_not_allow_except_on_option' do
380
+ assert_raise(ArgumentError) { @event.transition(:except_on => :ignite) }
381
+ end
382
+
383
+ it 'should_allow_transitioning_without_a_to_state' do
384
+ assert_nothing_raised { @event.transition(:from => :parked) }
385
+ end
386
+
387
+ it 'should_allow_transitioning_without_a_from_state' do
388
+ assert_nothing_raised { @event.transition(:to => :idling) }
389
+ end
390
+
391
+ it 'should_allow_except_from_option' do
392
+ assert_nothing_raised { @event.transition(:except_from => :idling) }
393
+ end
394
+
395
+ it 'should_allow_except_to_option' do
396
+ assert_nothing_raised { @event.transition(:except_to => :idling) }
397
+ end
398
+
399
+ it 'should_allow_transitioning_from_a_single_state' do
400
+ assert @event.transition(:parked => :idling)
401
+ end
402
+
403
+ it 'should_allow_transitioning_from_multiple_states' do
404
+ assert @event.transition([:parked, :idling] => :idling)
405
+ end
406
+
407
+ it 'should_allow_transitions_to_multiple_states' do
408
+ assert @event.transition(:parked => [:parked, :idling])
409
+ end
410
+
411
+ it 'should_have_transitions' do
412
+ branch = @event.transition(:to => :idling)
413
+ assert_equal [branch], @event.branches
414
+ end
415
+ end
416
+
417
+ context 'AfterBeingCopied' do
418
+ before(:each) do
419
+ @machine = StateMachines::Machine.new(Class.new)
420
+ @machine.events << @event = StateMachines::Event.new(@machine, :ignite)
421
+ @copied_event = @event.dup
422
+ end
423
+
424
+ it 'should_not_have_the_same_collection_of_branches' do
425
+ assert_not_same @event.branches, @copied_event.branches
426
+ end
427
+
428
+ it 'should_not_have_the_same_collection_of_known_states' do
429
+ assert_not_same @event.known_states, @copied_event.known_states
430
+ end
431
+ end
432
+
433
+ context 'WithoutTransitions' do
434
+ before(:each) do
435
+ @klass = Class.new
436
+ @machine = StateMachines::Machine.new(@klass)
437
+ @machine.events << @event = StateMachines::Event.new(@machine, :ignite)
438
+ @object = @klass.new
439
+ end
440
+
441
+ it 'should_not_be_able_to_fire' do
442
+ assert !@event.can_fire?(@object)
443
+ end
444
+
445
+ it 'should_not_have_a_transition' do
446
+ assert_nil @event.transition_for(@object)
447
+ end
448
+
449
+ it 'should_not_fire' do
450
+ assert !@event.fire(@object)
451
+ end
452
+
453
+ it 'should_not_change_the_current_state' do
454
+ @event.fire(@object)
455
+ assert_nil @object.state
456
+ end
457
+ end
458
+
459
+ context 'WithTransitions' do
460
+ before(:each) do
461
+ @klass = Class.new
462
+ @machine = StateMachines::Machine.new(@klass)
463
+ @machine.events << @event = StateMachines::Event.new(@machine, :ignite)
464
+ @event.transition(:parked => :idling)
465
+ @event.transition(:first_gear => :idling)
466
+ end
467
+
468
+ it 'should_include_all_transition_states_in_known_states' do
469
+ assert_equal [:parked, :idling, :first_gear], @event.known_states
470
+ end
471
+
472
+ it 'should_include_new_transition_states_after_calling_known_states' do
473
+ @event.known_states
474
+ @event.transition(:stalled => :idling)
475
+
476
+ assert_equal [:parked, :idling, :first_gear, :stalled], @event.known_states
477
+ end
478
+
479
+ it 'should_clear_known_states_on_reset' do
480
+ @event.reset
481
+ assert_equal [], @event.known_states
482
+ end
483
+
484
+ it 'should_use_pretty_inspect' do
485
+ assert_match "#<StateMachines::Event name=:ignite transitions=[:parked => :idling, :first_gear => :idling]>", @event.inspect
486
+ end
487
+ end
488
+
489
+ context 'WithoutMatchingTransitions' do
490
+ before(:each) do
491
+ @klass = Class.new
492
+ @machine = StateMachines::Machine.new(@klass)
493
+ @machine.state :parked, :idling
494
+
495
+ @machine.events << @event = StateMachines::Event.new(@machine, :ignite)
496
+ @event.transition(:parked => :idling)
497
+
498
+ @object = @klass.new
499
+ @object.state = 'idling'
500
+ end
501
+
502
+ it 'should_not_be_able_to_fire' do
503
+ assert !@event.can_fire?(@object)
504
+ end
505
+
506
+ it 'should_be_able_to_fire_with_custom_from_state' do
507
+ assert @event.can_fire?(@object, :from => :parked)
508
+ end
509
+
510
+ it 'should_not_have_a_transition' do
511
+ assert_nil @event.transition_for(@object)
512
+ end
513
+
514
+ it 'should_have_a_transition_with_custom_from_state' do
515
+ assert_not_nil @event.transition_for(@object, :from => :parked)
516
+ end
517
+
518
+ it 'should_not_fire' do
519
+ assert !@event.fire(@object)
520
+ end
521
+
522
+ it 'should_not_change_the_current_state' do
523
+ @event.fire(@object)
524
+ assert_equal 'idling', @object.state
525
+ end
526
+ end
527
+
528
+ context 'WithMatchingDisabledTransitions' do
529
+ before(:each) do
530
+ StateMachines::Integrations.const_set('Custom', Module.new do
531
+ include StateMachines::Integrations::Base
532
+
533
+ def invalidate(object, attribute, message, values = [])
534
+ (object.errors ||= []) << generate_message(message, values)
535
+ end
536
+
537
+ def reset(object)
538
+ object.errors = []
539
+ end
540
+ end)
541
+
542
+ @klass = Class.new do
543
+ attr_accessor :errors
544
+ end
545
+
546
+ @machine = StateMachines::Machine.new(@klass, :integration => :custom)
547
+ @machine.state :parked, :idling
548
+
549
+ @machine.events << @event = StateMachines::Event.new(@machine, :ignite)
550
+ @event.transition(:parked => :idling, :if => lambda { false })
551
+
552
+ @object = @klass.new
553
+ @object.state = 'parked'
554
+ end
555
+
556
+ it 'should_not_be_able_to_fire' do
557
+ assert !@event.can_fire?(@object)
558
+ end
559
+
560
+ it 'should_be_able_to_fire_with_disabled_guards' do
561
+ assert @event.can_fire?(@object, :guard => false)
562
+ end
563
+
564
+ it 'should_not_have_a_transition' do
565
+ assert_nil @event.transition_for(@object)
566
+ end
567
+
568
+ it 'should_have_a_transition_with_disabled_guards' do
569
+ assert_not_nil @event.transition_for(@object, :guard => false)
570
+ end
571
+
572
+ it 'should_not_fire' do
573
+ assert !@event.fire(@object)
574
+ end
575
+
576
+ it 'should_not_change_the_current_state' do
577
+ @event.fire(@object)
578
+ assert_equal 'parked', @object.state
579
+ end
580
+
581
+ it 'should_invalidate_the_state' do
582
+ @event.fire(@object)
583
+ assert_equal ['cannot transition via "ignite"'], @object.errors
584
+ end
585
+
586
+ it 'should_invalidate_with_human_event_name' do
587
+ @event.human_name = 'start'
588
+ @event.fire(@object)
589
+ assert_equal ['cannot transition via "start"'], @object.errors
590
+ end
591
+
592
+ it 'should_invalid_with_human_state_name_if_specified' do
593
+ klass = Class.new do
594
+ attr_accessor :errors
595
+ end
596
+
597
+ machine = StateMachines::Machine.new(klass, :integration => :custom, :messages => {:invalid_transition => 'cannot transition via "%s" from "%s"'})
598
+ parked, idling = machine.state :parked, :idling
599
+ parked.human_name = 'stopped'
600
+
601
+ machine.events << event = StateMachines::Event.new(machine, :ignite)
602
+ event.transition(:parked => :idling, :if => lambda { false })
603
+
604
+ object = @klass.new
605
+ object.state = 'parked'
606
+
607
+ event.fire(object)
608
+ assert_equal ['cannot transition via "ignite" from "stopped"'], object.errors
609
+ end
610
+
611
+ it 'should_reset_existing_error' do
612
+ @object.errors = ['invalid']
613
+
614
+ @event.fire(@object)
615
+ assert_equal ['cannot transition via "ignite"'], @object.errors
616
+ end
617
+
618
+ it 'should_run_failure_callbacks' do
619
+ callback_args = nil
620
+ @machine.after_failure { |*args| callback_args = args }
621
+
622
+ @event.fire(@object)
623
+
624
+ object, transition = callback_args
625
+ assert_equal @object, object
626
+ assert_not_nil transition
627
+ assert_equal @object, transition.object
628
+ assert_equal @machine, transition.machine
629
+ assert_equal :ignite, transition.event
630
+ assert_equal :parked, transition.from_name
631
+ assert_equal :parked, transition.to_name
632
+ end
633
+
634
+ after(:each) do
635
+ StateMachines::Integrations.send(:remove_const, 'Custom')
636
+ StateMachines::Integrations.send(:reset)
637
+ end
638
+ end
639
+
640
+ context 'WithMatchingEnabledTransitions' do
641
+ before(:each) do
642
+ StateMachines::Integrations.const_set('Custom', Module.new do
643
+ include StateMachines::Integrations::Base
644
+
645
+ def invalidate(object, attribute, message, values = [])
646
+ (object.errors ||= []) << generate_message(message, values)
647
+ end
648
+
649
+ def reset(object)
650
+ object.errors = []
651
+ end
652
+ end)
653
+
654
+ @klass = Class.new do
655
+ attr_accessor :errors
656
+ end
657
+
658
+ @machine = StateMachines::Machine.new(@klass, :integration => :custom)
659
+ @machine.state :parked, :idling
660
+
661
+ @machine.events << @event = StateMachines::Event.new(@machine, :ignite)
662
+ @event.transition(:parked => :idling)
663
+
664
+ @object = @klass.new
665
+ @object.state = 'parked'
666
+ end
667
+
668
+ it 'should_be_able_to_fire' do
669
+ assert @event.can_fire?(@object)
670
+ end
671
+
672
+ it 'should_have_a_transition' do
673
+ transition = @event.transition_for(@object)
674
+ assert_not_nil transition
675
+ assert_equal 'parked', transition.from
676
+ assert_equal 'idling', transition.to
677
+ assert_equal :ignite, transition.event
678
+ end
679
+
680
+ it 'should_fire' do
681
+ assert @event.fire(@object)
682
+ end
683
+
684
+ it 'should_change_the_current_state' do
685
+ @event.fire(@object)
686
+ assert_equal 'idling', @object.state
687
+ end
688
+
689
+ it 'should_reset_existing_error' do
690
+ @object.errors = ['invalid']
691
+
692
+ @event.fire(@object)
693
+ assert_equal [], @object.errors
694
+ end
695
+
696
+ it 'should_not_invalidate_the_state' do
697
+ @event.fire(@object)
698
+ assert_equal [], @object.errors
699
+ end
700
+
701
+ it 'should_not_be_able_to_fire_on_reset' do
702
+ @event.reset
703
+ assert !@event.can_fire?(@object)
704
+ end
705
+
706
+ after(:each) do
707
+ StateMachines::Integrations.send(:remove_const, 'Custom')
708
+ StateMachines::Integrations.send(:reset)
709
+ end
710
+ end
711
+
712
+ context 'WithTransitionWithoutToState' do
713
+ before(:each) do
714
+ @klass = Class.new
715
+ @machine = StateMachines::Machine.new(@klass)
716
+ @machine.state :parked
717
+
718
+ @machine.events << @event = StateMachines::Event.new(@machine, :park)
719
+ @event.transition(:from => :parked)
720
+
721
+ @object = @klass.new
722
+ @object.state = 'parked'
723
+ end
724
+
725
+ it 'should_be_able_to_fire' do
726
+ assert @event.can_fire?(@object)
727
+ end
728
+
729
+ it 'should_have_a_transition' do
730
+ transition = @event.transition_for(@object)
731
+ assert_not_nil transition
732
+ assert_equal 'parked', transition.from
733
+ assert_equal 'parked', transition.to
734
+ assert_equal :park, transition.event
735
+ end
736
+
737
+ it 'should_fire' do
738
+ assert @event.fire(@object)
739
+ end
740
+
741
+ it 'should_not_change_the_current_state' do
742
+ @event.fire(@object)
743
+ assert_equal 'parked', @object.state
744
+ end
745
+ end
746
+
747
+ context 'WithTransitionWithNilToState' do
748
+ before(:each) do
749
+ @klass = Class.new
750
+ @machine = StateMachines::Machine.new(@klass)
751
+ @machine.state nil, :idling
752
+
753
+ @machine.events << @event = StateMachines::Event.new(@machine, :park)
754
+ @event.transition(:idling => nil)
755
+
756
+ @object = @klass.new
757
+ @object.state = 'idling'
758
+ end
759
+
760
+ it 'should_be_able_to_fire' do
761
+ assert @event.can_fire?(@object)
762
+ end
763
+
764
+ it 'should_have_a_transition' do
765
+ transition = @event.transition_for(@object)
766
+ assert_not_nil transition
767
+ assert_equal 'idling', transition.from
768
+ assert_equal nil, transition.to
769
+ assert_equal :park, transition.event
770
+ end
771
+
772
+ it 'should_fire' do
773
+ assert @event.fire(@object)
774
+ end
775
+
776
+ it 'should_not_change_the_current_state' do
777
+ @event.fire(@object)
778
+ assert_equal nil, @object.state
779
+ end
780
+ end
781
+
782
+ context 'WithTransitionWithLoopbackState' do
783
+ before(:each) do
784
+ @klass = Class.new
785
+ @machine = StateMachines::Machine.new(@klass)
786
+ @machine.state :parked
787
+
788
+ @machine.events << @event = StateMachines::Event.new(@machine, :park)
789
+ @event.transition(:from => :parked, :to => StateMachines::LoopbackMatcher.instance)
790
+
791
+ @object = @klass.new
792
+ @object.state = 'parked'
793
+ end
794
+
795
+ it 'should_be_able_to_fire' do
796
+ assert @event.can_fire?(@object)
797
+ end
798
+
799
+ it 'should_have_a_transition' do
800
+ transition = @event.transition_for(@object)
801
+ assert_not_nil transition
802
+ assert_equal 'parked', transition.from
803
+ assert_equal 'parked', transition.to
804
+ assert_equal :park, transition.event
805
+ end
806
+
807
+ it 'should_fire' do
808
+ assert @event.fire(@object)
809
+ end
810
+
811
+ it 'should_not_change_the_current_state' do
812
+ @event.fire(@object)
813
+ assert_equal 'parked', @object.state
814
+ end
815
+ end
816
+
817
+ context 'WithTransitionWithBlacklistedToState' do
818
+ before(:each) do
819
+ @klass = Class.new
820
+ @machine = StateMachines::Machine.new(@klass, :initial => :parked)
821
+ @machine.state :parked, :idling, :first_gear, :second_gear
822
+
823
+ @machine.events << @event = StateMachines::Event.new(@machine, :ignite)
824
+ @event.transition(:from => :parked, :to => StateMachines::BlacklistMatcher.new([:parked, :idling]))
825
+
826
+ @object = @klass.new
827
+ @object.state = 'parked'
828
+ end
829
+
830
+ it 'should_be_able_to_fire' do
831
+ assert @event.can_fire?(@object)
832
+ end
833
+
834
+ it 'should_have_a_transition' do
835
+ transition = @event.transition_for(@object)
836
+ assert_not_nil transition
837
+ assert_equal 'parked', transition.from
838
+ assert_equal 'first_gear', transition.to
839
+ assert_equal :ignite, transition.event
840
+ end
841
+
842
+ it 'should_allow_loopback_first_when_possible' do
843
+ @event.transition(:from => :second_gear, :to => StateMachines::BlacklistMatcher.new([:parked, :idling]))
844
+ @object.state = 'second_gear'
845
+
846
+ transition = @event.transition_for(@object)
847
+ assert_not_nil transition
848
+ assert_equal 'second_gear', transition.from
849
+ assert_equal 'second_gear', transition.to
850
+ assert_equal :ignite, transition.event
851
+ end
852
+
853
+ it 'should_allow_specific_transition_selection_using_to' do
854
+ transition = @event.transition_for(@object, :from => :parked, :to => :second_gear)
855
+
856
+ assert_not_nil transition
857
+ assert_equal 'parked', transition.from
858
+ assert_equal 'second_gear', transition.to
859
+ assert_equal :ignite, transition.event
860
+ end
861
+
862
+ it 'should_not_allow_transition_selection_if_not_matching' do
863
+ transition = @event.transition_for(@object, :from => :parked, :to => :parked)
864
+ assert_nil transition
865
+ end
866
+
867
+ it 'should_fire' do
868
+ assert @event.fire(@object)
869
+ end
870
+
871
+ it 'should_change_the_current_state' do
872
+ @event.fire(@object)
873
+ assert_equal 'first_gear', @object.state
874
+ end
875
+ end
876
+
877
+ context 'WithTransitionWithWhitelistedToState' do
878
+ before(:each) do
879
+ @klass = Class.new
880
+ @machine = StateMachines::Machine.new(@klass, :initial => :parked)
881
+ @machine.state :parked, :idling, :first_gear, :second_gear
882
+
883
+ @machine.events << @event = StateMachines::Event.new(@machine, :ignite)
884
+ @event.transition(:from => :parked, :to => StateMachines::WhitelistMatcher.new([:first_gear, :second_gear]))
885
+
886
+ @object = @klass.new
887
+ @object.state = 'parked'
888
+ end
889
+
890
+ it 'should_be_able_to_fire' do
891
+ assert @event.can_fire?(@object)
892
+ end
893
+
894
+ it 'should_have_a_transition' do
895
+ transition = @event.transition_for(@object)
896
+ assert_not_nil transition
897
+ assert_equal 'parked', transition.from
898
+ assert_equal 'first_gear', transition.to
899
+ assert_equal :ignite, transition.event
900
+ end
901
+
902
+ it 'should_allow_specific_transition_selection_using_to' do
903
+ transition = @event.transition_for(@object, :from => :parked, :to => :second_gear)
904
+
905
+ assert_not_nil transition
906
+ assert_equal 'parked', transition.from
907
+ assert_equal 'second_gear', transition.to
908
+ assert_equal :ignite, transition.event
909
+ end
910
+
911
+ it 'should_not_allow_transition_selection_if_not_matching' do
912
+ transition = @event.transition_for(@object, :from => :parked, :to => :parked)
913
+ assert_nil transition
914
+ end
915
+
916
+ it 'should_fire' do
917
+ assert @event.fire(@object)
918
+ end
919
+
920
+ it 'should_change_the_current_state' do
921
+ @event.fire(@object)
922
+ assert_equal 'first_gear', @object.state
923
+ end
924
+ end
925
+
926
+ context 'WithMultipleTransitions' do
927
+ before(:each) do
928
+ @klass = Class.new
929
+ @machine = StateMachines::Machine.new(@klass)
930
+ @machine.state :parked, :idling
931
+
932
+ @machine.events << @event = StateMachines::Event.new(@machine, :ignite)
933
+ @event.transition(:idling => :idling)
934
+ @event.transition(:parked => :idling)
935
+ @event.transition(:parked => :parked)
936
+
937
+ @object = @klass.new
938
+ @object.state = 'parked'
939
+ end
940
+
941
+ it 'should_be_able_to_fire' do
942
+ assert @event.can_fire?(@object)
943
+ end
944
+
945
+ it 'should_have_a_transition' do
946
+ transition = @event.transition_for(@object)
947
+ assert_not_nil transition
948
+ assert_equal 'parked', transition.from
949
+ assert_equal 'idling', transition.to
950
+ assert_equal :ignite, transition.event
951
+ end
952
+
953
+ it 'should_allow_specific_transition_selection_using_from' do
954
+ transition = @event.transition_for(@object, :from => :idling)
955
+
956
+ assert_not_nil transition
957
+ assert_equal 'idling', transition.from
958
+ assert_equal 'idling', transition.to
959
+ assert_equal :ignite, transition.event
960
+ end
961
+
962
+ it 'should_allow_specific_transition_selection_using_to' do
963
+ transition = @event.transition_for(@object, :from => :parked, :to => :parked)
964
+
965
+ assert_not_nil transition
966
+ assert_equal 'parked', transition.from
967
+ assert_equal 'parked', transition.to
968
+ assert_equal :ignite, transition.event
969
+ end
970
+
971
+ it 'should_not_allow_specific_transition_selection_using_on' do
972
+ assert_raise(ArgumentError) { @event.transition_for(@object, :on => :park) }
973
+ end
974
+
975
+ it 'should_fire' do
976
+ assert @event.fire(@object)
977
+ end
978
+
979
+ it 'should_change_the_current_state' do
980
+ @event.fire(@object)
981
+ assert_equal 'idling', @object.state
982
+ end
983
+ end
984
+
985
+ context 'WithMachineAction' do
986
+ before(:each) do
987
+ @klass = Class.new do
988
+ attr_reader :saved
989
+
990
+ def save
991
+ @saved = true
992
+ end
993
+ end
994
+
995
+ @machine = StateMachines::Machine.new(@klass, :action => :save)
996
+ @machine.state :parked, :idling
997
+
998
+ @machine.events << @event = StateMachines::Event.new(@machine, :ignite)
999
+ @event.transition(:parked => :idling)
1000
+
1001
+ @object = @klass.new
1002
+ @object.state = 'parked'
1003
+ end
1004
+
1005
+ it 'should_run_action_on_fire' do
1006
+ @event.fire(@object)
1007
+ assert @object.saved
1008
+ end
1009
+
1010
+ it 'should_not_run_action_if_configured_to_skip' do
1011
+ @event.fire(@object, false)
1012
+ assert !@object.saved
1013
+ end
1014
+ end
1015
+
1016
+ context 'WithInvalidCurrentState' do
1017
+ before(:each) do
1018
+ @klass = Class.new
1019
+ @machine = StateMachines::Machine.new(@klass)
1020
+ @machine.state :parked, :idling
1021
+
1022
+ @machine.events << @event = StateMachines::Event.new(@machine, :ignite)
1023
+ @event.transition(:parked => :idling)
1024
+
1025
+ @object = @klass.new
1026
+ @object.state = 'invalid'
1027
+ end
1028
+
1029
+ it 'should_raise_exception_when_checking_availability' do
1030
+ assert_raise(ArgumentError) { @event.can_fire?(@object) }
1031
+ end
1032
+
1033
+ it 'should_raise_exception_when_finding_transition' do
1034
+ assert_raise(ArgumentError) { @event.transition_for(@object) }
1035
+ end
1036
+
1037
+ it 'should_raise_exception_when_firing' do
1038
+ assert_raise(ArgumentError) { @event.fire(@object) }
1039
+ end
1040
+ end
1041
+
1042
+ context 'OnFailure' do
1043
+ before(:each) do
1044
+ StateMachines::Integrations.const_set('Custom', Module.new do
1045
+ include StateMachines::Integrations::Base
1046
+
1047
+ def invalidate(object, attribute, message, values = [])
1048
+ (object.errors ||= []) << generate_message(message, values)
1049
+ end
1050
+
1051
+ def reset(object)
1052
+ object.errors = []
1053
+ end
1054
+ end)
1055
+
1056
+ @klass = Class.new do
1057
+ attr_accessor :errors
1058
+ end
1059
+
1060
+ @machine = StateMachines::Machine.new(@klass, :integration => :custom)
1061
+ @machine.state :parked
1062
+ @machine.events << @event = StateMachines::Event.new(@machine, :ignite)
1063
+
1064
+ @object = @klass.new
1065
+ @object.state = 'parked'
1066
+ end
1067
+
1068
+ it 'should_invalidate_the_state' do
1069
+ @event.fire(@object)
1070
+ assert_equal ['cannot transition via "ignite"'], @object.errors
1071
+ end
1072
+
1073
+ it 'should_run_failure_callbacks' do
1074
+ callback_args = nil
1075
+ @machine.after_failure { |*args| callback_args = args }
1076
+
1077
+ @event.fire(@object)
1078
+
1079
+ object, transition = callback_args
1080
+ assert_equal @object, object
1081
+ assert_not_nil transition
1082
+ assert_equal @object, transition.object
1083
+ assert_equal @machine, transition.machine
1084
+ assert_equal :ignite, transition.event
1085
+ assert_equal :parked, transition.from_name
1086
+ assert_equal :parked, transition.to_name
1087
+ end
1088
+
1089
+ after(:each) do
1090
+ StateMachines::Integrations.send(:remove_const, 'Custom')
1091
+ StateMachines::Integrations.send(:reset)
1092
+ end
1093
+ end
1094
+
1095
+ context 'WithMarshalling' do
1096
+ before(:each) do
1097
+ @klass = Class.new do
1098
+ def save
1099
+ true
1100
+ end
1101
+ end
1102
+ self.class.const_set('Example', @klass)
1103
+
1104
+ @machine = StateMachines::Machine.new(@klass, :action => :save)
1105
+ @machine.state :parked, :idling
1106
+
1107
+ @machine.events << @event = StateMachines::Event.new(@machine, :ignite)
1108
+ @event.transition(:parked => :idling)
1109
+
1110
+ @object = @klass.new
1111
+ @object.state = 'parked'
1112
+ end
1113
+
1114
+ it 'should_marshal_during_before_callbacks' do
1115
+ @machine.before_transition { |object, transition| Marshal.dump(object) }
1116
+ assert_nothing_raised { @event.fire(@object) }
1117
+ end
1118
+
1119
+ it 'should_marshal_during_action' do
1120
+ @klass.class_eval do
1121
+ remove_method :save
1122
+
1123
+ def save
1124
+ Marshal.dump(self)
1125
+ end
1126
+ end
1127
+
1128
+ assert_nothing_raised { @event.fire(@object) }
1129
+ end
1130
+
1131
+ it 'should_marshal_during_after_callbacks' do
1132
+ @machine.after_transition { |object, transition| Marshal.dump(object) }
1133
+ assert_nothing_raised { @event.fire(@object) }
1134
+ end
1135
+
1136
+ after(:each) do
1137
+ self.class.send(:remove_const, 'Example')
1138
+ end
1139
+ end
1140
+ end