pluginaweek-state_machine 0.7.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.rdoc +273 -0
- data/LICENSE +20 -0
- data/README.rdoc +466 -0
- data/Rakefile +98 -0
- data/examples/AutoShop_state.png +0 -0
- data/examples/Car_state.png +0 -0
- data/examples/TrafficLight_state.png +0 -0
- data/examples/Vehicle_state.png +0 -0
- data/examples/auto_shop.rb +11 -0
- data/examples/car.rb +19 -0
- data/examples/merb-rest/controller.rb +51 -0
- data/examples/merb-rest/model.rb +28 -0
- data/examples/merb-rest/view_edit.html.erb +24 -0
- data/examples/merb-rest/view_index.html.erb +23 -0
- data/examples/merb-rest/view_new.html.erb +13 -0
- data/examples/merb-rest/view_show.html.erb +17 -0
- data/examples/rails-rest/controller.rb +43 -0
- data/examples/rails-rest/migration.rb +11 -0
- data/examples/rails-rest/model.rb +23 -0
- data/examples/rails-rest/view_edit.html.erb +25 -0
- data/examples/rails-rest/view_index.html.erb +23 -0
- data/examples/rails-rest/view_new.html.erb +14 -0
- data/examples/rails-rest/view_show.html.erb +17 -0
- data/examples/traffic_light.rb +7 -0
- data/examples/vehicle.rb +31 -0
- data/init.rb +1 -0
- data/lib/state_machine.rb +429 -0
- data/lib/state_machine/assertions.rb +36 -0
- data/lib/state_machine/callback.rb +189 -0
- data/lib/state_machine/condition_proxy.rb +94 -0
- data/lib/state_machine/eval_helpers.rb +67 -0
- data/lib/state_machine/event.rb +251 -0
- data/lib/state_machine/event_collection.rb +113 -0
- data/lib/state_machine/extensions.rb +158 -0
- data/lib/state_machine/guard.rb +219 -0
- data/lib/state_machine/integrations.rb +68 -0
- data/lib/state_machine/integrations/active_record.rb +444 -0
- data/lib/state_machine/integrations/active_record/locale.rb +10 -0
- data/lib/state_machine/integrations/active_record/observer.rb +41 -0
- data/lib/state_machine/integrations/data_mapper.rb +325 -0
- data/lib/state_machine/integrations/data_mapper/observer.rb +139 -0
- data/lib/state_machine/integrations/sequel.rb +292 -0
- data/lib/state_machine/machine.rb +1431 -0
- data/lib/state_machine/machine_collection.rb +146 -0
- data/lib/state_machine/matcher.rb +123 -0
- data/lib/state_machine/matcher_helpers.rb +54 -0
- data/lib/state_machine/node_collection.rb +152 -0
- data/lib/state_machine/state.rb +249 -0
- data/lib/state_machine/state_collection.rb +112 -0
- data/lib/state_machine/transition.rb +367 -0
- data/tasks/state_machine.rake +1 -0
- data/tasks/state_machine.rb +30 -0
- data/test/classes/switch.rb +11 -0
- data/test/functional/state_machine_test.rb +941 -0
- data/test/test_helper.rb +4 -0
- data/test/unit/assertions_test.rb +40 -0
- data/test/unit/callback_test.rb +455 -0
- data/test/unit/condition_proxy_test.rb +328 -0
- data/test/unit/eval_helpers_test.rb +129 -0
- data/test/unit/event_collection_test.rb +293 -0
- data/test/unit/event_test.rb +605 -0
- data/test/unit/guard_test.rb +862 -0
- data/test/unit/integrations/active_record_test.rb +1001 -0
- data/test/unit/integrations/data_mapper_test.rb +694 -0
- data/test/unit/integrations/sequel_test.rb +486 -0
- data/test/unit/integrations_test.rb +42 -0
- data/test/unit/invalid_event_test.rb +7 -0
- data/test/unit/invalid_transition_test.rb +7 -0
- data/test/unit/machine_collection_test.rb +710 -0
- data/test/unit/machine_test.rb +1910 -0
- data/test/unit/matcher_helpers_test.rb +37 -0
- data/test/unit/matcher_test.rb +155 -0
- data/test/unit/node_collection_test.rb +207 -0
- data/test/unit/state_collection_test.rb +280 -0
- data/test/unit/state_machine_test.rb +31 -0
- data/test/unit/state_test.rb +795 -0
- data/test/unit/transition_test.rb +1113 -0
- metadata +161 -0
@@ -0,0 +1,1113 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
|
2
|
+
|
3
|
+
class TransitionTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@klass = Class.new
|
6
|
+
@machine = StateMachine::Machine.new(@klass)
|
7
|
+
@machine.state :parked, :idling
|
8
|
+
@machine.event :ignite
|
9
|
+
|
10
|
+
@object = @klass.new
|
11
|
+
@object.state = 'parked'
|
12
|
+
|
13
|
+
@transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_should_have_an_object
|
17
|
+
assert_equal @object, @transition.object
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_should_have_a_machine
|
21
|
+
assert_equal @machine, @transition.machine
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_should_have_an_event
|
25
|
+
assert_equal :ignite, @transition.event
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_should_have_a_qualified_event
|
29
|
+
assert_equal :ignite, @transition.qualified_event
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_should_have_a_from_value
|
33
|
+
assert_equal 'parked', @transition.from
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_should_have_a_from_name
|
37
|
+
assert_equal :parked, @transition.from_name
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_should_have_a_qualified_from_name
|
41
|
+
assert_equal :parked, @transition.qualified_from_name
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_should_have_a_to_value
|
45
|
+
assert_equal 'idling', @transition.to
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_should_have_a_to_name
|
49
|
+
assert_equal :idling, @transition.to_name
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_should_have_a_qualified_to_name
|
53
|
+
assert_equal :idling, @transition.qualified_to_name
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_should_have_an_attribute
|
57
|
+
assert_equal :state, @transition.attribute
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_should_not_have_an_action
|
61
|
+
assert_nil @transition.action
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_should_generate_attributes
|
65
|
+
expected = {:object => @object, :attribute => :state, :event => :ignite, :from => 'parked', :to => 'idling'}
|
66
|
+
assert_equal expected, @transition.attributes
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_should_have_empty_args
|
70
|
+
assert_equal [], @transition.args
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_should_not_have_a_result
|
74
|
+
assert_nil @transition.result
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_should_use_pretty_inspect
|
78
|
+
assert_equal '#<StateMachine::Transition attribute=:state event=:ignite from="parked" from_name=:parked to="idling" to_name=:idling>', @transition.inspect
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class TransitionWithInvalidNodesTest < Test::Unit::TestCase
|
83
|
+
def setup
|
84
|
+
@klass = Class.new
|
85
|
+
@machine = StateMachine::Machine.new(@klass)
|
86
|
+
@machine.state :parked, :idling
|
87
|
+
@machine.event :ignite
|
88
|
+
|
89
|
+
@object = @klass.new
|
90
|
+
@object.state = 'parked'
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_should_raise_exception_without_event
|
94
|
+
assert_raise(IndexError) { StateMachine::Transition.new(@object, @machine, nil, :parked, :idling) }
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_should_raise_exception_with_invalid_event
|
98
|
+
assert_raise(IndexError) { StateMachine::Transition.new(@object, @machine, :invalid, :parked, :idling) }
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_should_raise_exception_with_invalid_from_state
|
102
|
+
assert_raise(IndexError) { StateMachine::Transition.new(@object, @machine, :ignite, :invalid, :idling) }
|
103
|
+
end
|
104
|
+
|
105
|
+
def test_should_raise_exception_with_invalid_to_state
|
106
|
+
assert_raise(IndexError) { StateMachine::Transition.new(@object, @machine, :ignite, :parked, :invalid) }
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
class TransitionWithDynamicToValueTest < Test::Unit::TestCase
|
111
|
+
def setup
|
112
|
+
@klass = Class.new
|
113
|
+
@machine = StateMachine::Machine.new(@klass)
|
114
|
+
@machine.state :parked
|
115
|
+
@machine.state :idling, :value => lambda {1}
|
116
|
+
@machine.event :ignite
|
117
|
+
|
118
|
+
@object = @klass.new
|
119
|
+
@object.state = 'parked'
|
120
|
+
@transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
|
121
|
+
end
|
122
|
+
|
123
|
+
def test_should_evaluate_to_value
|
124
|
+
assert_equal 1, @transition.to
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
class TransitionLoopbackTest < Test::Unit::TestCase
|
129
|
+
def setup
|
130
|
+
@klass = Class.new
|
131
|
+
@machine = StateMachine::Machine.new(@klass)
|
132
|
+
@machine.state :parked
|
133
|
+
@machine.event :park
|
134
|
+
|
135
|
+
@object = @klass.new
|
136
|
+
@object.state = 'parked'
|
137
|
+
@transition = StateMachine::Transition.new(@object, @machine, :park, :parked, :parked)
|
138
|
+
end
|
139
|
+
|
140
|
+
def test_should_be_loopback
|
141
|
+
assert @transition.loopback?
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
class TransitionWithDifferentStatesTest < Test::Unit::TestCase
|
146
|
+
def setup
|
147
|
+
@klass = Class.new
|
148
|
+
@machine = StateMachine::Machine.new(@klass)
|
149
|
+
@machine.state :parked, :idling
|
150
|
+
@machine.event :ignite
|
151
|
+
|
152
|
+
@object = @klass.new
|
153
|
+
@object.state = 'parked'
|
154
|
+
@transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
|
155
|
+
end
|
156
|
+
|
157
|
+
def test_should_not_be_loopback
|
158
|
+
assert !@transition.loopback?
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
class TransitionWithNamespaceTest < Test::Unit::TestCase
|
163
|
+
def setup
|
164
|
+
@klass = Class.new
|
165
|
+
@machine = StateMachine::Machine.new(@klass, :namespace => 'alarm')
|
166
|
+
@machine.state :off, :active
|
167
|
+
@machine.event :activate
|
168
|
+
|
169
|
+
@object = @klass.new
|
170
|
+
@object.state = 'off'
|
171
|
+
|
172
|
+
@transition = StateMachine::Transition.new(@object, @machine, :activate, :off, :active)
|
173
|
+
end
|
174
|
+
|
175
|
+
def test_should_have_an_event
|
176
|
+
assert_equal :activate, @transition.event
|
177
|
+
end
|
178
|
+
|
179
|
+
def test_should_have_a_qualified_event
|
180
|
+
assert_equal :activate_alarm, @transition.qualified_event
|
181
|
+
end
|
182
|
+
|
183
|
+
def test_should_have_a_from_name
|
184
|
+
assert_equal :off, @transition.from_name
|
185
|
+
end
|
186
|
+
|
187
|
+
def test_should_have_a_qualified_from_name
|
188
|
+
assert_equal :alarm_off, @transition.qualified_from_name
|
189
|
+
end
|
190
|
+
|
191
|
+
def test_should_have_a_to_name
|
192
|
+
assert_equal :active, @transition.to_name
|
193
|
+
end
|
194
|
+
|
195
|
+
def test_should_have_a_qualified_to_name
|
196
|
+
assert_equal :alarm_active, @transition.qualified_to_name
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
class TransitionWithCustomMachineNameTest < Test::Unit::TestCase
|
201
|
+
def setup
|
202
|
+
@klass = Class.new
|
203
|
+
@machine = StateMachine::Machine.new(@klass, :state_id, :as => 'state')
|
204
|
+
@machine.state :off, :value => 1
|
205
|
+
@machine.state :active, :value => 2
|
206
|
+
@machine.event :activate
|
207
|
+
|
208
|
+
@object = @klass.new
|
209
|
+
@object.state_id = 1
|
210
|
+
|
211
|
+
@transition = StateMachine::Transition.new(@object, @machine, :activate, :off, :active)
|
212
|
+
end
|
213
|
+
|
214
|
+
def test_should_persist
|
215
|
+
@transition.persist
|
216
|
+
assert_equal 2, @object.state_id
|
217
|
+
end
|
218
|
+
|
219
|
+
def test_should_rollback
|
220
|
+
@object.state_id = 2
|
221
|
+
@transition.rollback
|
222
|
+
|
223
|
+
assert_equal 1, @object.state_id
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
class TransitionWithActionTest < Test::Unit::TestCase
|
228
|
+
def setup
|
229
|
+
@klass = Class.new do
|
230
|
+
def save
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
@machine = StateMachine::Machine.new(@klass, :action => :save)
|
235
|
+
@machine.state :parked, :idling
|
236
|
+
@machine.event :ignite
|
237
|
+
|
238
|
+
@object = @klass.new
|
239
|
+
@object.state = 'parked'
|
240
|
+
|
241
|
+
@transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
|
242
|
+
end
|
243
|
+
|
244
|
+
def test_should_have_an_action
|
245
|
+
assert_equal :save, @transition.action
|
246
|
+
end
|
247
|
+
|
248
|
+
def test_should_not_have_a_result
|
249
|
+
assert_nil @transition.result
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
class TransitionAfterBeingPersistedTest < Test::Unit::TestCase
|
254
|
+
def setup
|
255
|
+
@klass = Class.new
|
256
|
+
@machine = StateMachine::Machine.new(@klass, :action => :save)
|
257
|
+
@machine.state :parked, :idling
|
258
|
+
@machine.event :ignite
|
259
|
+
|
260
|
+
@object = @klass.new
|
261
|
+
@object.state = 'parked'
|
262
|
+
|
263
|
+
@transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
|
264
|
+
@transition.persist
|
265
|
+
end
|
266
|
+
|
267
|
+
def test_should_update_state_value
|
268
|
+
assert_equal 'idling', @object.state
|
269
|
+
end
|
270
|
+
|
271
|
+
def test_should_not_change_from_state
|
272
|
+
assert_equal 'parked', @transition.from
|
273
|
+
end
|
274
|
+
|
275
|
+
def test_should_not_change_to_state
|
276
|
+
assert_equal 'idling', @transition.to
|
277
|
+
end
|
278
|
+
|
279
|
+
def test_should_revert_to_from_state_on_rollback
|
280
|
+
@transition.rollback
|
281
|
+
assert_equal 'parked', @object.state
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
class TransitionAfterBeingRolledBackTest < Test::Unit::TestCase
|
286
|
+
def setup
|
287
|
+
@klass = Class.new
|
288
|
+
@machine = StateMachine::Machine.new(@klass, :action => :save)
|
289
|
+
@machine.state :parked, :idling
|
290
|
+
@machine.event :ignite
|
291
|
+
|
292
|
+
@object = @klass.new
|
293
|
+
@object.state = 'parked'
|
294
|
+
|
295
|
+
@transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
|
296
|
+
@object.state = 'idling'
|
297
|
+
|
298
|
+
@transition.rollback
|
299
|
+
end
|
300
|
+
|
301
|
+
def test_should_update_state_value_to_from_state
|
302
|
+
assert_equal 'parked', @object.state
|
303
|
+
end
|
304
|
+
|
305
|
+
def test_should_not_change_from_state
|
306
|
+
assert_equal 'parked', @transition.from
|
307
|
+
end
|
308
|
+
|
309
|
+
def test_should_not_change_to_state
|
310
|
+
assert_equal 'idling', @transition.to
|
311
|
+
end
|
312
|
+
|
313
|
+
def test_should_still_be_able_to_persist
|
314
|
+
@transition.persist
|
315
|
+
assert_equal 'idling', @object.state
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
class TransitionWithCallbacksTest < Test::Unit::TestCase
|
320
|
+
def setup
|
321
|
+
@klass = Class.new do
|
322
|
+
attr_reader :saved, :save_state
|
323
|
+
|
324
|
+
def save
|
325
|
+
@save_state = state
|
326
|
+
@saved = true
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
@machine = StateMachine::Machine.new(@klass)
|
331
|
+
@machine.state :parked, :idling
|
332
|
+
@machine.event :ignite
|
333
|
+
|
334
|
+
@object = @klass.new
|
335
|
+
@object.state = 'parked'
|
336
|
+
@transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
|
337
|
+
end
|
338
|
+
|
339
|
+
def test_should_run_before_callbacks_on_before
|
340
|
+
@machine.before_transition(lambda {|object| @run = true})
|
341
|
+
result = @transition.before
|
342
|
+
|
343
|
+
assert_equal true, result
|
344
|
+
assert_equal true, @run
|
345
|
+
end
|
346
|
+
|
347
|
+
def test_should_run_before_callbacks_in_the_order_they_were_defined
|
348
|
+
@callbacks = []
|
349
|
+
@machine.before_transition(lambda {@callbacks << 1})
|
350
|
+
@machine.before_transition(lambda {@callbacks << 2})
|
351
|
+
@transition.before
|
352
|
+
|
353
|
+
assert_equal [1, 2], @callbacks
|
354
|
+
end
|
355
|
+
|
356
|
+
def test_should_only_run_before_callbacks_that_match_transition_context
|
357
|
+
@count = 0
|
358
|
+
callback = lambda {@count += 1}
|
359
|
+
|
360
|
+
@machine.before_transition :from => :parked, :to => :idling, :on => :park, :do => callback
|
361
|
+
@machine.before_transition :from => :parked, :to => :parked, :on => :park, :do => callback
|
362
|
+
@machine.before_transition :from => :parked, :to => :idling, :on => :ignite, :do => callback
|
363
|
+
@machine.before_transition :from => :idling, :to => :idling, :on => :park, :do => callback
|
364
|
+
@transition.before
|
365
|
+
|
366
|
+
assert_equal 1, @count
|
367
|
+
end
|
368
|
+
|
369
|
+
def test_should_pass_transition_to_before_callbacks
|
370
|
+
@machine.before_transition(lambda {|*args| @args = args})
|
371
|
+
@transition.before
|
372
|
+
|
373
|
+
assert_equal [@object, @transition], @args
|
374
|
+
end
|
375
|
+
|
376
|
+
def test_should_catch_halted_before_callbacks
|
377
|
+
@machine.before_transition(lambda {throw :halt})
|
378
|
+
|
379
|
+
result = nil
|
380
|
+
assert_nothing_thrown { result = @transition.before }
|
381
|
+
assert_equal false, result
|
382
|
+
end
|
383
|
+
|
384
|
+
def test_should_run_before_callbacks_on_perform_before_changing_the_state
|
385
|
+
@machine.before_transition(lambda {|object| @state = object.state})
|
386
|
+
@transition.perform
|
387
|
+
|
388
|
+
assert_equal 'parked', @state
|
389
|
+
end
|
390
|
+
|
391
|
+
def test_should_run_after_callbacks_on_after
|
392
|
+
@machine.after_transition(lambda {|object| @run = true})
|
393
|
+
result = @transition.after(true)
|
394
|
+
|
395
|
+
assert_equal true, result
|
396
|
+
assert_equal true, @run
|
397
|
+
end
|
398
|
+
|
399
|
+
def test_should_set_result_on_after
|
400
|
+
@transition.after
|
401
|
+
assert_nil @transition.result
|
402
|
+
|
403
|
+
@transition.after(1)
|
404
|
+
assert_equal 1, @transition.result
|
405
|
+
end
|
406
|
+
|
407
|
+
def test_should_run_after_callbacks_in_the_order_they_were_defined
|
408
|
+
@callbacks = []
|
409
|
+
@machine.after_transition(lambda {@callbacks << 1})
|
410
|
+
@machine.after_transition(lambda {@callbacks << 2})
|
411
|
+
@transition.after(true)
|
412
|
+
|
413
|
+
assert_equal [1, 2], @callbacks
|
414
|
+
end
|
415
|
+
|
416
|
+
def test_should_only_run_after_callbacks_that_match_transition_context
|
417
|
+
@count = 0
|
418
|
+
callback = lambda {@count += 1}
|
419
|
+
|
420
|
+
@machine.after_transition :from => :parked, :to => :idling, :on => :park, :do => callback
|
421
|
+
@machine.after_transition :from => :parked, :to => :parked, :on => :park, :do => callback
|
422
|
+
@machine.after_transition :from => :parked, :to => :idling, :on => :ignite, :do => callback
|
423
|
+
@machine.after_transition :from => :idling, :to => :idling, :on => :park, :do => callback
|
424
|
+
@transition.after(true)
|
425
|
+
|
426
|
+
assert_equal 1, @count
|
427
|
+
end
|
428
|
+
|
429
|
+
def test_should_pass_transition_to_after_callbacks
|
430
|
+
@machine.after_transition(lambda {|*args| @args = args})
|
431
|
+
|
432
|
+
@transition.after(true)
|
433
|
+
assert_equal [@object, @transition], @args
|
434
|
+
assert_equal true, @transition.result
|
435
|
+
|
436
|
+
@transition.after(false)
|
437
|
+
assert_equal [@object, @transition], @args
|
438
|
+
assert_equal false, @transition.result
|
439
|
+
end
|
440
|
+
|
441
|
+
def test_should_catch_halted_after_callbacks
|
442
|
+
@machine.after_transition(lambda {throw :halt})
|
443
|
+
|
444
|
+
result = nil
|
445
|
+
assert_nothing_thrown { result = @transition.after(true) }
|
446
|
+
assert_equal true, result
|
447
|
+
end
|
448
|
+
|
449
|
+
def test_should_run_after_callbacks_on_perform_after_running_the_action
|
450
|
+
@machine.after_transition(lambda {|object| @state = object.state})
|
451
|
+
@transition.perform(true)
|
452
|
+
|
453
|
+
assert_equal 'idling', @state
|
454
|
+
end
|
455
|
+
end
|
456
|
+
|
457
|
+
class TransitionAfterBeingPerformedTest < Test::Unit::TestCase
|
458
|
+
def setup
|
459
|
+
@klass = Class.new do
|
460
|
+
attr_reader :saved, :save_state
|
461
|
+
|
462
|
+
def save
|
463
|
+
@save_state = state
|
464
|
+
@saved = true
|
465
|
+
end
|
466
|
+
end
|
467
|
+
|
468
|
+
@machine = StateMachine::Machine.new(@klass, :action => :save)
|
469
|
+
@machine.state :parked, :idling
|
470
|
+
@machine.event :ignite
|
471
|
+
|
472
|
+
@object = @klass.new
|
473
|
+
@object.state = 'parked'
|
474
|
+
@transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
|
475
|
+
@result = @transition.perform
|
476
|
+
end
|
477
|
+
|
478
|
+
def test_should_have_empty_args
|
479
|
+
assert_equal [], @transition.args
|
480
|
+
end
|
481
|
+
|
482
|
+
def test_should_have_a_result
|
483
|
+
assert_equal true, @transition.result
|
484
|
+
end
|
485
|
+
|
486
|
+
def test_should_be_successful
|
487
|
+
assert_equal true, @result
|
488
|
+
end
|
489
|
+
|
490
|
+
def test_should_change_the_current_state
|
491
|
+
assert_equal 'idling', @object.state
|
492
|
+
end
|
493
|
+
|
494
|
+
def test_should_run_the_action
|
495
|
+
assert @object.saved
|
496
|
+
end
|
497
|
+
|
498
|
+
def test_should_run_the_action_after_saving_the_state
|
499
|
+
assert_equal 'idling', @object.save_state
|
500
|
+
end
|
501
|
+
end
|
502
|
+
|
503
|
+
class TransitionWithPerformArgumentsTest < Test::Unit::TestCase
|
504
|
+
def setup
|
505
|
+
@klass = Class.new do
|
506
|
+
attr_reader :saved
|
507
|
+
|
508
|
+
def save
|
509
|
+
@saved = true
|
510
|
+
end
|
511
|
+
end
|
512
|
+
|
513
|
+
@machine = StateMachine::Machine.new(@klass, :action => :save)
|
514
|
+
@machine.state :parked, :idling
|
515
|
+
@machine.event :ignite
|
516
|
+
|
517
|
+
@object = @klass.new
|
518
|
+
@object.state = 'parked'
|
519
|
+
@transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
|
520
|
+
end
|
521
|
+
|
522
|
+
def test_should_have_arguments
|
523
|
+
@transition.perform(1, 2)
|
524
|
+
|
525
|
+
assert_equal [1, 2], @transition.args
|
526
|
+
assert @object.saved
|
527
|
+
end
|
528
|
+
|
529
|
+
def test_should_not_include_run_action_in_arguments
|
530
|
+
@transition.perform(1, 2, false)
|
531
|
+
|
532
|
+
assert_equal [1, 2], @transition.args
|
533
|
+
assert !@object.saved
|
534
|
+
end
|
535
|
+
end
|
536
|
+
|
537
|
+
class TransitionWithoutRunningActionTest < Test::Unit::TestCase
|
538
|
+
def setup
|
539
|
+
@klass = Class.new do
|
540
|
+
attr_reader :saved
|
541
|
+
|
542
|
+
def save
|
543
|
+
@saved = true
|
544
|
+
end
|
545
|
+
end
|
546
|
+
|
547
|
+
@machine = StateMachine::Machine.new(@klass, :action => :save)
|
548
|
+
@machine.state :parked, :idling
|
549
|
+
@machine.event :ignite
|
550
|
+
|
551
|
+
@object = @klass.new
|
552
|
+
@object.state = 'parked'
|
553
|
+
@transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
|
554
|
+
@result = @transition.perform(false)
|
555
|
+
end
|
556
|
+
|
557
|
+
def test_should_have_empty_args
|
558
|
+
assert_equal [], @transition.args
|
559
|
+
end
|
560
|
+
|
561
|
+
def test_should_not_have_a_result
|
562
|
+
assert_nil @transition.result
|
563
|
+
end
|
564
|
+
|
565
|
+
def test_should_be_successful
|
566
|
+
assert_equal true, @result
|
567
|
+
end
|
568
|
+
|
569
|
+
def test_should_change_the_current_state
|
570
|
+
assert_equal 'idling', @object.state
|
571
|
+
end
|
572
|
+
|
573
|
+
def test_should_not_run_the_action
|
574
|
+
assert !@object.saved
|
575
|
+
end
|
576
|
+
end
|
577
|
+
|
578
|
+
class TransitionWithTransactionsTest < Test::Unit::TestCase
|
579
|
+
def setup
|
580
|
+
@klass = Class.new do
|
581
|
+
class << self
|
582
|
+
attr_accessor :running_transaction
|
583
|
+
end
|
584
|
+
|
585
|
+
attr_accessor :result
|
586
|
+
|
587
|
+
def save
|
588
|
+
@result = self.class.running_transaction
|
589
|
+
true
|
590
|
+
end
|
591
|
+
end
|
592
|
+
|
593
|
+
@machine = StateMachine::Machine.new(@klass, :action => :save)
|
594
|
+
@machine.state :parked, :idling
|
595
|
+
@machine.event :ignite
|
596
|
+
|
597
|
+
@object = @klass.new
|
598
|
+
@object.state = 'parked'
|
599
|
+
@transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
|
600
|
+
|
601
|
+
class << @machine
|
602
|
+
def within_transaction(object)
|
603
|
+
owner_class.running_transaction = object
|
604
|
+
yield
|
605
|
+
owner_class.running_transaction = false
|
606
|
+
end
|
607
|
+
end
|
608
|
+
end
|
609
|
+
|
610
|
+
def test_should_run_blocks_within_transaction_for_object
|
611
|
+
@transition.within_transaction do
|
612
|
+
@result = @klass.running_transaction
|
613
|
+
end
|
614
|
+
|
615
|
+
assert_equal @object, @result
|
616
|
+
end
|
617
|
+
|
618
|
+
def test_should_run_before_callbacks_within_transaction
|
619
|
+
@machine.before_transition(lambda {|object| @result = @klass.running_transaction})
|
620
|
+
@transition.perform
|
621
|
+
|
622
|
+
assert_equal @object, @result
|
623
|
+
end
|
624
|
+
|
625
|
+
def test_should_run_action_within_transaction
|
626
|
+
@transition.perform
|
627
|
+
|
628
|
+
assert_equal @object, @object.result
|
629
|
+
end
|
630
|
+
|
631
|
+
def test_should_run_after_callbacks_within_transaction
|
632
|
+
@machine.after_transition(lambda {|object| @result = @klass.running_transaction})
|
633
|
+
@transition.perform
|
634
|
+
|
635
|
+
assert_equal @object, @result
|
636
|
+
end
|
637
|
+
end
|
638
|
+
|
639
|
+
class TransitionHaltedDuringBeforeCallbacksTest < Test::Unit::TestCase
|
640
|
+
def setup
|
641
|
+
@klass = Class.new do
|
642
|
+
class << self; attr_accessor :cancelled_transaction; end
|
643
|
+
attr_reader :saved
|
644
|
+
|
645
|
+
def save
|
646
|
+
@saved = true
|
647
|
+
end
|
648
|
+
end
|
649
|
+
@before_count = 0
|
650
|
+
@after_count = 0
|
651
|
+
|
652
|
+
@machine = StateMachine::Machine.new(@klass, :action => :save)
|
653
|
+
@machine.state :parked, :idling
|
654
|
+
@machine.event :ignite
|
655
|
+
class << @machine
|
656
|
+
def within_transaction(object)
|
657
|
+
owner_class.cancelled_transaction = yield == false
|
658
|
+
end
|
659
|
+
end
|
660
|
+
|
661
|
+
@machine.before_transition lambda {@before_count += 1; throw :halt}
|
662
|
+
@machine.before_transition lambda {@before_count += 1}
|
663
|
+
@machine.after_transition lambda {@after_count += 1}
|
664
|
+
|
665
|
+
@object = @klass.new
|
666
|
+
@object.state = 'parked'
|
667
|
+
@transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
|
668
|
+
@result = @transition.perform
|
669
|
+
end
|
670
|
+
|
671
|
+
def test_should_not_be_successful
|
672
|
+
assert !@result
|
673
|
+
end
|
674
|
+
|
675
|
+
def test_should_not_change_current_state
|
676
|
+
assert_equal 'parked', @object.state
|
677
|
+
end
|
678
|
+
|
679
|
+
def test_should_not_run_action
|
680
|
+
assert !@object.saved
|
681
|
+
end
|
682
|
+
|
683
|
+
def test_should_not_run_further_before_callbacks
|
684
|
+
assert_equal 1, @before_count
|
685
|
+
end
|
686
|
+
|
687
|
+
def test_should_not_run_after_callbacks
|
688
|
+
assert_equal 0, @after_count
|
689
|
+
end
|
690
|
+
|
691
|
+
def test_should_cancel_the_transaction
|
692
|
+
assert @klass.cancelled_transaction
|
693
|
+
end
|
694
|
+
end
|
695
|
+
|
696
|
+
class TransitionHaltedAfterCallbackTest < Test::Unit::TestCase
|
697
|
+
def setup
|
698
|
+
@klass = Class.new do
|
699
|
+
class << self; attr_accessor :cancelled_transaction; end
|
700
|
+
attr_reader :saved
|
701
|
+
|
702
|
+
def save
|
703
|
+
@saved = true
|
704
|
+
end
|
705
|
+
end
|
706
|
+
@before_count = 0
|
707
|
+
@after_count = 0
|
708
|
+
|
709
|
+
@machine = StateMachine::Machine.new(@klass, :action => :save)
|
710
|
+
@machine.state :parked, :idling
|
711
|
+
@machine.event :ignite
|
712
|
+
class << @machine
|
713
|
+
def within_transaction(object)
|
714
|
+
owner_class.cancelled_transaction = yield == false
|
715
|
+
end
|
716
|
+
end
|
717
|
+
|
718
|
+
@machine.before_transition lambda {@before_count += 1}
|
719
|
+
@machine.after_transition lambda {@after_count += 1; throw :halt}
|
720
|
+
@machine.after_transition lambda {@after_count += 1}
|
721
|
+
|
722
|
+
@object = @klass.new
|
723
|
+
@object.state = 'parked'
|
724
|
+
@transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
|
725
|
+
@result = @transition.perform
|
726
|
+
end
|
727
|
+
|
728
|
+
def test_should_be_successful
|
729
|
+
assert @result
|
730
|
+
end
|
731
|
+
|
732
|
+
def test_should_change_current_state
|
733
|
+
assert_equal 'idling', @object.state
|
734
|
+
end
|
735
|
+
|
736
|
+
def test_should_run_before_callbacks
|
737
|
+
assert_equal 1, @before_count
|
738
|
+
end
|
739
|
+
|
740
|
+
def test_should_not_run_further_after_callbacks
|
741
|
+
assert_equal 1, @after_count
|
742
|
+
end
|
743
|
+
|
744
|
+
def test_should_not_cancel_the_transaction
|
745
|
+
assert !@klass.cancelled_transaction
|
746
|
+
end
|
747
|
+
end
|
748
|
+
|
749
|
+
class TransitionWithActionFailedTest < Test::Unit::TestCase
|
750
|
+
def setup
|
751
|
+
@klass = Class.new do
|
752
|
+
class << self; attr_accessor :cancelled_transaction; end
|
753
|
+
attr_reader :saved
|
754
|
+
|
755
|
+
def save
|
756
|
+
false
|
757
|
+
end
|
758
|
+
end
|
759
|
+
@before_count = 0
|
760
|
+
@after_count = 0
|
761
|
+
|
762
|
+
@machine = StateMachine::Machine.new(@klass, :action => :save)
|
763
|
+
@machine.state :parked, :idling
|
764
|
+
@machine.event :ignite
|
765
|
+
class << @machine
|
766
|
+
def within_transaction(object)
|
767
|
+
owner_class.cancelled_transaction = yield == false
|
768
|
+
end
|
769
|
+
end
|
770
|
+
|
771
|
+
@machine.before_transition lambda {@before_count += 1}
|
772
|
+
@machine.after_transition lambda {@after_count += 1}
|
773
|
+
|
774
|
+
@object = @klass.new
|
775
|
+
@transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
|
776
|
+
@result = @transition.perform
|
777
|
+
end
|
778
|
+
|
779
|
+
def test_should_not_be_successful
|
780
|
+
assert !@result
|
781
|
+
end
|
782
|
+
|
783
|
+
def test_should_not_change_current_state
|
784
|
+
assert_nil @object.state
|
785
|
+
end
|
786
|
+
|
787
|
+
def test_should_run_before_callbacks
|
788
|
+
assert_equal 1, @before_count
|
789
|
+
end
|
790
|
+
|
791
|
+
def test_should_run_after_callbacks
|
792
|
+
assert_equal 1, @after_count
|
793
|
+
end
|
794
|
+
|
795
|
+
def test_should_cancel_the_transaction
|
796
|
+
assert @klass.cancelled_transaction
|
797
|
+
end
|
798
|
+
end
|
799
|
+
|
800
|
+
class TransitionWithActionErrorTest < Test::Unit::TestCase
|
801
|
+
def setup
|
802
|
+
@klass = Class.new do
|
803
|
+
def save
|
804
|
+
raise ArgumentError
|
805
|
+
end
|
806
|
+
end
|
807
|
+
|
808
|
+
@machine = StateMachine::Machine.new(@klass, :action => :save)
|
809
|
+
@machine.state :parked, :idling
|
810
|
+
@machine.event :ignite
|
811
|
+
|
812
|
+
@object = @klass.new
|
813
|
+
@object.state = 'parked'
|
814
|
+
@transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
|
815
|
+
|
816
|
+
@raised = true
|
817
|
+
begin
|
818
|
+
@transition.perform
|
819
|
+
@raised = false
|
820
|
+
rescue ArgumentError
|
821
|
+
end
|
822
|
+
end
|
823
|
+
|
824
|
+
def test_should_not_catch_exception
|
825
|
+
assert @raised
|
826
|
+
end
|
827
|
+
|
828
|
+
def test_should_not_change_current_state
|
829
|
+
assert_equal 'parked', @object.state
|
830
|
+
end
|
831
|
+
end
|
832
|
+
|
833
|
+
class TransitionsInParallelTest < Test::Unit::TestCase
|
834
|
+
def setup
|
835
|
+
@klass = Class.new do
|
836
|
+
attr_reader :persisted
|
837
|
+
|
838
|
+
def initialize
|
839
|
+
@persisted = []
|
840
|
+
@state = 'parked'
|
841
|
+
@status = 'first_gear'
|
842
|
+
super
|
843
|
+
end
|
844
|
+
|
845
|
+
def state=(value)
|
846
|
+
@persisted << value
|
847
|
+
@state = value
|
848
|
+
end
|
849
|
+
|
850
|
+
def status=(value)
|
851
|
+
@persisted << value
|
852
|
+
@status = value
|
853
|
+
end
|
854
|
+
end
|
855
|
+
|
856
|
+
@state = StateMachine::Machine.new(@klass, :state)
|
857
|
+
@state.event :ignite
|
858
|
+
@state.state :parked, :idling
|
859
|
+
|
860
|
+
@status = StateMachine::Machine.new(@klass, :status)
|
861
|
+
@status.event :shift_up
|
862
|
+
@status.state :first_gear, :second_gear
|
863
|
+
|
864
|
+
@object = @klass.new
|
865
|
+
@state_transition = StateMachine::Transition.new(@object, @state, :ignite, :parked, :idling)
|
866
|
+
@status_transition = StateMachine::Transition.new(@object, @status, :shift_up, :first_gear, :second_gear)
|
867
|
+
|
868
|
+
@result = StateMachine::Transition.perform([@state_transition, @status_transition])
|
869
|
+
end
|
870
|
+
|
871
|
+
def test_should_raise_exception_if_attempted_on_the_same_state_machine
|
872
|
+
exception = assert_raise(ArgumentError) { StateMachine::Transition.perform([@state_transition, @state_transition]) }
|
873
|
+
assert_equal 'Cannot perform multiple transitions in parallel for the same state machine attribute', exception.message
|
874
|
+
end
|
875
|
+
|
876
|
+
def test_should_perform
|
877
|
+
assert_equal true, @result
|
878
|
+
end
|
879
|
+
|
880
|
+
def test_should_persist_first_state
|
881
|
+
assert_equal 'idling', @object.state
|
882
|
+
end
|
883
|
+
|
884
|
+
def test_should_persist_second_state
|
885
|
+
assert_equal 'second_gear', @object.status
|
886
|
+
end
|
887
|
+
|
888
|
+
def test_should_persist_in_order
|
889
|
+
assert_equal ['idling', 'second_gear'], @object.persisted
|
890
|
+
end
|
891
|
+
|
892
|
+
def test_should_have_args_in_transitions
|
893
|
+
assert_equal [], @state_transition.args
|
894
|
+
assert_equal [], @status_transition.args
|
895
|
+
end
|
896
|
+
end
|
897
|
+
|
898
|
+
class TransitionsInParallelWithCallbacksTest < Test::Unit::TestCase
|
899
|
+
def setup
|
900
|
+
@klass = Class.new
|
901
|
+
|
902
|
+
@before_callbacks = []
|
903
|
+
@after_callbacks = []
|
904
|
+
|
905
|
+
@state = StateMachine::Machine.new(@klass, :state, :initial => :parked)
|
906
|
+
@state.event :ignite
|
907
|
+
@state.state :parked, :idling
|
908
|
+
@state.before_transition lambda {@before_callbacks << :state}
|
909
|
+
@state.after_transition lambda {@after_callbacks << :state}
|
910
|
+
|
911
|
+
@status = StateMachine::Machine.new(@klass, :status, :initial => :first_gear)
|
912
|
+
@status.event :shift_up
|
913
|
+
@status.state :first_gear, :second_gear
|
914
|
+
@status.before_transition lambda {@before_callbacks << :status}
|
915
|
+
@status.after_transition lambda {@after_callbacks << :status}
|
916
|
+
|
917
|
+
@object = @klass.new
|
918
|
+
@state_transition = StateMachine::Transition.new(@object, @state, :ignite, :parked, :idling)
|
919
|
+
@status_transition = StateMachine::Transition.new(@object, @status, :shift_up, :first_gear, :second_gear)
|
920
|
+
end
|
921
|
+
|
922
|
+
def test_should_run_before_callbacks_in_order
|
923
|
+
perform
|
924
|
+
assert_equal [:state, :status], @before_callbacks
|
925
|
+
end
|
926
|
+
|
927
|
+
def test_should_run_after_callbacks_in_order
|
928
|
+
perform
|
929
|
+
assert_equal [:state, :status], @after_callbacks
|
930
|
+
end
|
931
|
+
|
932
|
+
def test_should_not_run_after_callbacks_if_disabled
|
933
|
+
perform(:after => false)
|
934
|
+
assert_equal [], @after_callbacks
|
935
|
+
end
|
936
|
+
|
937
|
+
def test_should_halt_if_before_callback_halted_for_first_transition
|
938
|
+
@state.before_transition lambda {throw :halt}
|
939
|
+
|
940
|
+
assert_equal false, perform
|
941
|
+
assert_equal [:state], @before_callbacks
|
942
|
+
assert_equal 'parked', @object.state
|
943
|
+
assert_equal 'first_gear', @object.status
|
944
|
+
assert_equal [], @after_callbacks
|
945
|
+
end
|
946
|
+
|
947
|
+
def test_should_halt_if_before_callback_halted_for_second_transition
|
948
|
+
@status.before_transition lambda {throw :halt}
|
949
|
+
|
950
|
+
assert_equal false, perform
|
951
|
+
assert_equal [:state, :status], @before_callbacks
|
952
|
+
assert_equal 'parked', @object.state
|
953
|
+
assert_equal 'first_gear', @object.status
|
954
|
+
assert_equal [], @after_callbacks
|
955
|
+
end
|
956
|
+
|
957
|
+
def test_should_perform_if_after_callback_halted_for_first_transition
|
958
|
+
@state.after_transition lambda {throw :halt}
|
959
|
+
@state.after_transition lambda {@after_callbacks << :invalid}
|
960
|
+
|
961
|
+
assert_equal true, perform
|
962
|
+
assert_equal [:state, :status], @before_callbacks
|
963
|
+
assert_equal 'idling', @object.state
|
964
|
+
assert_equal 'second_gear', @object.status
|
965
|
+
assert_equal [:state, :status], @after_callbacks
|
966
|
+
end
|
967
|
+
|
968
|
+
def test_should_perform_if_after_callback_halted_for_second_transition
|
969
|
+
@status.after_transition lambda {throw :halt}
|
970
|
+
@status.after_transition lambda {@after_callbacks << :invalid}
|
971
|
+
|
972
|
+
assert_equal true, perform
|
973
|
+
assert_equal [:state, :status], @before_callbacks
|
974
|
+
assert_equal 'idling', @object.state
|
975
|
+
assert_equal 'second_gear', @object.status
|
976
|
+
assert_equal [:state, :status], @after_callbacks
|
977
|
+
end
|
978
|
+
|
979
|
+
private
|
980
|
+
def perform(options = {})
|
981
|
+
StateMachine::Transition.perform([@state_transition, @status_transition], options)
|
982
|
+
end
|
983
|
+
end
|
984
|
+
|
985
|
+
class TransitionsInParallelWithActionsTest < Test::Unit::TestCase
|
986
|
+
def setup
|
987
|
+
@klass = Class.new do
|
988
|
+
attr_reader :actions
|
989
|
+
|
990
|
+
def save_state
|
991
|
+
(@actions ||= []) << :save_state
|
992
|
+
:save_state
|
993
|
+
end
|
994
|
+
|
995
|
+
def save_status
|
996
|
+
(@actions ||= []) << :save_status
|
997
|
+
:save_status
|
998
|
+
end
|
999
|
+
end
|
1000
|
+
|
1001
|
+
@state = StateMachine::Machine.new(@klass, :state, :initial => :parked, :action => :save_state)
|
1002
|
+
@state.event :ignite
|
1003
|
+
@state.state :parked, :idling
|
1004
|
+
|
1005
|
+
@status = StateMachine::Machine.new(@klass, :status, :initial => :first_gear, :action => :save_status)
|
1006
|
+
@status.event :shift_up
|
1007
|
+
@status.state :first_gear, :second_gear
|
1008
|
+
|
1009
|
+
@object = @klass.new
|
1010
|
+
@state_transition = StateMachine::Transition.new(@object, @state, :ignite, :parked, :idling)
|
1011
|
+
@status_transition = StateMachine::Transition.new(@object, @status, :shift_up, :first_gear, :second_gear)
|
1012
|
+
end
|
1013
|
+
|
1014
|
+
def test_should_run_actions_in_order
|
1015
|
+
perform
|
1016
|
+
assert_equal [:save_state, :save_status], @object.actions
|
1017
|
+
end
|
1018
|
+
|
1019
|
+
def test_should_store_action_specific_results
|
1020
|
+
perform
|
1021
|
+
assert_equal :save_state, @state_transition.result
|
1022
|
+
assert_equal :save_status, @status_transition.result
|
1023
|
+
end
|
1024
|
+
|
1025
|
+
def test_should_not_perform_if_action_fails_for_first_transition
|
1026
|
+
@klass.class_eval do
|
1027
|
+
def save_state
|
1028
|
+
false
|
1029
|
+
end
|
1030
|
+
end
|
1031
|
+
|
1032
|
+
assert_equal false, perform
|
1033
|
+
assert_equal 'parked', @object.state
|
1034
|
+
assert_equal 'first_gear', @object.status
|
1035
|
+
end
|
1036
|
+
|
1037
|
+
def test_should_not_perform_if_action_fails_for_second_transition
|
1038
|
+
@klass.class_eval do
|
1039
|
+
def save_status
|
1040
|
+
false
|
1041
|
+
end
|
1042
|
+
end
|
1043
|
+
|
1044
|
+
assert_equal false, perform
|
1045
|
+
assert_equal 'parked', @object.state
|
1046
|
+
assert_equal 'first_gear', @object.status
|
1047
|
+
end
|
1048
|
+
|
1049
|
+
def test_should_rollback_if_action_errors_for_first_transition
|
1050
|
+
@klass.class_eval do
|
1051
|
+
def save_state
|
1052
|
+
raise ArgumentError
|
1053
|
+
end
|
1054
|
+
end
|
1055
|
+
|
1056
|
+
begin; perform; rescue; end
|
1057
|
+
assert_equal 'parked', @object.state
|
1058
|
+
assert_equal 'first_gear', @object.status
|
1059
|
+
end
|
1060
|
+
|
1061
|
+
def test_should_rollback_if_action_errors_for_second_transition
|
1062
|
+
@klass.class_eval do
|
1063
|
+
def save_status
|
1064
|
+
raise ArgumentError
|
1065
|
+
end
|
1066
|
+
end
|
1067
|
+
|
1068
|
+
begin; perform; rescue; end
|
1069
|
+
assert_equal 'parked', @object.state
|
1070
|
+
assert_equal 'first_gear', @object.status
|
1071
|
+
end
|
1072
|
+
|
1073
|
+
private
|
1074
|
+
def perform(options = {})
|
1075
|
+
StateMachine::Transition.perform([@state_transition, @status_transition], options)
|
1076
|
+
end
|
1077
|
+
end
|
1078
|
+
|
1079
|
+
class TransitionsWithPerformBlockTest < Test::Unit::TestCase
|
1080
|
+
def setup
|
1081
|
+
@klass = Class.new
|
1082
|
+
|
1083
|
+
@state = StateMachine::Machine.new(@klass, :state, :initial => :parked)
|
1084
|
+
@state.event :ignite
|
1085
|
+
@state.state :parked, :idling
|
1086
|
+
|
1087
|
+
@status = StateMachine::Machine.new(@klass, :status, :initial => :first_gear)
|
1088
|
+
@status.event :shift_up
|
1089
|
+
@status.state :first_gear, :second_gear
|
1090
|
+
|
1091
|
+
@object = @klass.new
|
1092
|
+
@state_transition = StateMachine::Transition.new(@object, @state, :ignite, :parked, :idling)
|
1093
|
+
@status_transition = StateMachine::Transition.new(@object, @status, :shift_up, :first_gear, :second_gear)
|
1094
|
+
end
|
1095
|
+
|
1096
|
+
def test_should_be_perform_if_result_is_not_false
|
1097
|
+
assert StateMachine::Transition.perform([@state_transition, @status_transition]) { true }
|
1098
|
+
assert_equal 'idling', @object.state
|
1099
|
+
assert_equal 'second_gear', @object.status
|
1100
|
+
end
|
1101
|
+
|
1102
|
+
def test_should_not_perform_if_result_is_false
|
1103
|
+
assert !StateMachine::Transition.perform([@state_transition, @status_transition]) { false }
|
1104
|
+
assert_equal 'parked', @object.state
|
1105
|
+
assert_equal 'first_gear', @object.status
|
1106
|
+
end
|
1107
|
+
|
1108
|
+
def test_should_use_result_as_transition_result
|
1109
|
+
StateMachine::Transition.perform([@state_transition, @status_transition]) { 1 }
|
1110
|
+
assert_equal 1, @state_transition.result
|
1111
|
+
assert_equal 1, @status_transition.result
|
1112
|
+
end
|
1113
|
+
end
|