state_machines 0.0.1 → 0.0.2

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