state_machine 0.6.3 → 0.7.0
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 +31 -1
- data/README.rdoc +33 -21
- data/Rakefile +2 -2
- 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/lib/state_machine/assertions.rb +2 -2
- data/lib/state_machine/callback.rb +14 -8
- data/lib/state_machine/condition_proxy.rb +3 -3
- data/lib/state_machine/event.rb +19 -21
- data/lib/state_machine/event_collection.rb +114 -0
- data/lib/state_machine/extensions.rb +127 -11
- data/lib/state_machine/guard.rb +1 -1
- data/lib/state_machine/integrations/active_record/locale.rb +2 -1
- data/lib/state_machine/integrations/active_record.rb +117 -39
- data/lib/state_machine/integrations/data_mapper/observer.rb +20 -64
- data/lib/state_machine/integrations/data_mapper.rb +71 -26
- data/lib/state_machine/integrations/sequel.rb +69 -21
- data/lib/state_machine/machine.rb +267 -139
- data/lib/state_machine/machine_collection.rb +145 -0
- data/lib/state_machine/matcher.rb +2 -2
- data/lib/state_machine/node_collection.rb +9 -4
- data/lib/state_machine/state.rb +22 -32
- data/lib/state_machine/state_collection.rb +66 -17
- data/lib/state_machine/transition.rb +259 -28
- data/lib/state_machine.rb +121 -56
- data/tasks/state_machine.rake +1 -0
- data/tasks/state_machine.rb +26 -0
- data/test/active_record.log +116877 -0
- data/test/functional/state_machine_test.rb +118 -12
- data/test/sequel.log +28542 -0
- data/test/unit/callback_test.rb +46 -1
- data/test/unit/condition_proxy_test.rb +55 -28
- data/test/unit/event_collection_test.rb +228 -0
- data/test/unit/event_test.rb +51 -46
- data/test/unit/integrations/active_record_test.rb +128 -70
- data/test/unit/integrations/data_mapper_test.rb +150 -58
- data/test/unit/integrations/sequel_test.rb +63 -6
- data/test/unit/invalid_event_test.rb +7 -0
- data/test/unit/machine_collection_test.rb +678 -0
- data/test/unit/machine_test.rb +198 -91
- data/test/unit/node_collection_test.rb +33 -30
- data/test/unit/state_collection_test.rb +112 -5
- data/test/unit/state_test.rb +23 -3
- data/test/unit/transition_test.rb +750 -89
- metadata +28 -3
@@ -0,0 +1,678 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
|
2
|
+
|
3
|
+
class MachineCollectionByDefaultTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@machines = StateMachine::MachineCollection.new
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_should_not_have_any_machines
|
9
|
+
assert @machines.empty?
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class MachineCollectionStateInitializationTest < Test::Unit::TestCase
|
14
|
+
def setup
|
15
|
+
@machines = StateMachine::MachineCollection.new
|
16
|
+
|
17
|
+
@klass = Class.new do
|
18
|
+
def initialize(attributes = {})
|
19
|
+
attributes.each do |attribute, value|
|
20
|
+
self.send("#{attribute}=", value)
|
21
|
+
end
|
22
|
+
|
23
|
+
super()
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
@machines[:state] = StateMachine::Machine.new(@klass, :state, :initial => :parked)
|
28
|
+
@machines[:alarm_state] = StateMachine::Machine.new(@klass, :alarm_state, :initial => :active)
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_should_set_states_if_nil
|
32
|
+
object = @klass.new
|
33
|
+
assert_equal 'parked', object.state
|
34
|
+
assert_equal 'active', object.alarm_state
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_should_set_states_if_empty
|
38
|
+
object = @klass.new(:state => '', :alarm_state => '')
|
39
|
+
assert_equal 'parked', object.state
|
40
|
+
assert_equal 'active', object.alarm_state
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_should_not_set_states_if_not_empty
|
44
|
+
object = @klass.new(:state => 'idling', :alarm_state => 'off')
|
45
|
+
assert_equal 'idling', object.state
|
46
|
+
assert_equal 'off', object.alarm_state
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class MachineCollectionFireExplicitTest < Test::Unit::TestCase
|
51
|
+
def setup
|
52
|
+
@machines = StateMachine::MachineCollection.new
|
53
|
+
|
54
|
+
@klass = Class.new do
|
55
|
+
attr_reader :saved
|
56
|
+
|
57
|
+
def save
|
58
|
+
@saved = true
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# First machine
|
63
|
+
@machines[:state] = @state = StateMachine::Machine.new(@klass, :state, :initial => :parked, :action => :save)
|
64
|
+
@state.event :ignite do
|
65
|
+
transition :parked => :idling
|
66
|
+
end
|
67
|
+
@state.event :park do
|
68
|
+
transition :idling => :parked
|
69
|
+
end
|
70
|
+
|
71
|
+
# Second machine
|
72
|
+
@machines[:alarm_state] = @alarm_state = StateMachine::Machine.new(@klass, :alarm_state, :initial => :active, :action => :save, :namespace => 'alarm')
|
73
|
+
@alarm_state.event :enable do
|
74
|
+
transition :off => :active
|
75
|
+
end
|
76
|
+
@alarm_state.event :disable do
|
77
|
+
transition :active => :off
|
78
|
+
end
|
79
|
+
|
80
|
+
@object = @klass.new
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_should_raise_exception_if_invalid_event_specified
|
84
|
+
exception = assert_raise(StateMachine::InvalidEvent) { @machines.fire_events(@object, :invalid) }
|
85
|
+
assert_equal ':invalid is an unknown state machine event', exception.message
|
86
|
+
|
87
|
+
exception = assert_raise(StateMachine::InvalidEvent) { @machines.fire_events(@object, :ignite, :invalid) }
|
88
|
+
assert_equal ':invalid is an unknown state machine event', exception.message
|
89
|
+
end
|
90
|
+
|
91
|
+
def test_should_fail_if_any_event_cannot_transition
|
92
|
+
assert !@machines.fire_events(@object, :park, :disable_alarm)
|
93
|
+
assert_equal 'parked', @object.state
|
94
|
+
assert_equal 'active', @object.alarm_state
|
95
|
+
assert !@object.saved
|
96
|
+
|
97
|
+
assert !@machines.fire_events(@object, :ignite, :enable_alarm)
|
98
|
+
assert_equal 'parked', @object.state
|
99
|
+
assert_equal 'active', @object.alarm_state
|
100
|
+
assert !@object.saved
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_should_be_successful_if_all_events_transition
|
104
|
+
assert @machines.fire_events(@object, :ignite, :disable_alarm)
|
105
|
+
assert_equal 'idling', @object.state
|
106
|
+
assert_equal 'off', @object.alarm_state
|
107
|
+
assert @object.saved
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_should_not_save_if_skipping_action
|
111
|
+
assert @machines.fire_events(@object, :ignite, :disable_alarm, false)
|
112
|
+
assert_equal 'idling', @object.state
|
113
|
+
assert_equal 'off', @object.alarm_state
|
114
|
+
assert !@object.saved
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
class MachineCollectionFireExplicitWithTransactionsTest < Test::Unit::TestCase
|
119
|
+
def setup
|
120
|
+
@machines = StateMachine::MachineCollection.new
|
121
|
+
|
122
|
+
@klass = Class.new do
|
123
|
+
attr_accessor :allow_save
|
124
|
+
|
125
|
+
def save
|
126
|
+
@allow_save
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
StateMachine::Integrations.const_set('Custom', Module.new do
|
131
|
+
attr_reader :rolled_back
|
132
|
+
|
133
|
+
def transaction(object)
|
134
|
+
@rolled_back = yield
|
135
|
+
end
|
136
|
+
end)
|
137
|
+
|
138
|
+
# First machine
|
139
|
+
@machines[:state] = @state = StateMachine::Machine.new(@klass, :state, :initial => :parked, :action => :save, :integration => :custom)
|
140
|
+
@state.event :ignite do
|
141
|
+
transition :parked => :idling
|
142
|
+
end
|
143
|
+
|
144
|
+
# Second machine
|
145
|
+
@machines[:alarm_state] = @alarm_state = StateMachine::Machine.new(@klass, :alarm_state, :initial => :active, :action => :save, :namespace => 'alarm', :integration => :custom)
|
146
|
+
@alarm_state.event :disable do
|
147
|
+
transition :active => :off
|
148
|
+
end
|
149
|
+
|
150
|
+
@object = @klass.new
|
151
|
+
end
|
152
|
+
|
153
|
+
def test_should_not_rollback_if_successful
|
154
|
+
@object.allow_save = true
|
155
|
+
|
156
|
+
assert @machines.fire_events(@object, :ignite, :disable_alarm)
|
157
|
+
assert_equal true, @state.rolled_back
|
158
|
+
assert_nil @alarm_state.rolled_back
|
159
|
+
assert_equal 'idling', @object.state
|
160
|
+
assert_equal 'off', @object.alarm_state
|
161
|
+
end
|
162
|
+
|
163
|
+
def test_should_rollback_if_not_successful
|
164
|
+
@object.allow_save = false
|
165
|
+
|
166
|
+
assert !@machines.fire_events(@object, :ignite, :disable_alarm)
|
167
|
+
assert_equal false, @state.rolled_back
|
168
|
+
assert_nil @alarm_state.rolled_back
|
169
|
+
assert_equal 'parked', @object.state
|
170
|
+
assert_equal 'active', @object.alarm_state
|
171
|
+
end
|
172
|
+
|
173
|
+
def teardown
|
174
|
+
StateMachine::Integrations.send(:remove_const, 'Custom')
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
class MachineCollectionFireExplicitWithValidationsTest < Test::Unit::TestCase
|
179
|
+
def setup
|
180
|
+
StateMachine::Integrations.const_set('Custom', Module.new do
|
181
|
+
def invalidate(object, attribute, message, values = [])
|
182
|
+
(object.errors ||= []) << generate_message(message, values)
|
183
|
+
end
|
184
|
+
|
185
|
+
def reset(object)
|
186
|
+
object.errors = []
|
187
|
+
end
|
188
|
+
end)
|
189
|
+
|
190
|
+
@klass = Class.new do
|
191
|
+
attr_accessor :errors
|
192
|
+
|
193
|
+
def initialize
|
194
|
+
@errors = []
|
195
|
+
super
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
@machines = StateMachine::MachineCollection.new
|
200
|
+
@machines[:state] = @state = StateMachine::Machine.new(@klass, :state, :initial => :parked, :integration => :custom)
|
201
|
+
@state.event :ignite do
|
202
|
+
transition :parked => :idling
|
203
|
+
end
|
204
|
+
|
205
|
+
@machines[:alarm_state] = @alarm_state = StateMachine::Machine.new(@klass, :alarm_state, :initial => :active, :namespace => 'alarm', :integration => :custom)
|
206
|
+
@alarm_state.event :disable do
|
207
|
+
transition :active => :off
|
208
|
+
end
|
209
|
+
|
210
|
+
@object = @klass.new
|
211
|
+
end
|
212
|
+
|
213
|
+
def test_should_not_invalidate_if_transitions_exist
|
214
|
+
assert @machines.fire_events(@object, :ignite, :disable_alarm)
|
215
|
+
assert_equal [], @object.errors
|
216
|
+
end
|
217
|
+
|
218
|
+
def test_should_invalidate_if_no_transitions_exist
|
219
|
+
@object.state = 'idling'
|
220
|
+
@object.alarm_state = 'off'
|
221
|
+
|
222
|
+
assert !@machines.fire_events(@object, :ignite, :disable_alarm)
|
223
|
+
assert_equal ['cannot transition via "ignite"', 'cannot transition via "disable_alarm"'], @object.errors
|
224
|
+
end
|
225
|
+
|
226
|
+
def teardown
|
227
|
+
StateMachine::Integrations.send(:remove_const, 'Custom')
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
class MachineCollectionFireImplicitTest < Test::Unit::TestCase
|
232
|
+
def setup
|
233
|
+
@klass = Class.new
|
234
|
+
|
235
|
+
@machines = StateMachine::MachineCollection.new
|
236
|
+
@machines[:state] = @machine = StateMachine::Machine.new(@klass, :state, :initial => :parked, :action => :save)
|
237
|
+
@machine.event :ignite do
|
238
|
+
transition :parked => :idling
|
239
|
+
end
|
240
|
+
|
241
|
+
@saved = false
|
242
|
+
@object = @klass.new
|
243
|
+
end
|
244
|
+
|
245
|
+
def default_test
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
class MachineCollectionFireImplicitWithoutEventTest < MachineCollectionFireImplicitTest
|
250
|
+
def setup
|
251
|
+
super
|
252
|
+
|
253
|
+
@object.state_event = nil
|
254
|
+
@result = @machines.fire_attribute_events(@object, :save) { @saved = true }
|
255
|
+
end
|
256
|
+
|
257
|
+
def test_should_be_successful
|
258
|
+
assert_equal true, @result
|
259
|
+
end
|
260
|
+
|
261
|
+
def test_should_run_action
|
262
|
+
assert @saved
|
263
|
+
end
|
264
|
+
|
265
|
+
def test_should_not_transition_state
|
266
|
+
assert_equal 'parked', @object.state
|
267
|
+
end
|
268
|
+
|
269
|
+
def test_should_not_change_event_attribute
|
270
|
+
assert_nil @object.state_event
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
class MachineCollectionFireImplicitWithBlankEventTest < MachineCollectionFireImplicitTest
|
275
|
+
def setup
|
276
|
+
super
|
277
|
+
|
278
|
+
@object.state_event = ''
|
279
|
+
@result = @machines.fire_attribute_events(@object, :save) { @saved = true }
|
280
|
+
end
|
281
|
+
|
282
|
+
def test_should_be_successful
|
283
|
+
assert_equal true, @result
|
284
|
+
end
|
285
|
+
|
286
|
+
def test_should_run_action
|
287
|
+
assert @saved
|
288
|
+
end
|
289
|
+
|
290
|
+
def test_should_not_transition_state
|
291
|
+
assert_equal 'parked', @object.state
|
292
|
+
end
|
293
|
+
|
294
|
+
def test_should_not_change_event_attribute
|
295
|
+
assert_nil @object.state_event
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
class MachineCollectionFireImplicitWithInvalidEventTest < MachineCollectionFireImplicitTest
|
300
|
+
def setup
|
301
|
+
super
|
302
|
+
|
303
|
+
@object.state_event = 'invalid'
|
304
|
+
@result = @machines.fire_attribute_events(@object, :save) { @saved = true }
|
305
|
+
end
|
306
|
+
|
307
|
+
def test_should_not_be_successful
|
308
|
+
assert_equal false, @result
|
309
|
+
end
|
310
|
+
|
311
|
+
def test_should_not_run_action
|
312
|
+
assert !@saved
|
313
|
+
end
|
314
|
+
|
315
|
+
def test_should_not_transition_state
|
316
|
+
assert_equal 'parked', @object.state
|
317
|
+
end
|
318
|
+
|
319
|
+
def test_should_not_reset_event_attribute
|
320
|
+
assert_equal :invalid, @object.state_event
|
321
|
+
end
|
322
|
+
|
323
|
+
def test_should_not_have_event_transition
|
324
|
+
assert_nil @object.state_event_transition
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
class MachineCollectionFireImplicitWithoutTransitionTest < MachineCollectionFireImplicitTest
|
329
|
+
def setup
|
330
|
+
super
|
331
|
+
|
332
|
+
@object.state = 'idling'
|
333
|
+
@object.state_event = 'ignite'
|
334
|
+
@result = @machines.fire_attribute_events(@object, :save) { @saved = true }
|
335
|
+
end
|
336
|
+
|
337
|
+
def test_should_not_be_successful
|
338
|
+
assert_equal false, @result
|
339
|
+
end
|
340
|
+
|
341
|
+
def test_should_not_run_action
|
342
|
+
assert !@saved
|
343
|
+
end
|
344
|
+
|
345
|
+
def test_should_not_transition_state
|
346
|
+
assert_equal 'idling', @object.state
|
347
|
+
end
|
348
|
+
|
349
|
+
def test_should_not_reset_event_attribute
|
350
|
+
assert_equal :ignite, @object.state_event
|
351
|
+
end
|
352
|
+
|
353
|
+
def test_should_not_have_event_transition
|
354
|
+
assert_nil @object.state_event_transition
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
class MachineCollectionFireImplicitWithTransitionTest < MachineCollectionFireImplicitTest
|
359
|
+
def setup
|
360
|
+
super
|
361
|
+
|
362
|
+
@state_event = nil
|
363
|
+
@state_event_transition = nil
|
364
|
+
|
365
|
+
@object.state_event = 'ignite'
|
366
|
+
@result = @machines.fire_attribute_events(@object, :save) do
|
367
|
+
@state_event = @object.state_event
|
368
|
+
@state_event_transition = @object.state_event_transition
|
369
|
+
@saved = true
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
def test_should_be_successful
|
374
|
+
assert_equal true, @result
|
375
|
+
end
|
376
|
+
|
377
|
+
def test_should_run_action
|
378
|
+
assert @saved
|
379
|
+
end
|
380
|
+
|
381
|
+
def test_should_not_have_event_while_running_action
|
382
|
+
assert_nil @state_event
|
383
|
+
end
|
384
|
+
|
385
|
+
def test_should_not_have_event_transition_while_running_action
|
386
|
+
assert_nil @state_event_transition
|
387
|
+
end
|
388
|
+
|
389
|
+
def test_should_transition_state
|
390
|
+
assert_equal 'idling', @object.state
|
391
|
+
end
|
392
|
+
|
393
|
+
def test_should_reset_event_attribute
|
394
|
+
assert_nil @object.state_event
|
395
|
+
end
|
396
|
+
|
397
|
+
def test_should_reset_event_transition
|
398
|
+
assert_nil @object.state_event_transition
|
399
|
+
end
|
400
|
+
end
|
401
|
+
|
402
|
+
class MachineCollectionFireImplicitWithActionFailureTest < MachineCollectionFireImplicitTest
|
403
|
+
def setup
|
404
|
+
super
|
405
|
+
|
406
|
+
@object.state_event = 'ignite'
|
407
|
+
@result = @machines.fire_attribute_events(@object, :save) { false }
|
408
|
+
end
|
409
|
+
|
410
|
+
def test_should_not_be_successful
|
411
|
+
assert_equal false, @result
|
412
|
+
end
|
413
|
+
|
414
|
+
def test_should_not_transition_state
|
415
|
+
assert_equal 'parked', @object.state
|
416
|
+
end
|
417
|
+
|
418
|
+
def test_should_not_reset_event_attribute
|
419
|
+
assert_equal :ignite, @object.state_event
|
420
|
+
end
|
421
|
+
|
422
|
+
def test_should_not_have_event_transition
|
423
|
+
assert_nil @object.state_event_transition
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
class MachineCollectionFireImplicitWithActionErrorTest < MachineCollectionFireImplicitTest
|
428
|
+
def setup
|
429
|
+
super
|
430
|
+
|
431
|
+
@object.state_event = 'ignite'
|
432
|
+
assert_raise(ArgumentError) { @machines.fire_attribute_events(@object, :save) { raise ArgumentError } }
|
433
|
+
end
|
434
|
+
|
435
|
+
def test_should_not_transition_state
|
436
|
+
assert_equal 'parked', @object.state
|
437
|
+
end
|
438
|
+
|
439
|
+
def test_should_not_reset_event_attribute
|
440
|
+
assert_equal :ignite, @object.state_event
|
441
|
+
end
|
442
|
+
|
443
|
+
def test_should_not_have_event_transition
|
444
|
+
assert_nil @object.state_event_transition
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
class MachineCollectionFireImplicitPartialTest < MachineCollectionFireImplicitTest
|
449
|
+
def setup
|
450
|
+
super
|
451
|
+
|
452
|
+
@ran_before_callback = false
|
453
|
+
@ran_after_callback = false
|
454
|
+
@machine.before_transition { @ran_before_callback = true }
|
455
|
+
@machine.after_transition { @ran_after_callback = true }
|
456
|
+
|
457
|
+
@state_event = nil
|
458
|
+
@state_event_transition = nil
|
459
|
+
|
460
|
+
@object.state_event = 'ignite'
|
461
|
+
@result = @machines.fire_attribute_events(@object, :save, false) do
|
462
|
+
@state_event = @object.state_event
|
463
|
+
@state_event_transition = @object.state_event_transition
|
464
|
+
true
|
465
|
+
end
|
466
|
+
end
|
467
|
+
|
468
|
+
def test_should_run_before_callbacks
|
469
|
+
assert @ran_before_callback
|
470
|
+
end
|
471
|
+
|
472
|
+
def test_should_not_run_after_callbacks
|
473
|
+
assert !@ran_after_callback
|
474
|
+
end
|
475
|
+
|
476
|
+
def test_should_be_successful
|
477
|
+
assert @result
|
478
|
+
end
|
479
|
+
|
480
|
+
def test_should_not_have_event_while_running_action
|
481
|
+
assert_nil @state_event
|
482
|
+
end
|
483
|
+
|
484
|
+
def test_should_not_have_event_transition_while_running_action
|
485
|
+
assert_nil @state_event_transition
|
486
|
+
end
|
487
|
+
|
488
|
+
def test_should_transition_state
|
489
|
+
assert_equal 'idling', @object.state
|
490
|
+
end
|
491
|
+
|
492
|
+
def test_should_not_reset_event_attribute
|
493
|
+
assert_equal :ignite, @object.state_event
|
494
|
+
end
|
495
|
+
|
496
|
+
def test_should_have_event_transition
|
497
|
+
assert_not_nil @object.state_event_transition
|
498
|
+
end
|
499
|
+
|
500
|
+
def test_should_reset_event_attributes_after_next_fire_on_success
|
501
|
+
assert @machines.fire_attribute_events(@object, :save) { true }
|
502
|
+
assert_equal 'idling', @object.state
|
503
|
+
assert_nil @object.state_event
|
504
|
+
assert_nil @object.state_event_transition
|
505
|
+
end
|
506
|
+
|
507
|
+
def test_should_rollback_all_attributes_after_next_fire_on_failure
|
508
|
+
assert !@machines.fire_attribute_events(@object, :save) { false }
|
509
|
+
assert_equal 'parked', @object.state
|
510
|
+
assert_equal :ignite, @object.state_event
|
511
|
+
assert_nil @object.state_event_transition
|
512
|
+
end
|
513
|
+
|
514
|
+
def test_should_rollback_all_attributes_after_next_fire_on_error
|
515
|
+
assert_raise(ArgumentError) { @machines.fire_attribute_events(@object, :save) { raise ArgumentError } }
|
516
|
+
assert_equal 'parked', @object.state
|
517
|
+
assert_equal :ignite, @object.state_event
|
518
|
+
assert_nil @object.state_event_transition
|
519
|
+
end
|
520
|
+
end
|
521
|
+
|
522
|
+
class MachineCollectionFireImplicitNestedPartialTest < MachineCollectionFireImplicitTest
|
523
|
+
def setup
|
524
|
+
super
|
525
|
+
|
526
|
+
@partial_result = nil
|
527
|
+
|
528
|
+
@object.state_event = 'ignite'
|
529
|
+
@result = @machines.fire_attribute_events(@object, :save) do
|
530
|
+
@partial_result = @machines.fire_attribute_events(@object, :save, false) { true }
|
531
|
+
true
|
532
|
+
end
|
533
|
+
end
|
534
|
+
|
535
|
+
def test_should_be_successful
|
536
|
+
assert @result
|
537
|
+
end
|
538
|
+
|
539
|
+
def test_should_have_successful_partial_fire
|
540
|
+
assert @partial_result
|
541
|
+
end
|
542
|
+
|
543
|
+
def test_should_transition_state
|
544
|
+
assert_equal 'idling', @object.state
|
545
|
+
end
|
546
|
+
|
547
|
+
def test_should_reset_event_attribute
|
548
|
+
assert_nil @object.state_event
|
549
|
+
end
|
550
|
+
|
551
|
+
def test_should_not_have_event_transition
|
552
|
+
assert_nil @object.state_event_transition
|
553
|
+
end
|
554
|
+
end
|
555
|
+
|
556
|
+
class MachineCollectionFireImplicitWithDifferentActionsTest < MachineCollectionFireImplicitTest
|
557
|
+
def setup
|
558
|
+
super
|
559
|
+
|
560
|
+
@machines[:alarm_state] = @alarm_machine = StateMachine::Machine.new(@klass, :alarm_state, :initial => :active, :action => :save_alarm)
|
561
|
+
@alarm_machine.event :disable do
|
562
|
+
transition :active => :off
|
563
|
+
end
|
564
|
+
|
565
|
+
@saved = false
|
566
|
+
@object = @klass.new
|
567
|
+
@object.state_event = 'ignite'
|
568
|
+
@object.alarm_state_event = 'disable'
|
569
|
+
|
570
|
+
@machines.fire_attribute_events(@object, :save) { true }
|
571
|
+
end
|
572
|
+
|
573
|
+
def test_should_transition_states_for_action
|
574
|
+
assert_equal 'idling', @object.state
|
575
|
+
end
|
576
|
+
|
577
|
+
def test_should_reset_event_attributes_for_action
|
578
|
+
assert_nil @object.state_event
|
579
|
+
end
|
580
|
+
|
581
|
+
def test_should_not_transition_states_for_other_actions
|
582
|
+
assert_equal 'active', @object.alarm_state
|
583
|
+
end
|
584
|
+
|
585
|
+
def test_should_not_reset_event_attributes_for_other_actions
|
586
|
+
assert_equal :disable, @object.alarm_state_event
|
587
|
+
end
|
588
|
+
end
|
589
|
+
|
590
|
+
class MachineCollectionFireImplicitWithSameActionsTest < MachineCollectionFireImplicitTest
|
591
|
+
def setup
|
592
|
+
super
|
593
|
+
|
594
|
+
@machines[:alarm_state] = @alarm_machine = StateMachine::Machine.new(@klass, :alarm_state, :initial => :active, :action => :save)
|
595
|
+
@alarm_machine.event :disable do
|
596
|
+
transition :active => :off
|
597
|
+
end
|
598
|
+
|
599
|
+
@saved = false
|
600
|
+
@object = @klass.new
|
601
|
+
@object.state_event = 'ignite'
|
602
|
+
@object.alarm_state_event = 'disable'
|
603
|
+
|
604
|
+
@machines.fire_attribute_events(@object, :save) { true }
|
605
|
+
end
|
606
|
+
|
607
|
+
def test_should_transition_all_states_for_action
|
608
|
+
assert_equal 'idling', @object.state
|
609
|
+
assert_equal 'off', @object.alarm_state
|
610
|
+
end
|
611
|
+
|
612
|
+
def test_should_reset_all_event_attributes_for_action
|
613
|
+
assert_nil @object.state_event
|
614
|
+
assert_nil @object.alarm_state_event
|
615
|
+
end
|
616
|
+
|
617
|
+
def test_should_reset_all_event_transitions_for_action
|
618
|
+
assert_nil @object.state_event_transition
|
619
|
+
assert_nil @object.alarm_state_event_transition
|
620
|
+
end
|
621
|
+
end
|
622
|
+
|
623
|
+
class MachineCollectionFireImplicitWithValidationsTest < Test::Unit::TestCase
|
624
|
+
def setup
|
625
|
+
StateMachine::Integrations.const_set('Custom', Module.new do
|
626
|
+
def invalidate(object, attribute, message, values = [])
|
627
|
+
(object.errors ||= []) << generate_message(message, values)
|
628
|
+
end
|
629
|
+
|
630
|
+
def reset(object)
|
631
|
+
object.errors = []
|
632
|
+
end
|
633
|
+
end)
|
634
|
+
|
635
|
+
@klass = Class.new do
|
636
|
+
attr_accessor :errors
|
637
|
+
|
638
|
+
def initialize
|
639
|
+
@errors = []
|
640
|
+
super
|
641
|
+
end
|
642
|
+
end
|
643
|
+
|
644
|
+
@machines = StateMachine::MachineCollection.new
|
645
|
+
@machines[:state] = @machine = StateMachine::Machine.new(@klass, :state, :initial => :parked, :action => :save, :integration => :custom)
|
646
|
+
@machine.event :ignite do
|
647
|
+
transition :parked => :idling
|
648
|
+
end
|
649
|
+
|
650
|
+
@object = @klass.new
|
651
|
+
end
|
652
|
+
|
653
|
+
def test_should_invalidate_if_event_is_invalid
|
654
|
+
@object.state_event = 'invalid'
|
655
|
+
@machines.fire_attribute_events(@object, :save) { true }
|
656
|
+
|
657
|
+
assert !@object.errors.empty?
|
658
|
+
end
|
659
|
+
|
660
|
+
def test_should_invalidate_if_no_transition_exists
|
661
|
+
@object.state = 'idling'
|
662
|
+
@object.state_event = 'ignite'
|
663
|
+
@machines.fire_attribute_events(@object, :save) { true }
|
664
|
+
|
665
|
+
assert !@object.errors.empty?
|
666
|
+
end
|
667
|
+
|
668
|
+
def test_should_not_invalidate_if_transition_exists
|
669
|
+
@object.state_event = 'ignite'
|
670
|
+
@machines.fire_attribute_events(@object, :save) { true }
|
671
|
+
|
672
|
+
assert @object.errors.empty?
|
673
|
+
end
|
674
|
+
|
675
|
+
def teardown
|
676
|
+
StateMachine::Integrations.send(:remove_const, 'Custom')
|
677
|
+
end
|
678
|
+
end
|