state_machine 0.3.1 → 0.4.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 +26 -0
- data/README.rdoc +254 -46
- data/Rakefile +29 -3
- data/examples/AutoShop_state.png +0 -0
- data/examples/Car_state.jpg +0 -0
- data/examples/Vehicle_state.png +0 -0
- data/lib/state_machine.rb +161 -116
- data/lib/state_machine/assertions.rb +21 -0
- data/lib/state_machine/callback.rb +168 -0
- data/lib/state_machine/eval_helpers.rb +67 -0
- data/lib/state_machine/event.rb +135 -101
- data/lib/state_machine/extensions.rb +83 -0
- data/lib/state_machine/guard.rb +115 -0
- data/lib/state_machine/integrations/active_record.rb +242 -0
- data/lib/state_machine/integrations/data_mapper.rb +198 -0
- data/lib/state_machine/integrations/data_mapper/observer.rb +153 -0
- data/lib/state_machine/integrations/sequel.rb +169 -0
- data/lib/state_machine/machine.rb +746 -352
- data/lib/state_machine/transition.rb +104 -212
- data/test/active_record.log +34865 -0
- data/test/classes/switch.rb +11 -0
- data/test/data_mapper.log +14015 -0
- data/test/functional/state_machine_test.rb +249 -15
- data/test/sequel.log +3835 -0
- data/test/test_helper.rb +3 -12
- data/test/unit/assertions_test.rb +13 -0
- data/test/unit/callback_test.rb +189 -0
- data/test/unit/eval_helpers_test.rb +92 -0
- data/test/unit/event_test.rb +247 -113
- data/test/unit/guard_test.rb +420 -0
- data/test/unit/integrations/active_record_test.rb +515 -0
- data/test/unit/integrations/data_mapper_test.rb +407 -0
- data/test/unit/integrations/sequel_test.rb +244 -0
- data/test/unit/invalid_transition_test.rb +1 -1
- data/test/unit/machine_test.rb +1056 -98
- data/test/unit/state_machine_test.rb +14 -113
- data/test/unit/transition_test.rb +269 -495
- metadata +44 -30
- data/test/app_root/app/models/auto_shop.rb +0 -34
- data/test/app_root/app/models/car.rb +0 -19
- data/test/app_root/app/models/highway.rb +0 -3
- data/test/app_root/app/models/motorcycle.rb +0 -3
- data/test/app_root/app/models/switch.rb +0 -23
- data/test/app_root/app/models/switch_observer.rb +0 -20
- data/test/app_root/app/models/toggle_switch.rb +0 -2
- data/test/app_root/app/models/vehicle.rb +0 -78
- data/test/app_root/config/environment.rb +0 -7
- data/test/app_root/db/migrate/001_create_switches.rb +0 -12
- data/test/app_root/db/migrate/002_create_auto_shops.rb +0 -13
- data/test/app_root/db/migrate/003_create_highways.rb +0 -11
- data/test/app_root/db/migrate/004_create_vehicles.rb +0 -16
- data/test/factory.rb +0 -77
@@ -2,6 +2,6 @@ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
|
|
2
2
|
|
3
3
|
class InvalidTransitionTest < Test::Unit::TestCase
|
4
4
|
def test_should_exist
|
5
|
-
assert_not_nil
|
5
|
+
assert_not_nil StateMachine::InvalidTransition
|
6
6
|
end
|
7
7
|
end
|
data/test/unit/machine_test.rb
CHANGED
@@ -2,7 +2,13 @@ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
|
|
2
2
|
|
3
3
|
class MachineByDefaultTest < Test::Unit::TestCase
|
4
4
|
def setup
|
5
|
-
@
|
5
|
+
@klass = Class.new
|
6
|
+
@machine = StateMachine::Machine.new(@klass)
|
7
|
+
@object = @klass.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_should_have_an_owner_class
|
11
|
+
assert_equal @klass, @machine.owner_class
|
6
12
|
end
|
7
13
|
|
8
14
|
def test_should_have_an_attribute
|
@@ -10,77 +16,299 @@ class MachineByDefaultTest < Test::Unit::TestCase
|
|
10
16
|
end
|
11
17
|
|
12
18
|
def test_should_not_have_an_initial_state
|
13
|
-
assert_nil @machine.initial_state(
|
14
|
-
end
|
15
|
-
|
16
|
-
def test_should_have_an_owner_class
|
17
|
-
assert_equal Switch, @machine.owner_class
|
19
|
+
assert_nil @machine.initial_state(@object)
|
18
20
|
end
|
19
21
|
|
20
22
|
def test_should_not_have_any_events
|
21
23
|
assert @machine.events.empty?
|
22
24
|
end
|
23
25
|
|
26
|
+
def test_should_not_have_any_before_callbacks
|
27
|
+
assert @machine.callbacks[:before].empty?
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_should_not_have_any_after_callbacks
|
31
|
+
assert @machine.callbacks[:after].empty?
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_should_not_have_an_action
|
35
|
+
assert_nil @machine.action
|
36
|
+
end
|
37
|
+
|
24
38
|
def test_should_not_have_any_states
|
25
39
|
assert @machine.states.empty?
|
26
40
|
end
|
41
|
+
|
42
|
+
def test_should_not_be_extended_by_the_active_record_integration
|
43
|
+
assert !(class << @machine; ancestors; end).include?(StateMachine::Integrations::ActiveRecord)
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_should_not_be_extended_by_the_datamapper_integration
|
47
|
+
assert !(class << @machine; ancestors; end).include?(StateMachine::Integrations::DataMapper)
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_should_not_be_extended_by_the_sequel_integration
|
51
|
+
assert !(class << @machine; ancestors; end).include?(StateMachine::Integrations::Sequel)
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_should_define_a_reader_attribute_for_the_attribute
|
55
|
+
assert @object.respond_to?(:state)
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_should_define_a_writer_attribute_for_the_attribute
|
59
|
+
assert @object.respond_to?(:state=)
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_should_define_a_predicate_for_the_attribute
|
63
|
+
assert @object.respond_to?(:state?)
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_should_not_define_singular_with_scope
|
67
|
+
assert !@klass.respond_to?(:with_state)
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_should_not_define_singular_without_scope
|
71
|
+
assert !@klass.respond_to?(:without_state)
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_should_not_define_plural_with_scope
|
75
|
+
assert !@klass.respond_to?(:with_states)
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_should_not_define_plural_without_scope
|
79
|
+
assert !@klass.respond_to?(:without_states)
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_should_extend_owner_class_with_class_methods
|
83
|
+
assert (class << @klass; ancestors; end).include?(StateMachine::ClassMethods)
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_should_include_instance_methods_in_owner_class
|
87
|
+
assert @klass.included_modules.include?(StateMachine::InstanceMethods)
|
88
|
+
end
|
89
|
+
|
90
|
+
def test_should_define_state_machines_reader
|
91
|
+
expected = {'state' => @machine}
|
92
|
+
assert_equal expected, @klass.state_machines
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
class MachineWithCustomAttributeTest < Test::Unit::TestCase
|
97
|
+
def setup
|
98
|
+
@klass = Class.new
|
99
|
+
@machine = StateMachine::Machine.new(@klass, 'status')
|
100
|
+
@object = @klass.new
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_should_use_custom_attribute
|
104
|
+
assert_equal 'status', @machine.attribute
|
105
|
+
end
|
106
|
+
|
107
|
+
def test_should_define_a_reader_attribute_for_the_attribute
|
108
|
+
assert @object.respond_to?(:status)
|
109
|
+
end
|
110
|
+
|
111
|
+
def test_should_define_a_writer_attribute_for_the_attribute
|
112
|
+
assert @object.respond_to?(:status=)
|
113
|
+
end
|
114
|
+
|
115
|
+
def test_should_define_a_predicate_for_the_attribute
|
116
|
+
assert @object.respond_to?(:status?)
|
117
|
+
end
|
27
118
|
end
|
28
119
|
|
29
|
-
class
|
120
|
+
class MachineWithStaticInitialStateTest < Test::Unit::TestCase
|
30
121
|
def setup
|
31
|
-
@
|
122
|
+
@klass = Class.new do
|
123
|
+
def initialize(attributes = {})
|
124
|
+
attributes.each {|attr, value| send("#{attr}=", value)}
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
@machine = StateMachine::Machine.new(@klass, :initial => 'off')
|
32
129
|
end
|
33
130
|
|
34
131
|
def test_should_have_an_initial_state
|
35
|
-
|
132
|
+
object = @klass.new
|
133
|
+
assert_equal 'off', @machine.initial_state(object)
|
134
|
+
end
|
135
|
+
|
136
|
+
def test_should_set_initial_state_if_existing_is_nil
|
137
|
+
object = @klass.new(:state => nil)
|
138
|
+
assert_equal 'off', object.state
|
139
|
+
end
|
140
|
+
|
141
|
+
def test_should_set_initial_state_if_existing_is_empty
|
142
|
+
object = @klass.new(:state => '')
|
143
|
+
assert_equal 'off', object.state
|
144
|
+
end
|
145
|
+
|
146
|
+
def test_should_not_set_initial_state_if_existing_is_not_empty
|
147
|
+
object = @klass.new(:state => 'on')
|
148
|
+
assert_equal 'on', object.state
|
149
|
+
end
|
150
|
+
|
151
|
+
def test_should_be_included_in_known_states
|
152
|
+
assert_equal %w(off), @machine.states
|
36
153
|
end
|
37
154
|
end
|
38
155
|
|
39
156
|
class MachineWithDynamicInitialStateTest < Test::Unit::TestCase
|
40
157
|
def setup
|
41
|
-
@
|
42
|
-
|
158
|
+
@klass = Class.new do
|
159
|
+
attr_accessor :initial_state
|
160
|
+
end
|
161
|
+
@machine = StateMachine::Machine.new(@klass, :initial => lambda {|object| object.initial_state || 'default'})
|
162
|
+
@object = @klass.new
|
43
163
|
end
|
44
164
|
|
45
165
|
def test_should_use_the_record_for_determining_the_initial_state
|
46
|
-
|
47
|
-
assert_equal '
|
166
|
+
@object.initial_state = 'off'
|
167
|
+
assert_equal 'off', @machine.initial_state(@object)
|
168
|
+
|
169
|
+
@object.initial_state = 'on'
|
170
|
+
assert_equal 'on', @machine.initial_state(@object)
|
171
|
+
end
|
172
|
+
|
173
|
+
def test_should_set_initial_state_on_created_object
|
174
|
+
assert_equal 'default', @object.state
|
175
|
+
end
|
176
|
+
|
177
|
+
def test_should_not_be_included_in_known_states
|
178
|
+
assert_equal [], @machine.states
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
class MachineWithCustomActionTest < Test::Unit::TestCase
|
183
|
+
def setup
|
184
|
+
@machine = StateMachine::Machine.new(Class.new, :action => :save)
|
185
|
+
end
|
186
|
+
|
187
|
+
def test_should_use_the_custom_action
|
188
|
+
assert_equal :save, @machine.action
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
class MachineWithNilActionTest < Test::Unit::TestCase
|
193
|
+
def setup
|
194
|
+
integration = Module.new do
|
195
|
+
def default_action
|
196
|
+
:save
|
197
|
+
end
|
198
|
+
end
|
199
|
+
StateMachine::Integrations.const_set('Custom', integration)
|
200
|
+
@machine = StateMachine::Machine.new(Class.new, :action => nil, :integration => :custom)
|
201
|
+
end
|
202
|
+
|
203
|
+
def test_should_have_a_nil_action
|
204
|
+
assert_nil @machine.action
|
205
|
+
end
|
206
|
+
|
207
|
+
def teardown
|
208
|
+
StateMachine::Integrations.send(:remove_const, 'Custom')
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
class MachineWithCustomIntegrationTest < Test::Unit::TestCase
|
213
|
+
def setup
|
214
|
+
StateMachine::Integrations.const_set('Custom', Module.new)
|
215
|
+
@machine = StateMachine::Machine.new(Class.new, :integration => :custom)
|
216
|
+
end
|
217
|
+
|
218
|
+
def test_should_be_extended_by_the_integration
|
219
|
+
assert (class << @machine; ancestors; end).include?(StateMachine::Integrations::Custom)
|
220
|
+
end
|
221
|
+
|
222
|
+
def teardown
|
223
|
+
StateMachine::Integrations.send(:remove_const, 'Custom')
|
48
224
|
end
|
49
225
|
end
|
50
226
|
|
51
227
|
class MachineTest < Test::Unit::TestCase
|
228
|
+
def test_should_raise_exception_if_invalid_option_specified
|
229
|
+
assert_raise(ArgumentError) {StateMachine::Machine.new(Class.new, :invalid => true)}
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
class MachineWithoutIntegrationTest < Test::Unit::TestCase
|
52
234
|
def setup
|
53
|
-
@
|
235
|
+
@klass = Class.new
|
236
|
+
@machine = StateMachine::Machine.new(@klass)
|
237
|
+
@object = @klass.new
|
54
238
|
end
|
55
239
|
|
56
|
-
def
|
57
|
-
|
58
|
-
|
240
|
+
def test_transaction_should_yield
|
241
|
+
@yielded = false
|
242
|
+
@machine.within_transaction(@object) do
|
243
|
+
@yielded = true
|
244
|
+
end
|
59
245
|
|
60
|
-
|
246
|
+
assert @yielded
|
61
247
|
end
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
248
|
+
end
|
249
|
+
|
250
|
+
class MachineWithIntegrationTest < Test::Unit::TestCase
|
251
|
+
def setup
|
252
|
+
@integration = Module.new do
|
253
|
+
class << self; attr_accessor :initialized, :with_scopes, :without_scopes; end
|
254
|
+
@initialized = false
|
255
|
+
@with_scopes = []
|
256
|
+
@without_scopes = []
|
257
|
+
|
258
|
+
def after_initialize
|
259
|
+
StateMachine::Integrations::Custom.initialized = true
|
260
|
+
end
|
261
|
+
|
262
|
+
def default_action
|
263
|
+
:save
|
264
|
+
end
|
265
|
+
|
266
|
+
def define_with_scope(name)
|
267
|
+
StateMachine::Integrations::Custom.with_scopes << name
|
268
|
+
end
|
269
|
+
|
270
|
+
def define_without_scope(name)
|
271
|
+
StateMachine::Integrations::Custom.without_scopes << name
|
272
|
+
end
|
273
|
+
end
|
66
274
|
|
67
|
-
|
275
|
+
StateMachine::Integrations.const_set('Custom', @integration)
|
276
|
+
@machine = StateMachine::Machine.new(Class.new, :integration => :custom)
|
68
277
|
end
|
69
278
|
|
70
|
-
def
|
71
|
-
|
279
|
+
def test_should_call_after_initialize_hook
|
280
|
+
assert @integration.initialized
|
281
|
+
end
|
282
|
+
|
283
|
+
def test_should_use_the_default_action
|
284
|
+
assert_equal :save, @machine.action
|
72
285
|
end
|
73
286
|
|
74
|
-
def
|
75
|
-
machine =
|
76
|
-
assert_equal
|
287
|
+
def test_should_use_the_custom_action_if_specified
|
288
|
+
machine = StateMachine::Machine.new(Class.new, :integration => :custom, :action => :save!)
|
289
|
+
assert_equal :save!, machine.action
|
290
|
+
end
|
291
|
+
|
292
|
+
def test_should_define_a_singular_and_plural_with_scope
|
293
|
+
assert_equal %w(with_state with_states), @integration.with_scopes
|
294
|
+
end
|
295
|
+
|
296
|
+
def test_should_define_a_singular_and_plural_without_scope
|
297
|
+
assert_equal %w(without_state without_states), @integration.without_scopes
|
298
|
+
end
|
299
|
+
|
300
|
+
def teardown
|
301
|
+
StateMachine::Integrations.send(:remove_const, 'Custom')
|
77
302
|
end
|
78
303
|
end
|
79
304
|
|
80
305
|
class MachineAfterBeingCopiedTest < Test::Unit::TestCase
|
81
306
|
def setup
|
82
|
-
@machine =
|
307
|
+
@machine = StateMachine::Machine.new(Class.new, 'state')
|
83
308
|
@machine.event(:turn_on) {}
|
309
|
+
@machine.before_transition(lambda {})
|
310
|
+
@machine.after_transition(lambda {})
|
311
|
+
@machine.states # Caches the states variable
|
84
312
|
|
85
313
|
@copied_machine = @machine.dup
|
86
314
|
end
|
@@ -104,65 +332,238 @@ class MachineAfterBeingCopiedTest < Test::Unit::TestCase
|
|
104
332
|
def test_should_not_update_machine_for_original_event
|
105
333
|
assert_equal @machine, @machine.events['turn_on'].machine
|
106
334
|
end
|
335
|
+
|
336
|
+
def test_should_not_have_the_same_callbacks
|
337
|
+
assert_not_same @copied_machine.callbacks, @machine.callbacks
|
338
|
+
end
|
339
|
+
|
340
|
+
def test_should_not_have_the_same_before_callbacks
|
341
|
+
assert_not_same @copied_machine.callbacks[:before], @machine.callbacks[:before]
|
342
|
+
end
|
343
|
+
|
344
|
+
def test_should_not_have_the_same_after_callbacks
|
345
|
+
assert_not_same @copied_machine.callbacks[:after], @machine.callbacks[:after]
|
346
|
+
end
|
107
347
|
end
|
108
348
|
|
109
349
|
class MachineAfterChangingContextTest < Test::Unit::TestCase
|
110
350
|
def setup
|
111
|
-
@
|
351
|
+
@original_class = Class.new
|
352
|
+
@machine = StateMachine::Machine.new(@original_class, 'state')
|
353
|
+
|
354
|
+
@new_class = Class.new(@original_class)
|
355
|
+
@new_machine = @machine.within_context(@new_class)
|
356
|
+
|
357
|
+
@object = @new_class.new
|
112
358
|
end
|
113
359
|
|
114
360
|
def test_should_create_copy_of_machine
|
115
|
-
|
116
|
-
assert_not_same @machine, new_machine
|
361
|
+
assert_not_same @machine, @new_machine
|
117
362
|
end
|
118
363
|
|
119
|
-
def
|
120
|
-
|
121
|
-
assert_equal ToggleSwitch, new_machine.owner_class
|
364
|
+
def test_should_update_owner_class
|
365
|
+
assert_equal @new_class, @new_machine.owner_class
|
122
366
|
end
|
123
367
|
|
124
|
-
def
|
125
|
-
|
126
|
-
|
368
|
+
def test_should_not_change_original_owner_class
|
369
|
+
assert_equal @original_class, @machine.owner_class
|
370
|
+
end
|
371
|
+
|
372
|
+
def test_should_allow_changing_the_initial_state
|
373
|
+
new_machine = @machine.within_context(@new_class, :initial => 'off')
|
374
|
+
assert_equal 'off', new_machine.initial_state(@object)
|
375
|
+
end
|
376
|
+
|
377
|
+
def test_should_not_change_original_initial_state_if_updated
|
378
|
+
new_machine = @machine.within_context(@new_class, :initial => 'off')
|
379
|
+
assert_nil @machine.initial_state(@object)
|
127
380
|
end
|
128
381
|
|
129
382
|
def test_should_not_update_initial_state_if_not_provided
|
130
|
-
|
131
|
-
|
383
|
+
assert_nil @new_machine.initial_state(@object)
|
384
|
+
end
|
385
|
+
|
386
|
+
def test_should_allow_changing_the_integration
|
387
|
+
StateMachine::Integrations.const_set('Custom', Module.new)
|
388
|
+
new_machine = @machine.within_context(@new_class, :integration => :custom)
|
389
|
+
assert (class << new_machine; ancestors; end).include?(StateMachine::Integrations::Custom)
|
390
|
+
end
|
391
|
+
|
392
|
+
def test_should_not_change_original_integration_if_updated
|
393
|
+
StateMachine::Integrations.const_set('Custom', Module.new)
|
394
|
+
new_machine = @machine.within_context(@new_class, :integration => :custom)
|
395
|
+
assert !(class << @machine; ancestors; end).include?(StateMachine::Integrations::Custom)
|
396
|
+
end
|
397
|
+
|
398
|
+
def test_should_change_the_associated_machine_in_the_new_class
|
399
|
+
assert_equal @new_machine, @new_class.state_machines['state']
|
400
|
+
end
|
401
|
+
|
402
|
+
def test_should_not_change_the_associated_machine_in_the_original_class
|
403
|
+
assert_equal @machine, @original_class.state_machines['state']
|
132
404
|
end
|
133
405
|
|
134
406
|
def test_raise_exception_if_invalid_option_specified
|
135
|
-
assert_raise(ArgumentError) {@machine.within_context(
|
407
|
+
assert_raise(ArgumentError) {@machine.within_context(@new_class, :invalid => true)}
|
408
|
+
end
|
409
|
+
|
410
|
+
def teardown
|
411
|
+
StateMachine::Integrations.send(:remove_const, 'Custom') if StateMachine::Integrations.const_defined?('Custom')
|
136
412
|
end
|
137
413
|
end
|
138
414
|
|
139
|
-
class
|
140
|
-
|
141
|
-
|
142
|
-
:
|
415
|
+
class MachineWithConflictingAttributeAccessorsTest < Test::Unit::TestCase
|
416
|
+
def setup
|
417
|
+
@klass = Class.new do
|
418
|
+
attr_accessor :status
|
419
|
+
|
420
|
+
def state
|
421
|
+
status
|
422
|
+
end
|
423
|
+
|
424
|
+
def state=(value)
|
425
|
+
self.status = value
|
426
|
+
end
|
427
|
+
|
428
|
+
def state?
|
429
|
+
true
|
430
|
+
end
|
143
431
|
end
|
144
|
-
|
145
|
-
|
146
|
-
|
432
|
+
@machine = StateMachine::Machine.new(@klass)
|
433
|
+
@object = @klass.new
|
434
|
+
end
|
435
|
+
|
436
|
+
def test_should_not_define_attribute_reader
|
437
|
+
@object.status = 'on'
|
438
|
+
assert_equal 'on', @object.state
|
439
|
+
end
|
440
|
+
|
441
|
+
def test_should_not_define_attribute_writer
|
442
|
+
@object.state = 'on'
|
443
|
+
assert_equal 'on', @object.status
|
444
|
+
end
|
445
|
+
|
446
|
+
def test_should_not_define_attribute_predicate
|
447
|
+
assert @object.state?
|
448
|
+
end
|
449
|
+
end
|
450
|
+
|
451
|
+
class MachineWithConflictingPrivateAttributeAccessorsTest < Test::Unit::TestCase
|
452
|
+
def setup
|
453
|
+
@klass = Class.new do
|
454
|
+
attr_accessor :status
|
455
|
+
|
456
|
+
private
|
457
|
+
def state
|
458
|
+
status
|
459
|
+
end
|
460
|
+
|
461
|
+
def state=(value)
|
462
|
+
self.status = value
|
463
|
+
end
|
464
|
+
|
465
|
+
def state?
|
466
|
+
true
|
467
|
+
end
|
147
468
|
end
|
469
|
+
@machine = StateMachine::Machine.new(@klass)
|
470
|
+
@object = @klass.new
|
471
|
+
end
|
472
|
+
|
473
|
+
def test_should_not_define_attribute_reader
|
474
|
+
@object.status = 'on'
|
475
|
+
assert_equal 'on', @object.send(:state)
|
476
|
+
end
|
477
|
+
|
478
|
+
def test_should_not_define_attribute_writer
|
479
|
+
@object.send(:state=, 'on')
|
480
|
+
assert_equal 'on', @object.status
|
148
481
|
end
|
149
482
|
|
483
|
+
def test_should_not_define_attribute_predicate
|
484
|
+
assert @object.send(:state?)
|
485
|
+
end
|
486
|
+
end
|
487
|
+
|
488
|
+
class MachineWithConflictingStatePredicatesTest < Test::Unit::TestCase
|
150
489
|
def setup
|
151
|
-
@
|
490
|
+
@klass = Class.new do
|
491
|
+
def on?
|
492
|
+
true
|
493
|
+
end
|
494
|
+
|
495
|
+
def off?
|
496
|
+
true
|
497
|
+
end
|
498
|
+
end
|
499
|
+
@machine = StateMachine::Machine.new(@klass)
|
500
|
+
@machine.before_transition :to => 'on', :from => 'off', :do => lambda {}
|
501
|
+
@object = @klass.new
|
502
|
+
end
|
503
|
+
|
504
|
+
def test_should_not_define_state_predicates
|
505
|
+
assert @object.on?
|
506
|
+
assert @object.off?
|
507
|
+
end
|
508
|
+
end
|
509
|
+
|
510
|
+
class MachineWithConflictingScopesTest < Test::Unit::TestCase
|
511
|
+
def setup
|
512
|
+
@klass = Class.new do
|
513
|
+
def self.with_state
|
514
|
+
:with_state
|
515
|
+
end
|
516
|
+
|
517
|
+
def self.with_states
|
518
|
+
:with_states
|
519
|
+
end
|
520
|
+
|
521
|
+
def self.without_state
|
522
|
+
:without_state
|
523
|
+
end
|
524
|
+
|
525
|
+
def self.without_states
|
526
|
+
:without_states
|
527
|
+
end
|
528
|
+
end
|
529
|
+
|
530
|
+
integration = Module.new do
|
531
|
+
def define_with_scope(name)
|
532
|
+
raise ArgumentError, 'should not define a with scope'
|
533
|
+
end
|
534
|
+
|
535
|
+
def define_without_scope(name)
|
536
|
+
raise ArgumentError, 'should not define a without scope'
|
537
|
+
end
|
538
|
+
end
|
539
|
+
StateMachine::Integrations.const_set('Custom', integration)
|
540
|
+
@machine = StateMachine::Machine.new(@klass, :integration => :custom)
|
152
541
|
end
|
153
542
|
|
154
|
-
def
|
155
|
-
assert_equal :
|
543
|
+
def test_should_not_define_singular_with_scope
|
544
|
+
assert_equal :with_state, @klass.with_state
|
156
545
|
end
|
157
546
|
|
158
|
-
def
|
159
|
-
assert_equal :
|
547
|
+
def test_should_not_define_plural_with_scope
|
548
|
+
assert_equal :with_states, @klass.with_states
|
549
|
+
end
|
550
|
+
|
551
|
+
def test_should_not_define_singular_without_scope
|
552
|
+
assert_equal :without_state, @klass.without_state
|
553
|
+
end
|
554
|
+
|
555
|
+
def test_should_not_define_plural_without_scope
|
556
|
+
assert_equal :without_states, @klass.without_states
|
557
|
+
end
|
558
|
+
|
559
|
+
def teardown
|
560
|
+
StateMachine::Integrations.send(:remove_const, 'Custom')
|
160
561
|
end
|
161
562
|
end
|
162
563
|
|
163
564
|
class MachineWithEventsTest < Test::Unit::TestCase
|
164
565
|
def setup
|
165
|
-
@machine =
|
566
|
+
@machine = StateMachine::Machine.new(Class.new)
|
166
567
|
end
|
167
568
|
|
168
569
|
def test_should_create_event_with_given_name
|
@@ -185,9 +586,230 @@ class MachineWithEventsTest < Test::Unit::TestCase
|
|
185
586
|
end
|
186
587
|
end
|
187
588
|
|
589
|
+
class MachineWithConflictingPredefinedInitializeTest < Test::Unit::TestCase
|
590
|
+
def setup
|
591
|
+
@klass = Class.new do
|
592
|
+
attr_reader :initialized
|
593
|
+
attr_reader :block_given
|
594
|
+
|
595
|
+
def initialize
|
596
|
+
@initialized = true
|
597
|
+
@block_given = block_given?
|
598
|
+
end
|
599
|
+
end
|
600
|
+
|
601
|
+
@machine = StateMachine::Machine.new(@klass, :initial => 'off')
|
602
|
+
@object = @klass.new {}
|
603
|
+
end
|
604
|
+
|
605
|
+
def test_should_not_override_existing_method
|
606
|
+
assert @object.initialized
|
607
|
+
end
|
608
|
+
|
609
|
+
def test_should_still_initialize_state
|
610
|
+
assert_equal 'off', @object.state
|
611
|
+
end
|
612
|
+
|
613
|
+
def test_should_preserve_block
|
614
|
+
assert @object.block_given
|
615
|
+
end
|
616
|
+
|
617
|
+
def test_should_not_include_initialize_in_instance_methods
|
618
|
+
assert !@klass.instance_methods(false).include?('initialize')
|
619
|
+
end
|
620
|
+
end
|
621
|
+
|
622
|
+
class MachineWithConflictingPostdefinedInitializeTest < Test::Unit::TestCase
|
623
|
+
def setup
|
624
|
+
@klass = Class.new
|
625
|
+
@machine = StateMachine::Machine.new(@klass, :initial => 'off')
|
626
|
+
@klass.class_eval do
|
627
|
+
attr_reader :initialized
|
628
|
+
attr_reader :block_given
|
629
|
+
|
630
|
+
def initialize
|
631
|
+
@initialized = true
|
632
|
+
@block_given = block_given?
|
633
|
+
end
|
634
|
+
end
|
635
|
+
|
636
|
+
@object = @klass.new {}
|
637
|
+
end
|
638
|
+
|
639
|
+
def test_should_not_override_existing_method
|
640
|
+
assert @object.initialized
|
641
|
+
end
|
642
|
+
|
643
|
+
def test_should_still_initialize_state
|
644
|
+
assert_equal 'off', @object.state
|
645
|
+
end
|
646
|
+
|
647
|
+
def test_should_preserve_block
|
648
|
+
assert @object.block_given
|
649
|
+
end
|
650
|
+
|
651
|
+
def test_should_not_include_initialize_in_instance_methods
|
652
|
+
assert !@klass.instance_methods(false).include?('initialize')
|
653
|
+
end
|
654
|
+
end
|
655
|
+
|
656
|
+
class MachineWithConflictingSuperclassInitializeTest < Test::Unit::TestCase
|
657
|
+
def setup
|
658
|
+
@superclass = Class.new do
|
659
|
+
attr_reader :initialized
|
660
|
+
attr_reader :block_given
|
661
|
+
|
662
|
+
def initialize
|
663
|
+
@initialized = true
|
664
|
+
@block_given = block_given?
|
665
|
+
end
|
666
|
+
end
|
667
|
+
@klass = Class.new(@superclass)
|
668
|
+
@machine = StateMachine::Machine.new(@klass, :initial => 'off')
|
669
|
+
@object = @klass.new {}
|
670
|
+
end
|
671
|
+
|
672
|
+
def test_should_not_override_existing_method
|
673
|
+
assert @object.initialized
|
674
|
+
end
|
675
|
+
|
676
|
+
def test_should_still_initialize_state
|
677
|
+
assert_equal 'off', @object.state
|
678
|
+
end
|
679
|
+
|
680
|
+
def test_should_preserve_block
|
681
|
+
assert @object.block_given
|
682
|
+
end
|
683
|
+
|
684
|
+
def test_should_not_include_initialize_in_instance_methods
|
685
|
+
assert !@klass.instance_methods(false).include?('initialize')
|
686
|
+
end
|
687
|
+
end
|
688
|
+
|
689
|
+
class MachineWithConflictingPredefinedAndSuperclassInitializeTest < Test::Unit::TestCase
|
690
|
+
def setup
|
691
|
+
@superclass = Class.new do
|
692
|
+
attr_reader :base_initialized
|
693
|
+
|
694
|
+
def initialize
|
695
|
+
@base_initialized = true
|
696
|
+
end
|
697
|
+
end
|
698
|
+
@klass = Class.new(@superclass) do
|
699
|
+
attr_reader :initialized
|
700
|
+
|
701
|
+
def initialize
|
702
|
+
super
|
703
|
+
@initialized = true
|
704
|
+
end
|
705
|
+
end
|
706
|
+
|
707
|
+
@machine = StateMachine::Machine.new(@klass, :initial => 'off')
|
708
|
+
@object = @klass.new
|
709
|
+
end
|
710
|
+
|
711
|
+
def test_should_not_override_base_method
|
712
|
+
assert @object.base_initialized
|
713
|
+
end
|
714
|
+
|
715
|
+
def test_should_not_override_existing_method
|
716
|
+
assert @object.initialized
|
717
|
+
end
|
718
|
+
|
719
|
+
def test_should_still_initialize_state
|
720
|
+
assert_equal 'off', @object.state
|
721
|
+
end
|
722
|
+
|
723
|
+
def test_should_not_include_initialize_in_instance_methods
|
724
|
+
assert !@klass.instance_methods(false).include?('initialize')
|
725
|
+
end
|
726
|
+
end
|
727
|
+
|
728
|
+
class MachineWithConflictingPostdefinedAndSuperclassInitializeTest < Test::Unit::TestCase
|
729
|
+
def setup
|
730
|
+
@superclass = Class.new do
|
731
|
+
attr_reader :base_initialized
|
732
|
+
|
733
|
+
def initialize
|
734
|
+
@base_initialized = true
|
735
|
+
end
|
736
|
+
end
|
737
|
+
@klass = Class.new(@superclass)
|
738
|
+
|
739
|
+
@machine = StateMachine::Machine.new(@klass, :initial => 'off')
|
740
|
+
@klass.class_eval do
|
741
|
+
attr_reader :initialized
|
742
|
+
|
743
|
+
def initialize
|
744
|
+
super
|
745
|
+
@initialized = true
|
746
|
+
end
|
747
|
+
end
|
748
|
+
|
749
|
+
@object = @klass.new
|
750
|
+
end
|
751
|
+
|
752
|
+
def test_should_not_override_base_method
|
753
|
+
assert @object.base_initialized
|
754
|
+
end
|
755
|
+
|
756
|
+
def test_should_not_override_existing_method
|
757
|
+
assert @object.initialized
|
758
|
+
end
|
759
|
+
|
760
|
+
def test_should_still_initialize_state
|
761
|
+
assert_equal 'off', @object.state
|
762
|
+
end
|
763
|
+
|
764
|
+
def test_should_not_include_initialize_in_instance_methods
|
765
|
+
assert !@klass.instance_methods(false).include?('initialize')
|
766
|
+
end
|
767
|
+
end
|
768
|
+
|
769
|
+
class MachineWithConflictingMethodAddedTest < Test::Unit::TestCase
|
770
|
+
def setup
|
771
|
+
@klass = Class.new do
|
772
|
+
class << self
|
773
|
+
attr_reader :called_method_added
|
774
|
+
|
775
|
+
def method_added(method)
|
776
|
+
super
|
777
|
+
@called_method_added = true
|
778
|
+
end
|
779
|
+
end
|
780
|
+
end
|
781
|
+
@machine = StateMachine::Machine.new(@klass, :initial => 'off')
|
782
|
+
@object = @klass.new
|
783
|
+
end
|
784
|
+
|
785
|
+
def test_should_not_override_existing_method
|
786
|
+
assert @klass.called_method_added
|
787
|
+
end
|
788
|
+
|
789
|
+
def test_should_still_initialize_state
|
790
|
+
assert_equal 'off', @object.state
|
791
|
+
end
|
792
|
+
end
|
793
|
+
|
794
|
+
class MachineWithExistingAttributeValue < Test::Unit::TestCase
|
795
|
+
def setup
|
796
|
+
@klass = Class.new do
|
797
|
+
def initialize
|
798
|
+
@state = 'on'
|
799
|
+
end
|
800
|
+
end
|
801
|
+
@machine = StateMachine::Machine.new(@klass, :initial => 'off')
|
802
|
+
@object = @klass.new
|
803
|
+
end
|
804
|
+
|
805
|
+
def test_should_not_set_the_initial_state
|
806
|
+
assert_equal 'on', @object.state
|
807
|
+
end
|
808
|
+
end
|
809
|
+
|
188
810
|
class MachineWithExistingEventTest < Test::Unit::TestCase
|
189
811
|
def setup
|
190
|
-
@machine =
|
812
|
+
@machine = StateMachine::Machine.new(Class.new)
|
191
813
|
@event = @machine.event(:turn_on) {}
|
192
814
|
@same_event = @machine.event(:turn_on) {}
|
193
815
|
end
|
@@ -197,9 +819,10 @@ class MachineWithExistingEventTest < Test::Unit::TestCase
|
|
197
819
|
end
|
198
820
|
end
|
199
821
|
|
200
|
-
class
|
822
|
+
class MachineWithEventsWithTransitionsTest < Test::Unit::TestCase
|
201
823
|
def setup
|
202
|
-
@
|
824
|
+
@klass = Class.new
|
825
|
+
@machine = StateMachine::Machine.new(@klass)
|
203
826
|
@machine.event(:turn_on) do
|
204
827
|
transition :to => 'on', :from => 'off'
|
205
828
|
transition :to => 'error', :from => 'unknown'
|
@@ -211,7 +834,7 @@ class MachineWithEventsAndTransitionsTest < Test::Unit::TestCase
|
|
211
834
|
end
|
212
835
|
|
213
836
|
def test_should_track_states_defined_in_event_transitions
|
214
|
-
assert_equal %w(error off on unknown), @machine.states
|
837
|
+
assert_equal %w(error off on unknown), @machine.states.sort
|
215
838
|
end
|
216
839
|
|
217
840
|
def test_should_not_duplicate_states_defined_in_multiple_event_transitions
|
@@ -219,18 +842,71 @@ class MachineWithEventsAndTransitionsTest < Test::Unit::TestCase
|
|
219
842
|
transition :to => 'off', :from => 'on'
|
220
843
|
end
|
221
844
|
|
222
|
-
assert_equal %w(error off on unknown), @machine.states
|
845
|
+
assert_equal %w(error off on unknown), @machine.states.sort
|
846
|
+
end
|
847
|
+
|
848
|
+
def test_should_track_state_from_new_events
|
849
|
+
@machine.states
|
850
|
+
@machine.event :turn_off do
|
851
|
+
transition :to => 'maybe'
|
852
|
+
end
|
853
|
+
|
854
|
+
assert_equal %w(error maybe off on unknown), @machine.states.sort
|
855
|
+
end
|
856
|
+
|
857
|
+
def test_should_define_predicates_for_each_state
|
858
|
+
object = @klass.new
|
859
|
+
|
860
|
+
[:on?, :off?, :error?, :unknown?].each {|predicate| assert object.respond_to?(predicate)}
|
861
|
+
end
|
862
|
+
end
|
863
|
+
|
864
|
+
class MachineWithSymbolStatesTest < Test::Unit::TestCase
|
865
|
+
def setup
|
866
|
+
@klass = Class.new
|
867
|
+
@machine = StateMachine::Machine.new(@klass)
|
868
|
+
@machine.event(:turn_on) do
|
869
|
+
transition :to => :on, :from => :off
|
870
|
+
end
|
871
|
+
end
|
872
|
+
|
873
|
+
def test_should_define_predicates_for_each_state
|
874
|
+
object = @klass.new
|
875
|
+
|
876
|
+
[:on?, :off?].each {|predicate| assert object.respond_to?(predicate)}
|
877
|
+
end
|
878
|
+
end
|
879
|
+
|
880
|
+
class MachineWithNumericStatesTest < Test::Unit::TestCase
|
881
|
+
def setup
|
882
|
+
@klass = Class.new
|
883
|
+
@machine = StateMachine::Machine.new(@klass)
|
884
|
+
@machine.event(:turn_on) do
|
885
|
+
transition :to => 1, :from => 2
|
886
|
+
end
|
887
|
+
end
|
888
|
+
|
889
|
+
def test_should_not_define_predicates_for_each_state
|
890
|
+
object = @klass.new
|
891
|
+
|
892
|
+
['1?', '2?'].each {|predicate| assert !object.respond_to?(predicate)}
|
223
893
|
end
|
224
894
|
end
|
225
895
|
|
226
896
|
class MachineWithTransitionCallbacksTest < Test::Unit::TestCase
|
227
897
|
def setup
|
228
|
-
@
|
898
|
+
@klass = Class.new do
|
899
|
+
attr_accessor :callbacks
|
900
|
+
end
|
901
|
+
|
902
|
+
@machine = StateMachine::Machine.new(@klass)
|
229
903
|
@event = @machine.event :turn_on do
|
230
904
|
transition :to => 'on', :from => 'off'
|
231
905
|
end
|
232
906
|
|
233
|
-
@
|
907
|
+
@object = @klass.new
|
908
|
+
@object.state = 'off'
|
909
|
+
@object.callbacks = []
|
234
910
|
end
|
235
911
|
|
236
912
|
def test_should_raise_exception_if_invalid_option_specified
|
@@ -242,65 +918,347 @@ class MachineWithTransitionCallbacksTest < Test::Unit::TestCase
|
|
242
918
|
end
|
243
919
|
|
244
920
|
def test_should_invoke_callbacks_during_transition
|
245
|
-
@machine.before_transition lambda {|
|
246
|
-
@machine.after_transition lambda {|
|
921
|
+
@machine.before_transition lambda {|object| object.callbacks << 'before'}
|
922
|
+
@machine.after_transition lambda {|object| object.callbacks << 'after'}
|
923
|
+
|
924
|
+
@event.fire(@object)
|
925
|
+
assert_equal %w(before after), @object.callbacks
|
926
|
+
end
|
927
|
+
|
928
|
+
def test_should_support_from_requirement
|
929
|
+
@machine.before_transition :from => 'off', :do => lambda {|object| object.callbacks << 'off'}
|
930
|
+
@machine.before_transition :from => 'on', :do => lambda {|object| object.callbacks << 'on'}
|
931
|
+
|
932
|
+
@event.fire(@object)
|
933
|
+
assert_equal %w(off), @object.callbacks
|
934
|
+
end
|
935
|
+
|
936
|
+
def test_should_support_except_from_requirement
|
937
|
+
@machine.before_transition :except_from => 'off', :do => lambda {|object| object.callbacks << 'off'}
|
938
|
+
@machine.before_transition :except_from => 'on', :do => lambda {|object| object.callbacks << 'on'}
|
939
|
+
|
940
|
+
@event.fire(@object)
|
941
|
+
assert_equal %w(on), @object.callbacks
|
942
|
+
end
|
943
|
+
|
944
|
+
def test_should_support_to_requirement
|
945
|
+
@machine.before_transition :to => 'off', :do => lambda {|object| object.callbacks << 'off'}
|
946
|
+
@machine.before_transition :to => 'on', :do => lambda {|object| object.callbacks << 'on'}
|
947
|
+
|
948
|
+
@event.fire(@object)
|
949
|
+
assert_equal %w(on), @object.callbacks
|
950
|
+
end
|
951
|
+
|
952
|
+
def test_should_support_except_to_requirement
|
953
|
+
@machine.before_transition :except_to => 'off', :do => lambda {|object| object.callbacks << 'off'}
|
954
|
+
@machine.before_transition :except_to => 'on', :do => lambda {|object| object.callbacks << 'on'}
|
247
955
|
|
248
|
-
@event.fire(@
|
249
|
-
assert_equal %w(
|
956
|
+
@event.fire(@object)
|
957
|
+
assert_equal %w(off), @object.callbacks
|
250
958
|
end
|
251
959
|
|
252
|
-
def
|
253
|
-
@machine.before_transition :
|
254
|
-
@machine.before_transition :
|
960
|
+
def test_should_support_on_requirement
|
961
|
+
@machine.before_transition :on => 'turn_off', :do => lambda {|object| object.callbacks << 'turn_off'}
|
962
|
+
@machine.before_transition :on => 'turn_on', :do => lambda {|object| object.callbacks << 'turn_on'}
|
255
963
|
|
256
|
-
@event.fire(@
|
257
|
-
assert_equal %w(
|
964
|
+
@event.fire(@object)
|
965
|
+
assert_equal %w(turn_on), @object.callbacks
|
258
966
|
end
|
259
967
|
|
260
|
-
def
|
261
|
-
@machine.before_transition :
|
262
|
-
@machine.before_transition :
|
968
|
+
def test_should_support_except_on_requirement
|
969
|
+
@machine.before_transition :except_on => 'turn_off', :do => lambda {|object| object.callbacks << 'turn_off'}
|
970
|
+
@machine.before_transition :except_on => 'turn_on', :do => lambda {|object| object.callbacks << 'turn_on'}
|
263
971
|
|
264
|
-
@event.fire(@
|
265
|
-
assert_equal %w(
|
972
|
+
@event.fire(@object)
|
973
|
+
assert_equal %w(turn_off), @object.callbacks
|
266
974
|
end
|
267
975
|
|
268
|
-
def
|
269
|
-
@machine.before_transition :
|
270
|
-
@machine.
|
976
|
+
def test_should_track_states_defined_in_transition_callbacks
|
977
|
+
@machine.before_transition :from => 'off', :to => 'on', :do => lambda {}
|
978
|
+
@machine.after_transition :from => 'unknown', :to => 'error', :do => lambda {}
|
271
979
|
|
272
|
-
@
|
273
|
-
assert_equal %w(on), @switch.callbacks
|
980
|
+
assert_equal %w(error off on unknown), @machine.states.sort
|
274
981
|
end
|
275
982
|
|
276
|
-
def
|
277
|
-
@machine.before_transition :
|
278
|
-
@machine.
|
983
|
+
def test_should_not_duplicate_states_defined_in_multiple_event_transitions
|
984
|
+
@machine.before_transition :from => 'off', :to => 'on', :do => lambda {}
|
985
|
+
@machine.after_transition :from => 'unknown', :to => 'error', :do => lambda {}
|
986
|
+
@machine.after_transition :from => 'off', :to => 'on', :do => lambda {}
|
279
987
|
|
280
|
-
@
|
281
|
-
|
988
|
+
assert_equal %w(error off on unknown), @machine.states.sort
|
989
|
+
end
|
990
|
+
|
991
|
+
def test_should_define_predicates_for_each_state
|
992
|
+
[:on?, :off?].each {|predicate| assert @object.respond_to?(predicate)}
|
993
|
+
end
|
994
|
+
end
|
995
|
+
|
996
|
+
class MachineWithOtherStates < Test::Unit::TestCase
|
997
|
+
def setup
|
998
|
+
@klass = Class.new
|
999
|
+
@machine = StateMachine::Machine.new(@klass, :initial => 'on')
|
1000
|
+
@machine.other_states('on', 'off')
|
282
1001
|
end
|
283
1002
|
|
284
|
-
def
|
285
|
-
|
286
|
-
|
1003
|
+
def test_should_include_other_states_in_known_states
|
1004
|
+
assert_equal %w(off on), @machine.states.sort
|
1005
|
+
end
|
1006
|
+
|
1007
|
+
def test_should_define_predicates_for_each_state
|
1008
|
+
object = @klass.new
|
287
1009
|
|
288
|
-
|
289
|
-
|
1010
|
+
[:on?, :off?].each {|predicate| assert object.respond_to?(predicate)}
|
1011
|
+
end
|
1012
|
+
end
|
1013
|
+
|
1014
|
+
class MachineWithOwnerSubclassTest < Test::Unit::TestCase
|
1015
|
+
def setup
|
1016
|
+
@klass = Class.new
|
1017
|
+
@machine = StateMachine::Machine.new(@klass)
|
1018
|
+
@subclass = Class.new(@klass)
|
1019
|
+
end
|
1020
|
+
|
1021
|
+
def test_should_have_a_different_collection_of_state_machines
|
1022
|
+
assert_not_same @klass.state_machines, @subclass.state_machines
|
1023
|
+
end
|
1024
|
+
|
1025
|
+
def test_should_have_the_same_attribute_associated_state_machines
|
1026
|
+
assert_equal @klass.state_machines, @subclass.state_machines
|
1027
|
+
end
|
1028
|
+
end
|
1029
|
+
|
1030
|
+
class MachineWithExistingMachinesOnOwnerClassTest < Test::Unit::TestCase
|
1031
|
+
def setup
|
1032
|
+
@klass = Class.new
|
1033
|
+
@machine = StateMachine::Machine.new(@klass, :initial => 'off')
|
1034
|
+
@second_machine = StateMachine::Machine.new(@klass, 'status', :initial => 'active')
|
1035
|
+
@object = @klass.new
|
1036
|
+
end
|
1037
|
+
|
1038
|
+
def test_should_track_each_state_machine
|
1039
|
+
expected = {'state' => @machine, 'status' => @second_machine}
|
1040
|
+
assert_equal expected, @klass.state_machines
|
1041
|
+
end
|
1042
|
+
|
1043
|
+
def test_should_initialize_state_for_both_machines
|
1044
|
+
assert_equal 'off', @object.state
|
1045
|
+
assert_equal 'active', @object.status
|
1046
|
+
end
|
1047
|
+
end
|
1048
|
+
|
1049
|
+
class MachineFinderWithoutExistingMachineTest < Test::Unit::TestCase
|
1050
|
+
def setup
|
1051
|
+
@klass = Class.new
|
1052
|
+
@machine = StateMachine::Machine.find_or_create(@klass)
|
1053
|
+
end
|
1054
|
+
|
1055
|
+
def test_should_create_a_new_machine
|
1056
|
+
assert_not_nil @machine
|
1057
|
+
end
|
1058
|
+
|
1059
|
+
def test_should_use_default_state
|
1060
|
+
assert_equal 'state', @machine.attribute
|
1061
|
+
end
|
1062
|
+
end
|
1063
|
+
|
1064
|
+
class MachineFinderWithExistingOnSameClassTest < Test::Unit::TestCase
|
1065
|
+
def setup
|
1066
|
+
@klass = Class.new
|
1067
|
+
@existing_machine = StateMachine::Machine.new(@klass)
|
1068
|
+
@machine = StateMachine::Machine.find_or_create(@klass)
|
290
1069
|
end
|
291
1070
|
|
292
|
-
def
|
293
|
-
@machine
|
294
|
-
|
1071
|
+
def test_should_not_create_a_new_machine
|
1072
|
+
assert_same @machine, @existing_machine
|
1073
|
+
end
|
1074
|
+
end
|
1075
|
+
|
1076
|
+
class MachineFinderWithExistingMachineOnSuperclassTest < Test::Unit::TestCase
|
1077
|
+
def setup
|
1078
|
+
integration = Module.new do
|
1079
|
+
def self.matches?(klass)
|
1080
|
+
false
|
1081
|
+
end
|
1082
|
+
end
|
1083
|
+
StateMachine::Integrations.const_set('Custom', integration)
|
1084
|
+
|
1085
|
+
@base_class = Class.new
|
1086
|
+
@base_machine = StateMachine::Machine.new(@base_class, 'status', :action => :save, :integration => :custom)
|
1087
|
+
@base_machine.event(:turn_on) {}
|
1088
|
+
@base_machine.before_transition(lambda {})
|
1089
|
+
@base_machine.after_transition(lambda {})
|
295
1090
|
|
296
|
-
@
|
297
|
-
|
1091
|
+
@klass = Class.new(@base_class)
|
1092
|
+
@machine = StateMachine::Machine.find_or_create(@klass, 'status')
|
1093
|
+
end
|
1094
|
+
|
1095
|
+
def test_should_create_a_new_machine
|
1096
|
+
assert_not_nil @machine
|
1097
|
+
assert_not_same @machine, @base_machine
|
1098
|
+
end
|
1099
|
+
|
1100
|
+
def test_should_copy_the_base_attribute
|
1101
|
+
assert_equal 'status', @machine.attribute
|
1102
|
+
end
|
1103
|
+
|
1104
|
+
def test_should_copy_the_base_configuration
|
1105
|
+
assert_equal :save, @machine.action
|
1106
|
+
end
|
1107
|
+
|
1108
|
+
def test_should_copy_events
|
1109
|
+
# Can't assert equal arrays since their machines change
|
1110
|
+
assert_equal 1, @machine.events.size
|
1111
|
+
end
|
1112
|
+
|
1113
|
+
def test_should_copy_before_callbacks
|
1114
|
+
assert_equal @base_machine.callbacks[:before], @machine.callbacks[:before]
|
1115
|
+
end
|
1116
|
+
|
1117
|
+
def test_should_copy_after_transitions
|
1118
|
+
assert_equal @base_machine.callbacks[:after], @machine.callbacks[:after]
|
1119
|
+
end
|
1120
|
+
|
1121
|
+
def test_should_use_the_same_integration
|
1122
|
+
assert (class << @machine; ancestors; end).include?(StateMachine::Integrations::Custom)
|
298
1123
|
end
|
299
1124
|
|
300
1125
|
def teardown
|
301
|
-
|
302
|
-
|
303
|
-
|
1126
|
+
StateMachine::Integrations.send(:remove_const, 'Custom')
|
1127
|
+
end
|
1128
|
+
end
|
1129
|
+
|
1130
|
+
class MachineFinderCustomOptionsTest < Test::Unit::TestCase
|
1131
|
+
def setup
|
1132
|
+
@klass = Class.new
|
1133
|
+
@machine = StateMachine::Machine.find_or_create(@klass, 'status', :initial => 'off')
|
1134
|
+
@object = @klass.new
|
1135
|
+
end
|
1136
|
+
|
1137
|
+
def test_should_use_custom_attribute
|
1138
|
+
assert_equal 'status', @machine.attribute
|
1139
|
+
end
|
1140
|
+
|
1141
|
+
def test_should_set_custom_initial_state
|
1142
|
+
assert_equal 'off', @machine.initial_state(@object)
|
1143
|
+
end
|
1144
|
+
end
|
1145
|
+
|
1146
|
+
begin
|
1147
|
+
# Load library
|
1148
|
+
require 'rubygems'
|
1149
|
+
require 'graphviz'
|
1150
|
+
|
1151
|
+
class MachineDrawingTest < Test::Unit::TestCase
|
1152
|
+
def setup
|
1153
|
+
@klass = Class.new do
|
1154
|
+
def self.name; 'Vehicle'; end
|
1155
|
+
end
|
1156
|
+
@machine = StateMachine::Machine.new(@klass)
|
1157
|
+
@machine.event :ignite do
|
1158
|
+
transition :from => 'parked', :to => 'idling'
|
1159
|
+
end
|
1160
|
+
end
|
1161
|
+
|
1162
|
+
def test_should_raise_exception_if_invalid_option_specified
|
1163
|
+
assert_raise(ArgumentError) {@machine.draw(:invalid => true)}
|
1164
|
+
end
|
1165
|
+
|
1166
|
+
def test_should_save_file_with_class_name_by_default
|
1167
|
+
@machine.draw
|
1168
|
+
assert File.exist?('./Vehicle_state.png')
|
1169
|
+
ensure
|
1170
|
+
FileUtils.rm('./Vehicle_state.png')
|
1171
|
+
end
|
1172
|
+
|
1173
|
+
def test_should_allow_base_name_to_be_customized
|
1174
|
+
@machine.draw(:name => 'machine')
|
1175
|
+
assert File.exist?('./machine.png')
|
1176
|
+
ensure
|
1177
|
+
FileUtils.rm('./machine.png')
|
1178
|
+
end
|
1179
|
+
|
1180
|
+
def test_should_allow_format_to_be_customized
|
1181
|
+
@machine.draw(:format => 'jpg')
|
1182
|
+
assert File.exist?('./Vehicle_state.jpg')
|
1183
|
+
ensure
|
1184
|
+
FileUtils.rm('./Vehicle_state.jpg')
|
1185
|
+
end
|
1186
|
+
|
1187
|
+
def test_should_allow_path_to_be_customized
|
1188
|
+
@machine.draw(:path => "#{File.dirname(__FILE__)}/")
|
1189
|
+
assert File.exist?("#{File.dirname(__FILE__)}/Vehicle_state.png")
|
1190
|
+
ensure
|
1191
|
+
FileUtils.rm("#{File.dirname(__FILE__)}/Vehicle_state.png")
|
1192
|
+
end
|
1193
|
+
end
|
1194
|
+
|
1195
|
+
class MachineDrawingWithIntegerStatesTest < Test::Unit::TestCase
|
1196
|
+
def setup
|
1197
|
+
@klass = Class.new do
|
1198
|
+
def self.name; 'Vehicle'; end
|
1199
|
+
end
|
1200
|
+
@machine = StateMachine::Machine.new(@klass, :state_id)
|
1201
|
+
@machine.event :ignite do
|
1202
|
+
transition :from => 2, :to => 1
|
1203
|
+
end
|
1204
|
+
@machine.draw
|
1205
|
+
end
|
1206
|
+
|
1207
|
+
def test_should_draw_machine
|
1208
|
+
assert File.exist?('./Vehicle_state_id.png')
|
1209
|
+
ensure
|
1210
|
+
FileUtils.rm('./Vehicle_state_id.png')
|
1211
|
+
end
|
1212
|
+
end
|
1213
|
+
|
1214
|
+
class MachineDrawingWithTimeStatesTest < Test::Unit::TestCase
|
1215
|
+
def setup
|
1216
|
+
@klass = Class.new do
|
1217
|
+
def self.name; 'Vehicle'; end
|
1218
|
+
end
|
1219
|
+
@machine = StateMachine::Machine.new(@klass, :activated_at)
|
1220
|
+
@machine.event :activate do
|
1221
|
+
transition :from => nil, :to => lambda {Time.now}
|
1222
|
+
end
|
1223
|
+
@machine.draw
|
1224
|
+
end
|
1225
|
+
|
1226
|
+
def test_should_draw_machine
|
1227
|
+
assert File.exist?('./Vehicle_activated_at.png')
|
1228
|
+
ensure
|
1229
|
+
FileUtils.rm('./Vehicle_activated_at.png')
|
1230
|
+
end
|
1231
|
+
end
|
1232
|
+
|
1233
|
+
class MachineClassDrawingTest < Test::Unit::TestCase
|
1234
|
+
def setup
|
1235
|
+
@klass = Class.new do
|
1236
|
+
def self.name; 'Vehicle'; end
|
1237
|
+
end
|
1238
|
+
@machine = StateMachine::Machine.new(@klass)
|
1239
|
+
@machine.event :ignite do
|
1240
|
+
transition :from => 'parked', :to => 'idling'
|
1241
|
+
end
|
1242
|
+
end
|
1243
|
+
|
1244
|
+
def test_should_raise_exception_if_no_class_names_specified
|
1245
|
+
assert_raise(ArgumentError) {StateMachine::Machine.draw(nil)}
|
1246
|
+
end
|
1247
|
+
|
1248
|
+
def test_should_load_files
|
1249
|
+
StateMachine::Machine.draw('Switch', :file => "#{File.dirname(__FILE__)}/../classes/switch.rb")
|
1250
|
+
assert defined?(::Switch)
|
1251
|
+
ensure
|
1252
|
+
FileUtils.rm('./Switch_state.png')
|
1253
|
+
end
|
1254
|
+
|
1255
|
+
def test_should_allow_path_and_format_to_be_customized
|
1256
|
+
StateMachine::Machine.draw('Switch', :file => "#{File.dirname(__FILE__)}/../classes/switch.rb", :path => "#{File.dirname(__FILE__)}/", :format => 'jpg')
|
1257
|
+
assert File.exist?("#{File.dirname(__FILE__)}/Switch_state.jpg")
|
1258
|
+
ensure
|
1259
|
+
FileUtils.rm("#{File.dirname(__FILE__)}/Switch_state.jpg")
|
304
1260
|
end
|
305
1261
|
end
|
1262
|
+
rescue LoadError
|
1263
|
+
$stderr.puts 'Skipping GraphViz tests. `gem install ruby-graphviz` and try again.'
|
306
1264
|
end
|