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
@@ -2,13 +2,18 @@ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
|
|
2
2
|
|
3
3
|
class NodeCollectionByDefaultTest < Test::Unit::TestCase
|
4
4
|
def setup
|
5
|
-
@
|
5
|
+
@machine = StateMachine::Machine.new(Class.new)
|
6
|
+
@collection = StateMachine::NodeCollection.new(@machine)
|
6
7
|
end
|
7
8
|
|
8
9
|
def test_should_not_have_any_nodes
|
9
10
|
assert_equal 0, @collection.length
|
10
11
|
end
|
11
12
|
|
13
|
+
def test_should_have_a_machine
|
14
|
+
assert_equal @machine, @collection.machine
|
15
|
+
end
|
16
|
+
|
12
17
|
def test_should_index_by_name
|
13
18
|
@collection << object = Struct.new(:name).new(:parked)
|
14
19
|
assert_equal object, @collection[:parked]
|
@@ -17,11 +22,12 @@ end
|
|
17
22
|
|
18
23
|
class NodeCollectionTest < Test::Unit::TestCase
|
19
24
|
def setup
|
20
|
-
@
|
25
|
+
@machine = StateMachine::Machine.new(Class.new)
|
26
|
+
@collection = StateMachine::NodeCollection.new(@machine)
|
21
27
|
end
|
22
28
|
|
23
29
|
def test_should_raise_exception_if_invalid_option_specified
|
24
|
-
exception = assert_raise(ArgumentError) { StateMachine::NodeCollection.new(:invalid => true) }
|
30
|
+
exception = assert_raise(ArgumentError) { StateMachine::NodeCollection.new(@machine, :invalid => true) }
|
25
31
|
assert_equal 'Invalid key(s): invalid', exception.message
|
26
32
|
end
|
27
33
|
|
@@ -38,7 +44,8 @@ end
|
|
38
44
|
|
39
45
|
class NodeCollectionAfterBeingCopiedTest < Test::Unit::TestCase
|
40
46
|
def setup
|
41
|
-
|
47
|
+
machine = StateMachine::Machine.new(Class.new)
|
48
|
+
@collection = StateMachine::NodeCollection.new(machine)
|
42
49
|
@collection << @parked = Struct.new(:name).new(:parked)
|
43
50
|
|
44
51
|
@copied_collection = @collection.dup
|
@@ -62,7 +69,8 @@ end
|
|
62
69
|
|
63
70
|
class NodeCollectionWithoutIndicesTest < Test::Unit::TestCase
|
64
71
|
def setup
|
65
|
-
|
72
|
+
machine = StateMachine::Machine.new(Class.new)
|
73
|
+
@collection = StateMachine::NodeCollection.new(machine, :index => {})
|
66
74
|
end
|
67
75
|
|
68
76
|
def test_should_allow_adding_node
|
@@ -90,7 +98,8 @@ end
|
|
90
98
|
|
91
99
|
class NodeCollectionWithIndicesTest < Test::Unit::TestCase
|
92
100
|
def setup
|
93
|
-
|
101
|
+
machine = StateMachine::Machine.new(Class.new)
|
102
|
+
@collection = StateMachine::NodeCollection.new(machine, :index => [:name, :value])
|
94
103
|
|
95
104
|
@object = Struct.new(:name, :value).new(:parked, 1)
|
96
105
|
@collection << @object
|
@@ -116,24 +125,25 @@ class NodeCollectionWithIndicesTest < Test::Unit::TestCase
|
|
116
125
|
|
117
126
|
def test_should_use_first_index_by_default_on_fetch
|
118
127
|
assert_equal @object, @collection.fetch(:parked)
|
119
|
-
exception = assert_raise(
|
128
|
+
exception = assert_raise(IndexError) { @collection.fetch(1) }
|
120
129
|
assert_equal '1 is an invalid name', exception.message
|
121
130
|
end
|
122
131
|
|
123
132
|
def test_should_allow_customizing_index_on_fetch
|
124
133
|
assert_equal @object, @collection.fetch(1, :value)
|
125
|
-
exception = assert_raise(
|
134
|
+
exception = assert_raise(IndexError) { @collection.fetch(:parked, :value) }
|
126
135
|
assert_equal ':parked is an invalid value', exception.message
|
127
136
|
end
|
128
137
|
end
|
129
138
|
|
130
139
|
class NodeCollectionWithNodesTest < Test::Unit::TestCase
|
131
140
|
def setup
|
132
|
-
|
141
|
+
machine = StateMachine::Machine.new(Class.new)
|
142
|
+
@collection = StateMachine::NodeCollection.new(machine)
|
133
143
|
|
134
|
-
@klass = Struct.new(:name)
|
135
|
-
@parked = @klass.new(:parked)
|
136
|
-
@idling = @klass.new(:idling)
|
144
|
+
@klass = Struct.new(:name, :machine)
|
145
|
+
@parked = @klass.new(:parked, machine)
|
146
|
+
@idling = @klass.new(:idling, machine)
|
137
147
|
|
138
148
|
@collection << @parked
|
139
149
|
@collection << @idling
|
@@ -150,11 +160,21 @@ class NodeCollectionWithNodesTest < Test::Unit::TestCase
|
|
150
160
|
assert_equal @parked, @collection.at(0)
|
151
161
|
assert_equal @idling, @collection.at(1)
|
152
162
|
end
|
163
|
+
|
164
|
+
def test_should_deep_copy_machine_changes
|
165
|
+
new_machine = StateMachine::Machine.new(Class.new)
|
166
|
+
@collection.machine = new_machine
|
167
|
+
|
168
|
+
assert_equal new_machine, @collection.machine
|
169
|
+
assert_equal new_machine, @parked.machine
|
170
|
+
assert_equal new_machine, @idling.machine
|
171
|
+
end
|
153
172
|
end
|
154
173
|
|
155
174
|
class NodeCollectionAfterUpdateTest < Test::Unit::TestCase
|
156
175
|
def setup
|
157
|
-
|
176
|
+
machine = StateMachine::Machine.new(Class.new)
|
177
|
+
@collection = StateMachine::NodeCollection.new(machine, :index => [:name, :value])
|
158
178
|
|
159
179
|
@klass = Struct.new(:name, :value)
|
160
180
|
@parked = @klass.new(:parked, 1)
|
@@ -185,20 +205,3 @@ class NodeCollectionAfterUpdateTest < Test::Unit::TestCase
|
|
185
205
|
assert_nil @collection[1, :value]
|
186
206
|
end
|
187
207
|
end
|
188
|
-
|
189
|
-
class NodeCollectionChangingMachineTest < Test::Unit::TestCase
|
190
|
-
def setup
|
191
|
-
@collection = StateMachine::NodeCollection.new
|
192
|
-
|
193
|
-
@klass = Struct.new(:name, :machine)
|
194
|
-
@collection << @parked = @klass.new(:parked)
|
195
|
-
@collection << @idling = @klass.new(:idling)
|
196
|
-
|
197
|
-
@collection.machine = :machine
|
198
|
-
end
|
199
|
-
|
200
|
-
def test_should_update_each_node_machine
|
201
|
-
assert_equal :machine, @parked.machine
|
202
|
-
assert_equal :machine, @idling.machine
|
203
|
-
end
|
204
|
-
end
|
@@ -2,23 +2,130 @@ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
|
|
2
2
|
|
3
3
|
class StateCollectionByDefaultTest < Test::Unit::TestCase
|
4
4
|
def setup
|
5
|
-
@
|
5
|
+
@machine = StateMachine::Machine.new(Class.new)
|
6
|
+
@states = StateMachine::StateCollection.new(@machine)
|
6
7
|
end
|
7
8
|
|
8
9
|
def test_should_not_have_any_nodes
|
9
10
|
assert_equal 0, @states.length
|
10
11
|
end
|
11
12
|
|
13
|
+
def test_should_have_a_machine
|
14
|
+
assert_equal @machine, @states.machine
|
15
|
+
end
|
16
|
+
|
12
17
|
def test_should_be_empty_by_priority
|
13
18
|
assert_equal [], @states.by_priority
|
14
19
|
end
|
15
20
|
end
|
16
21
|
|
22
|
+
class StateCollectionTest < Test::Unit::TestCase
|
23
|
+
def setup
|
24
|
+
@klass = Class.new
|
25
|
+
@machine = StateMachine::Machine.new(@klass)
|
26
|
+
@states = StateMachine::StateCollection.new(@machine)
|
27
|
+
|
28
|
+
@states << @nil = StateMachine::State.new(@machine, nil)
|
29
|
+
@states << @parked = StateMachine::State.new(@machine, :parked)
|
30
|
+
@states << @idling = StateMachine::State.new(@machine, :idling)
|
31
|
+
|
32
|
+
@object = @klass.new
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_should_index_by_name
|
36
|
+
assert_equal @parked, @states[:parked, :name]
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_should_index_by_name_by_default
|
40
|
+
assert_equal @parked, @states[:parked]
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_should_index_by_value
|
44
|
+
assert_equal @parked, @states['parked', :value]
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_should_not_match_if_value_does_not_match
|
48
|
+
assert !@states.matches?(@object, :parked)
|
49
|
+
assert !@states.matches?(@object, :idling)
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_should_match_if_value_matches
|
53
|
+
assert @states.matches?(@object, nil)
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_raise_exception_if_matching_invalid_state
|
57
|
+
assert_raise(IndexError) { @states.matches?(@object, :invalid) }
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_should_find_state_for_object_if_value_is_known
|
61
|
+
@object.state = 'parked'
|
62
|
+
assert_equal @parked, @states.match(@object)
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_should_raise_exception_if_finding_state_for_object_with_unknown_value
|
66
|
+
@object.state = 'invalid'
|
67
|
+
exception = assert_raise(ArgumentError) { @states.match(@object) }
|
68
|
+
assert_equal '"invalid" is not a known state value', exception.message
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class StateCollectionWithCustomStateValuesTest < Test::Unit::TestCase
|
73
|
+
def setup
|
74
|
+
@klass = Class.new
|
75
|
+
@machine = StateMachine::Machine.new(@klass)
|
76
|
+
@states = StateMachine::StateCollection.new(@machine)
|
77
|
+
|
78
|
+
@states << @state = StateMachine::State.new(@machine, :parked, :value => 1)
|
79
|
+
|
80
|
+
@object = @klass.new
|
81
|
+
@object.state = 1
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_should_match_if_value_matches
|
85
|
+
assert @states.matches?(@object, :parked)
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_should_not_match_if_value_does_not_match
|
89
|
+
@object.state = 2
|
90
|
+
assert !@states.matches?(@object, :parked)
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_should_find_state_for_object_if_value_is_known
|
94
|
+
assert_equal @state, @states.match(@object)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class StateCollectionWithStateMatchersTest < Test::Unit::TestCase
|
99
|
+
def setup
|
100
|
+
@klass = Class.new
|
101
|
+
@machine = StateMachine::Machine.new(@klass)
|
102
|
+
@states = StateMachine::StateCollection.new(@machine)
|
103
|
+
|
104
|
+
@states << @state = StateMachine::State.new(@machine, :parked, :if => lambda {|value| !value.nil?})
|
105
|
+
|
106
|
+
@object = @klass.new
|
107
|
+
@object.state = 1
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_should_match_if_value_matches
|
111
|
+
assert @states.matches?(@object, :parked)
|
112
|
+
end
|
113
|
+
|
114
|
+
def test_should_not_match_if_value_does_not_match
|
115
|
+
@object.state = nil
|
116
|
+
assert !@states.matches?(@object, :parked)
|
117
|
+
end
|
118
|
+
|
119
|
+
def test_should_find_state_for_object_if_value_is_known
|
120
|
+
assert_equal @state, @states.match(@object)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
17
124
|
class StateCollectionWithInitialStateTest < Test::Unit::TestCase
|
18
125
|
def setup
|
19
126
|
@machine = StateMachine::Machine.new(Class.new)
|
127
|
+
@states = StateMachine::StateCollection.new(@machine)
|
20
128
|
|
21
|
-
@states = StateMachine::StateCollection.new
|
22
129
|
@states << @parked = StateMachine::State.new(@machine, :parked)
|
23
130
|
@states << @idling = StateMachine::State.new(@machine, :idling)
|
24
131
|
|
@@ -54,8 +161,8 @@ end
|
|
54
161
|
class StateCollectionWithStateBehaviorsTest < Test::Unit::TestCase
|
55
162
|
def setup
|
56
163
|
@machine = StateMachine::Machine.new(Class.new)
|
164
|
+
@states = StateMachine::StateCollection.new(@machine)
|
57
165
|
|
58
|
-
@states = StateMachine::StateCollection.new
|
59
166
|
@states << @parked = StateMachine::State.new(@machine, :parked)
|
60
167
|
@states << @idling = StateMachine::State.new(@machine, :idling)
|
61
168
|
|
@@ -91,8 +198,8 @@ end
|
|
91
198
|
class StateCollectionWithEventTransitionsTest < Test::Unit::TestCase
|
92
199
|
def setup
|
93
200
|
@machine = StateMachine::Machine.new(Class.new)
|
201
|
+
@states = StateMachine::StateCollection.new(@machine)
|
94
202
|
|
95
|
-
@states = StateMachine::StateCollection.new
|
96
203
|
@states << @parked = StateMachine::State.new(@machine, :parked)
|
97
204
|
@states << @idling = StateMachine::State.new(@machine, :idling)
|
98
205
|
|
@@ -128,8 +235,8 @@ end
|
|
128
235
|
class StateCollectionWithTransitionCallbacksTest < Test::Unit::TestCase
|
129
236
|
def setup
|
130
237
|
@machine = StateMachine::Machine.new(Class.new)
|
238
|
+
@states = StateMachine::StateCollection.new(@machine)
|
131
239
|
|
132
|
-
@states = StateMachine::StateCollection.new
|
133
240
|
@states << @parked = StateMachine::State.new(@machine, :parked)
|
134
241
|
@states << @idling = StateMachine::State.new(@machine, :idling)
|
135
242
|
|
data/test/unit/state_test.rb
CHANGED
@@ -14,6 +14,10 @@ class StateByDefaultTest < Test::Unit::TestCase
|
|
14
14
|
assert_equal :parked, @state.name
|
15
15
|
end
|
16
16
|
|
17
|
+
def test_should_have_a_qualified_name
|
18
|
+
assert_equal :parked, @state.name
|
19
|
+
end
|
20
|
+
|
17
21
|
def test_should_use_stringify_the_name_as_the_value
|
18
22
|
assert_equal 'parked', @state.value
|
19
23
|
end
|
@@ -81,6 +85,10 @@ class StateWithoutNameTest < Test::Unit::TestCase
|
|
81
85
|
assert_nil @state.name
|
82
86
|
end
|
83
87
|
|
88
|
+
def test_should_have_a_nil_qualified_name
|
89
|
+
assert_nil @state.qualified_name
|
90
|
+
end
|
91
|
+
|
84
92
|
def test_should_have_a_nil_value
|
85
93
|
assert_nil @state.value
|
86
94
|
end
|
@@ -107,6 +115,10 @@ class StateWithNameTest < Test::Unit::TestCase
|
|
107
115
|
assert_equal :parked, @state.name
|
108
116
|
end
|
109
117
|
|
118
|
+
def test_should_have_a_qualified_name
|
119
|
+
assert_equal :parked, @state.name
|
120
|
+
end
|
121
|
+
|
110
122
|
def test_should_use_stringify_the_name_as_the_value
|
111
123
|
assert_equal 'parked', @state.value
|
112
124
|
end
|
@@ -362,13 +374,21 @@ end
|
|
362
374
|
class StateWithNamespaceTest < Test::Unit::TestCase
|
363
375
|
def setup
|
364
376
|
@klass = Class.new
|
365
|
-
@machine = StateMachine::Machine.new(@klass, :namespace => '
|
366
|
-
@state = StateMachine::State.new(@machine, :
|
377
|
+
@machine = StateMachine::Machine.new(@klass, :namespace => 'alarm')
|
378
|
+
@state = StateMachine::State.new(@machine, :active)
|
367
379
|
@object = @klass.new
|
368
380
|
end
|
369
381
|
|
382
|
+
def test_should_have_a_name
|
383
|
+
assert_equal :active, @state.name
|
384
|
+
end
|
385
|
+
|
386
|
+
def test_should_have_a_qualified_name
|
387
|
+
assert_equal :alarm_active, @state.qualified_name
|
388
|
+
end
|
389
|
+
|
370
390
|
def test_should_namespace_predicate
|
371
|
-
assert @object.respond_to?(:
|
391
|
+
assert @object.respond_to?(:alarm_active?)
|
372
392
|
end
|
373
393
|
end
|
374
394
|
|
@@ -5,6 +5,7 @@ class TransitionTest < Test::Unit::TestCase
|
|
5
5
|
@klass = Class.new
|
6
6
|
@machine = StateMachine::Machine.new(@klass)
|
7
7
|
@machine.state :parked, :idling
|
8
|
+
@machine.event :ignite
|
8
9
|
|
9
10
|
@object = @klass.new
|
10
11
|
@object.state = 'parked'
|
@@ -24,6 +25,10 @@ class TransitionTest < Test::Unit::TestCase
|
|
24
25
|
assert_equal :ignite, @transition.event
|
25
26
|
end
|
26
27
|
|
28
|
+
def test_should_have_a_qualified_event
|
29
|
+
assert_equal :ignite, @transition.qualified_event
|
30
|
+
end
|
31
|
+
|
27
32
|
def test_should_have_a_from_value
|
28
33
|
assert_equal 'parked', @transition.from
|
29
34
|
end
|
@@ -32,6 +37,10 @@ class TransitionTest < Test::Unit::TestCase
|
|
32
37
|
assert_equal :parked, @transition.from_name
|
33
38
|
end
|
34
39
|
|
40
|
+
def test_should_have_a_qualified_from_name
|
41
|
+
assert_equal :parked, @transition.qualified_from_name
|
42
|
+
end
|
43
|
+
|
35
44
|
def test_should_have_a_to_value
|
36
45
|
assert_equal 'idling', @transition.to
|
37
46
|
end
|
@@ -40,26 +49,88 @@ class TransitionTest < Test::Unit::TestCase
|
|
40
49
|
assert_equal :idling, @transition.to_name
|
41
50
|
end
|
42
51
|
|
52
|
+
def test_should_have_a_qualified_to_name
|
53
|
+
assert_equal :idling, @transition.qualified_to_name
|
54
|
+
end
|
55
|
+
|
43
56
|
def test_should_have_an_attribute
|
44
57
|
assert_equal :state, @transition.attribute
|
45
58
|
end
|
46
59
|
|
60
|
+
def test_should_not_have_an_action
|
61
|
+
assert_nil @transition.action
|
62
|
+
end
|
63
|
+
|
47
64
|
def test_should_generate_attributes
|
48
65
|
expected = {:object => @object, :attribute => :state, :event => :ignite, :from => 'parked', :to => 'idling'}
|
49
66
|
assert_equal expected, @transition.attributes
|
50
67
|
end
|
51
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
|
+
|
52
77
|
def test_should_use_pretty_inspect
|
53
78
|
assert_equal '#<StateMachine::Transition attribute=:state event=:ignite from="parked" from_name=:parked to="idling" to_name=:idling>', @transition.inspect
|
54
79
|
end
|
55
80
|
end
|
56
81
|
|
82
|
+
class TransitionWithoutEventTest < Test::Unit::TestCase
|
83
|
+
def setup
|
84
|
+
@klass = Class.new
|
85
|
+
@machine = StateMachine::Machine.new(@klass)
|
86
|
+
@machine.state :parked, :idling
|
87
|
+
|
88
|
+
@object = @klass.new
|
89
|
+
@object.state = 'parked'
|
90
|
+
|
91
|
+
@transition = StateMachine::Transition.new(@object, @machine, nil, :parked, :idling)
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_should_not_have_an_event
|
95
|
+
assert_nil @transition.event
|
96
|
+
end
|
97
|
+
|
98
|
+
def test_should_not_have_a_qualified_event
|
99
|
+
assert_nil @transition.qualified_event
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
class TransitionWithInvalidNodesTest < Test::Unit::TestCase
|
104
|
+
def setup
|
105
|
+
@klass = Class.new
|
106
|
+
@machine = StateMachine::Machine.new(@klass)
|
107
|
+
@machine.state :parked, :idling
|
108
|
+
@machine.event :ignite
|
109
|
+
|
110
|
+
@object = @klass.new
|
111
|
+
@object.state = 'parked'
|
112
|
+
end
|
113
|
+
|
114
|
+
def test_should_raise_exception_with_invalid_event
|
115
|
+
assert_raise(IndexError) { StateMachine::Transition.new(@object, @machine, :invalid, :parked, :idling) }
|
116
|
+
end
|
117
|
+
|
118
|
+
def test_should_raise_exception_with_invalid_from_state
|
119
|
+
assert_raise(IndexError) { StateMachine::Transition.new(@object, @machine, :ignite, :invalid, :idling) }
|
120
|
+
end
|
121
|
+
|
122
|
+
def test_should_raise_exception_with_invalid_to_state
|
123
|
+
assert_raise(IndexError) { StateMachine::Transition.new(@object, @machine, :ignite, :parked, :invalid) }
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
57
127
|
class TransitionWithDynamicToValueTest < Test::Unit::TestCase
|
58
128
|
def setup
|
59
129
|
@klass = Class.new
|
60
130
|
@machine = StateMachine::Machine.new(@klass)
|
61
131
|
@machine.state :parked
|
62
132
|
@machine.state :idling, :value => lambda {1}
|
133
|
+
@machine.event :ignite
|
63
134
|
|
64
135
|
@object = @klass.new
|
65
136
|
@object.state = 'parked'
|
@@ -71,72 +142,167 @@ class TransitionWithDynamicToValueTest < Test::Unit::TestCase
|
|
71
142
|
end
|
72
143
|
end
|
73
144
|
|
74
|
-
class
|
145
|
+
class TransitionLoopbackTest < Test::Unit::TestCase
|
75
146
|
def setup
|
76
|
-
@klass = Class.new
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
@save_state = state
|
81
|
-
@saved = true
|
82
|
-
end
|
83
|
-
end
|
147
|
+
@klass = Class.new
|
148
|
+
@machine = StateMachine::Machine.new(@klass)
|
149
|
+
@machine.state :parked
|
150
|
+
@machine.event :park
|
84
151
|
|
85
|
-
@
|
152
|
+
@object = @klass.new
|
153
|
+
@object.state = 'parked'
|
154
|
+
@transition = StateMachine::Transition.new(@object, @machine, :park, :parked, :parked)
|
155
|
+
end
|
156
|
+
|
157
|
+
def test_should_be_loopback
|
158
|
+
assert @transition.loopback?
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
class TransitionWithDifferentStatesTest < Test::Unit::TestCase
|
163
|
+
def setup
|
164
|
+
@klass = Class.new
|
165
|
+
@machine = StateMachine::Machine.new(@klass)
|
86
166
|
@machine.state :parked, :idling
|
167
|
+
@machine.event :ignite
|
87
168
|
|
88
169
|
@object = @klass.new
|
89
170
|
@object.state = 'parked'
|
90
171
|
@transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
|
91
|
-
@result = @transition.perform
|
92
172
|
end
|
93
173
|
|
94
|
-
def
|
95
|
-
|
174
|
+
def test_should_not_be_loopback
|
175
|
+
assert !@transition.loopback?
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
class TransitionWithNamespaceTest < Test::Unit::TestCase
|
180
|
+
def setup
|
181
|
+
@klass = Class.new
|
182
|
+
@machine = StateMachine::Machine.new(@klass, :namespace => 'alarm')
|
183
|
+
@machine.state :off, :active
|
184
|
+
@machine.event :activate
|
185
|
+
|
186
|
+
@object = @klass.new
|
187
|
+
@object.state = 'off'
|
188
|
+
|
189
|
+
@transition = StateMachine::Transition.new(@object, @machine, :activate, :off, :active)
|
96
190
|
end
|
97
191
|
|
98
|
-
def
|
99
|
-
assert_equal
|
192
|
+
def test_should_have_an_event
|
193
|
+
assert_equal :activate, @transition.event
|
100
194
|
end
|
101
195
|
|
102
|
-
def
|
103
|
-
|
196
|
+
def test_should_have_a_qualified_event
|
197
|
+
assert_equal :activate_alarm, @transition.qualified_event
|
104
198
|
end
|
105
199
|
|
106
|
-
def
|
107
|
-
assert_equal
|
200
|
+
def test_should_have_a_from_name
|
201
|
+
assert_equal :off, @transition.from_name
|
202
|
+
end
|
203
|
+
|
204
|
+
def test_should_have_a_qualified_from_name
|
205
|
+
assert_equal :alarm_off, @transition.qualified_from_name
|
206
|
+
end
|
207
|
+
|
208
|
+
def test_should_have_a_to_name
|
209
|
+
assert_equal :active, @transition.to_name
|
210
|
+
end
|
211
|
+
|
212
|
+
def test_should_have_a_qualified_to_name
|
213
|
+
assert_equal :alarm_active, @transition.qualified_to_name
|
108
214
|
end
|
109
215
|
end
|
110
216
|
|
111
|
-
class
|
217
|
+
class TransitionWithActionTest < Test::Unit::TestCase
|
112
218
|
def setup
|
113
219
|
@klass = Class.new do
|
114
|
-
attr_reader :saved
|
115
|
-
|
116
220
|
def save
|
117
|
-
@saved = true
|
118
221
|
end
|
119
222
|
end
|
120
223
|
|
121
224
|
@machine = StateMachine::Machine.new(@klass, :action => :save)
|
122
225
|
@machine.state :parked, :idling
|
226
|
+
@machine.event :ignite
|
123
227
|
|
124
228
|
@object = @klass.new
|
125
229
|
@object.state = 'parked'
|
230
|
+
|
126
231
|
@transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
|
127
|
-
@result = @transition.perform(false)
|
128
232
|
end
|
129
233
|
|
130
|
-
def
|
131
|
-
assert_equal
|
234
|
+
def test_should_have_an_action
|
235
|
+
assert_equal :save, @transition.action
|
132
236
|
end
|
133
237
|
|
134
|
-
def
|
238
|
+
def test_should_not_have_a_result
|
239
|
+
assert_nil @transition.result
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
class TransitionAfterBeingPersistedTest < Test::Unit::TestCase
|
244
|
+
def setup
|
245
|
+
@klass = Class.new
|
246
|
+
@machine = StateMachine::Machine.new(@klass, :action => :save)
|
247
|
+
@machine.state :parked, :idling
|
248
|
+
@machine.event :ignite
|
249
|
+
|
250
|
+
@object = @klass.new
|
251
|
+
@object.state = 'parked'
|
252
|
+
|
253
|
+
@transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
|
254
|
+
@transition.persist
|
255
|
+
end
|
256
|
+
|
257
|
+
def test_should_update_state_value
|
135
258
|
assert_equal 'idling', @object.state
|
136
259
|
end
|
137
260
|
|
138
|
-
def
|
139
|
-
|
261
|
+
def test_should_not_change_from_state
|
262
|
+
assert_equal 'parked', @transition.from
|
263
|
+
end
|
264
|
+
|
265
|
+
def test_should_not_change_to_state
|
266
|
+
assert_equal 'idling', @transition.to
|
267
|
+
end
|
268
|
+
|
269
|
+
def test_should_revert_to_from_state_on_rollback
|
270
|
+
@transition.rollback
|
271
|
+
assert_equal 'parked', @object.state
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
class TransitionAfterBeingRolledBackTest < Test::Unit::TestCase
|
276
|
+
def setup
|
277
|
+
@klass = Class.new
|
278
|
+
@machine = StateMachine::Machine.new(@klass, :action => :save)
|
279
|
+
@machine.state :parked, :idling
|
280
|
+
@machine.event :ignite
|
281
|
+
|
282
|
+
@object = @klass.new
|
283
|
+
@object.state = 'parked'
|
284
|
+
|
285
|
+
@transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
|
286
|
+
@object.state = 'idling'
|
287
|
+
|
288
|
+
@transition.rollback
|
289
|
+
end
|
290
|
+
|
291
|
+
def test_should_update_state_value_to_from_state
|
292
|
+
assert_equal 'parked', @object.state
|
293
|
+
end
|
294
|
+
|
295
|
+
def test_should_not_change_from_state
|
296
|
+
assert_equal 'parked', @transition.from
|
297
|
+
end
|
298
|
+
|
299
|
+
def test_should_not_change_to_state
|
300
|
+
assert_equal 'idling', @transition.to
|
301
|
+
end
|
302
|
+
|
303
|
+
def test_should_still_be_able_to_persist
|
304
|
+
@transition.persist
|
305
|
+
assert_equal 'idling', @object.state
|
140
306
|
end
|
141
307
|
end
|
142
308
|
|
@@ -153,40 +319,26 @@ class TransitionWithCallbacksTest < Test::Unit::TestCase
|
|
153
319
|
|
154
320
|
@machine = StateMachine::Machine.new(@klass)
|
155
321
|
@machine.state :parked, :idling
|
322
|
+
@machine.event :ignite
|
156
323
|
|
157
324
|
@object = @klass.new
|
158
325
|
@object.state = 'parked'
|
159
326
|
@transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
|
160
327
|
end
|
161
328
|
|
162
|
-
def
|
163
|
-
@machine.before_transition(lambda {|object| @
|
164
|
-
@transition.
|
165
|
-
|
166
|
-
assert_equal 'parked', @state
|
167
|
-
end
|
168
|
-
|
169
|
-
def test_should_run_after_callbacks_after_running_the_action
|
170
|
-
@machine.after_transition(lambda {|object| @state = object.state})
|
171
|
-
@transition.perform
|
329
|
+
def test_should_run_before_callbacks_on_before
|
330
|
+
@machine.before_transition(lambda {|object| @run = true})
|
331
|
+
result = @transition.before
|
172
332
|
|
173
|
-
assert_equal
|
333
|
+
assert_equal true, result
|
334
|
+
assert_equal true, @run
|
174
335
|
end
|
175
336
|
|
176
337
|
def test_should_run_before_callbacks_in_the_order_they_were_defined
|
177
338
|
@callbacks = []
|
178
339
|
@machine.before_transition(lambda {@callbacks << 1})
|
179
340
|
@machine.before_transition(lambda {@callbacks << 2})
|
180
|
-
@transition.
|
181
|
-
|
182
|
-
assert_equal [1, 2], @callbacks
|
183
|
-
end
|
184
|
-
|
185
|
-
def test_should_run_after_callbacks_in_the_order_they_were_defined
|
186
|
-
@callbacks = []
|
187
|
-
@machine.after_transition(lambda {@callbacks << 1})
|
188
|
-
@machine.after_transition(lambda {@callbacks << 2})
|
189
|
-
@transition.perform
|
341
|
+
@transition.before
|
190
342
|
|
191
343
|
assert_equal [1, 2], @callbacks
|
192
344
|
end
|
@@ -199,11 +351,58 @@ class TransitionWithCallbacksTest < Test::Unit::TestCase
|
|
199
351
|
@machine.before_transition :from => :parked, :to => :parked, :on => :park, :do => callback
|
200
352
|
@machine.before_transition :from => :parked, :to => :idling, :on => :ignite, :do => callback
|
201
353
|
@machine.before_transition :from => :idling, :to => :idling, :on => :park, :do => callback
|
202
|
-
@transition.
|
354
|
+
@transition.before
|
203
355
|
|
204
356
|
assert_equal 1, @count
|
205
357
|
end
|
206
358
|
|
359
|
+
def test_should_pass_transition_to_before_callbacks
|
360
|
+
@machine.before_transition(lambda {|*args| @args = args})
|
361
|
+
@transition.before
|
362
|
+
|
363
|
+
assert_equal [@object, @transition], @args
|
364
|
+
end
|
365
|
+
|
366
|
+
def test_should_catch_halted_before_callbacks
|
367
|
+
@machine.before_transition(lambda {throw :halt})
|
368
|
+
|
369
|
+
result = nil
|
370
|
+
assert_nothing_thrown { result = @transition.before }
|
371
|
+
assert_equal false, result
|
372
|
+
end
|
373
|
+
|
374
|
+
def test_should_run_before_callbacks_on_perform_before_changing_the_state
|
375
|
+
@machine.before_transition(lambda {|object| @state = object.state})
|
376
|
+
@transition.perform
|
377
|
+
|
378
|
+
assert_equal 'parked', @state
|
379
|
+
end
|
380
|
+
|
381
|
+
def test_should_run_after_callbacks_on_after
|
382
|
+
@machine.after_transition(lambda {|object| @run = true})
|
383
|
+
result = @transition.after(true)
|
384
|
+
|
385
|
+
assert_equal true, result
|
386
|
+
assert_equal true, @run
|
387
|
+
end
|
388
|
+
|
389
|
+
def test_should_set_result_on_after
|
390
|
+
@transition.after
|
391
|
+
assert_nil @transition.result
|
392
|
+
|
393
|
+
@transition.after(1)
|
394
|
+
assert_equal 1, @transition.result
|
395
|
+
end
|
396
|
+
|
397
|
+
def test_should_run_after_callbacks_in_the_order_they_were_defined
|
398
|
+
@callbacks = []
|
399
|
+
@machine.after_transition(lambda {@callbacks << 1})
|
400
|
+
@machine.after_transition(lambda {@callbacks << 2})
|
401
|
+
@transition.after(true)
|
402
|
+
|
403
|
+
assert_equal [1, 2], @callbacks
|
404
|
+
end
|
405
|
+
|
207
406
|
def test_should_only_run_after_callbacks_that_match_transition_context
|
208
407
|
@count = 0
|
209
408
|
callback = lambda {@count += 1}
|
@@ -212,90 +411,229 @@ class TransitionWithCallbacksTest < Test::Unit::TestCase
|
|
212
411
|
@machine.after_transition :from => :parked, :to => :parked, :on => :park, :do => callback
|
213
412
|
@machine.after_transition :from => :parked, :to => :idling, :on => :ignite, :do => callback
|
214
413
|
@machine.after_transition :from => :idling, :to => :idling, :on => :park, :do => callback
|
215
|
-
@transition.
|
414
|
+
@transition.after(true)
|
216
415
|
|
217
416
|
assert_equal 1, @count
|
218
417
|
end
|
219
418
|
|
220
|
-
def
|
221
|
-
@machine.
|
222
|
-
|
419
|
+
def test_should_pass_transition_to_after_callbacks
|
420
|
+
@machine.after_transition(lambda {|*args| @args = args})
|
421
|
+
|
422
|
+
@transition.after(true)
|
423
|
+
assert_equal [@object, @transition], @args
|
424
|
+
assert_equal true, @transition.result
|
223
425
|
|
426
|
+
@transition.after(false)
|
224
427
|
assert_equal [@object, @transition], @args
|
428
|
+
assert_equal false, @transition.result
|
225
429
|
end
|
226
430
|
|
227
|
-
def
|
228
|
-
@machine.after_transition(lambda {
|
229
|
-
|
431
|
+
def test_should_catch_halted_after_callbacks
|
432
|
+
@machine.after_transition(lambda {throw :halt})
|
433
|
+
|
434
|
+
result = nil
|
435
|
+
assert_nothing_thrown { result = @transition.after(true) }
|
436
|
+
assert_equal true, result
|
437
|
+
end
|
438
|
+
|
439
|
+
def test_should_run_after_callbacks_on_perform_after_running_the_action
|
440
|
+
@machine.after_transition(lambda {|object| @state = object.state})
|
441
|
+
@transition.perform(true)
|
230
442
|
|
231
|
-
assert_equal
|
443
|
+
assert_equal 'idling', @state
|
232
444
|
end
|
233
445
|
end
|
234
446
|
|
235
|
-
class
|
447
|
+
class TransitionAfterBeingPerformedTest < Test::Unit::TestCase
|
448
|
+
def setup
|
449
|
+
@klass = Class.new do
|
450
|
+
attr_reader :saved, :save_state
|
451
|
+
|
452
|
+
def save
|
453
|
+
@save_state = state
|
454
|
+
@saved = true
|
455
|
+
end
|
456
|
+
end
|
457
|
+
|
458
|
+
@machine = StateMachine::Machine.new(@klass, :action => :save)
|
459
|
+
@machine.state :parked, :idling
|
460
|
+
@machine.event :ignite
|
461
|
+
|
462
|
+
@object = @klass.new
|
463
|
+
@object.state = 'parked'
|
464
|
+
@transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
|
465
|
+
@result = @transition.perform
|
466
|
+
end
|
467
|
+
|
468
|
+
def test_should_have_empty_args
|
469
|
+
assert_equal [], @transition.args
|
470
|
+
end
|
471
|
+
|
472
|
+
def test_should_have_a_result
|
473
|
+
assert_equal true, @transition.result
|
474
|
+
end
|
475
|
+
|
476
|
+
def test_should_be_successful
|
477
|
+
assert_equal true, @result
|
478
|
+
end
|
479
|
+
|
480
|
+
def test_should_change_the_current_state
|
481
|
+
assert_equal 'idling', @object.state
|
482
|
+
end
|
483
|
+
|
484
|
+
def test_should_run_the_action
|
485
|
+
assert @object.saved
|
486
|
+
end
|
487
|
+
|
488
|
+
def test_should_run_the_action_after_saving_the_state
|
489
|
+
assert_equal 'idling', @object.save_state
|
490
|
+
end
|
491
|
+
end
|
492
|
+
|
493
|
+
class TransitionWithPerformArgumentsTest < Test::Unit::TestCase
|
236
494
|
def setup
|
237
495
|
@klass = Class.new do
|
238
|
-
class << self; attr_accessor :cancelled_transaction; end
|
239
496
|
attr_reader :saved
|
240
497
|
|
241
498
|
def save
|
242
499
|
@saved = true
|
243
500
|
end
|
244
501
|
end
|
245
|
-
@before_count = 0
|
246
|
-
@after_count = 0
|
247
502
|
|
248
503
|
@machine = StateMachine::Machine.new(@klass, :action => :save)
|
249
504
|
@machine.state :parked, :idling
|
250
|
-
|
251
|
-
|
252
|
-
|
505
|
+
@machine.event :ignite
|
506
|
+
|
507
|
+
@object = @klass.new
|
508
|
+
@object.state = 'parked'
|
509
|
+
@transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
|
510
|
+
end
|
511
|
+
|
512
|
+
def test_should_have_arguments
|
513
|
+
@transition.perform(1, 2)
|
514
|
+
|
515
|
+
assert_equal [1, 2], @transition.args
|
516
|
+
assert @object.saved
|
517
|
+
end
|
518
|
+
|
519
|
+
def test_should_not_include_run_action_in_arguments
|
520
|
+
@transition.perform(1, 2, false)
|
521
|
+
|
522
|
+
assert_equal [1, 2], @transition.args
|
523
|
+
assert !@object.saved
|
524
|
+
end
|
525
|
+
end
|
526
|
+
|
527
|
+
class TransitionWithoutRunningActionTest < Test::Unit::TestCase
|
528
|
+
def setup
|
529
|
+
@klass = Class.new do
|
530
|
+
attr_reader :saved
|
531
|
+
|
532
|
+
def save
|
533
|
+
@saved = true
|
253
534
|
end
|
254
535
|
end
|
255
536
|
|
256
|
-
@machine
|
257
|
-
@machine.
|
258
|
-
@machine.
|
537
|
+
@machine = StateMachine::Machine.new(@klass, :action => :save)
|
538
|
+
@machine.state :parked, :idling
|
539
|
+
@machine.event :ignite
|
259
540
|
|
260
541
|
@object = @klass.new
|
261
542
|
@object.state = 'parked'
|
262
543
|
@transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
|
263
|
-
@result = @transition.perform
|
544
|
+
@result = @transition.perform(false)
|
264
545
|
end
|
265
546
|
|
266
|
-
def
|
267
|
-
|
547
|
+
def test_should_have_empty_args
|
548
|
+
assert_equal [], @transition.args
|
268
549
|
end
|
269
550
|
|
270
|
-
def
|
271
|
-
|
551
|
+
def test_should_not_have_a_result
|
552
|
+
assert_nil @transition.result
|
272
553
|
end
|
273
554
|
|
274
|
-
def
|
555
|
+
def test_should_be_successful
|
556
|
+
assert_equal true, @result
|
557
|
+
end
|
558
|
+
|
559
|
+
def test_should_change_the_current_state
|
560
|
+
assert_equal 'idling', @object.state
|
561
|
+
end
|
562
|
+
|
563
|
+
def test_should_not_run_the_action
|
275
564
|
assert !@object.saved
|
276
565
|
end
|
566
|
+
end
|
567
|
+
|
568
|
+
class TransitionWithTransactionsTest < Test::Unit::TestCase
|
569
|
+
def setup
|
570
|
+
@klass = Class.new do
|
571
|
+
class << self
|
572
|
+
attr_accessor :running_transaction
|
573
|
+
end
|
574
|
+
|
575
|
+
attr_accessor :result
|
576
|
+
|
577
|
+
def save
|
578
|
+
@result = self.class.running_transaction
|
579
|
+
true
|
580
|
+
end
|
581
|
+
end
|
582
|
+
|
583
|
+
@machine = StateMachine::Machine.new(@klass, :action => :save)
|
584
|
+
@machine.state :parked, :idling
|
585
|
+
@machine.event :ignite
|
586
|
+
|
587
|
+
@object = @klass.new
|
588
|
+
@object.state = 'parked'
|
589
|
+
@transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
|
590
|
+
|
591
|
+
class << @machine
|
592
|
+
def within_transaction(object)
|
593
|
+
owner_class.running_transaction = object
|
594
|
+
yield
|
595
|
+
owner_class.running_transaction = false
|
596
|
+
end
|
597
|
+
end
|
598
|
+
end
|
277
599
|
|
278
|
-
def
|
279
|
-
|
600
|
+
def test_should_run_blocks_within_transaction_for_object
|
601
|
+
@transition.within_transaction do
|
602
|
+
@result = @klass.running_transaction
|
603
|
+
end
|
604
|
+
|
605
|
+
assert_equal @object, @result
|
280
606
|
end
|
281
607
|
|
282
|
-
def
|
283
|
-
|
608
|
+
def test_should_run_before_callbacks_within_transaction
|
609
|
+
@machine.before_transition(lambda {|object| @result = @klass.running_transaction})
|
610
|
+
@transition.perform
|
611
|
+
|
612
|
+
assert_equal @object, @result
|
284
613
|
end
|
285
614
|
|
286
|
-
def
|
287
|
-
|
615
|
+
def test_should_run_action_within_transaction
|
616
|
+
@transition.perform
|
617
|
+
|
618
|
+
assert_equal @object, @object.result
|
619
|
+
end
|
620
|
+
|
621
|
+
def test_should_run_after_callbacks_within_transaction
|
622
|
+
@machine.after_transition(lambda {|object| @result = @klass.running_transaction})
|
623
|
+
@transition.perform
|
624
|
+
|
625
|
+
assert_equal @object, @result
|
288
626
|
end
|
289
627
|
end
|
290
628
|
|
291
|
-
class
|
629
|
+
class TransitionHaltedDuringBeforeCallbacksTest < Test::Unit::TestCase
|
292
630
|
def setup
|
293
631
|
@klass = Class.new do
|
294
632
|
class << self; attr_accessor :cancelled_transaction; end
|
295
633
|
attr_reader :saved
|
296
634
|
|
297
635
|
def save
|
298
|
-
|
636
|
+
@saved = true
|
299
637
|
end
|
300
638
|
end
|
301
639
|
@before_count = 0
|
@@ -303,12 +641,14 @@ class TransitionHaltedDuringActionTest < Test::Unit::TestCase
|
|
303
641
|
|
304
642
|
@machine = StateMachine::Machine.new(@klass, :action => :save)
|
305
643
|
@machine.state :parked, :idling
|
644
|
+
@machine.event :ignite
|
306
645
|
class << @machine
|
307
646
|
def within_transaction(object)
|
308
647
|
owner_class.cancelled_transaction = yield == false
|
309
648
|
end
|
310
649
|
end
|
311
650
|
|
651
|
+
@machine.before_transition lambda {@before_count += 1; throw :halt}
|
312
652
|
@machine.before_transition lambda {@before_count += 1}
|
313
653
|
@machine.after_transition lambda {@after_count += 1}
|
314
654
|
|
@@ -322,11 +662,15 @@ class TransitionHaltedDuringActionTest < Test::Unit::TestCase
|
|
322
662
|
assert !@result
|
323
663
|
end
|
324
664
|
|
325
|
-
def
|
326
|
-
assert_equal '
|
665
|
+
def test_should_not_change_current_state
|
666
|
+
assert_equal 'parked', @object.state
|
327
667
|
end
|
328
668
|
|
329
|
-
def
|
669
|
+
def test_should_not_run_action
|
670
|
+
assert !@object.saved
|
671
|
+
end
|
672
|
+
|
673
|
+
def test_should_not_run_further_before_callbacks
|
330
674
|
assert_equal 1, @before_count
|
331
675
|
end
|
332
676
|
|
@@ -354,6 +698,7 @@ class TransitionHaltedAfterCallbackTest < Test::Unit::TestCase
|
|
354
698
|
|
355
699
|
@machine = StateMachine::Machine.new(@klass, :action => :save)
|
356
700
|
@machine.state :parked, :idling
|
701
|
+
@machine.event :ignite
|
357
702
|
class << @machine
|
358
703
|
def within_transaction(object)
|
359
704
|
owner_class.cancelled_transaction = yield == false
|
@@ -391,7 +736,7 @@ class TransitionHaltedAfterCallbackTest < Test::Unit::TestCase
|
|
391
736
|
end
|
392
737
|
end
|
393
738
|
|
394
|
-
class
|
739
|
+
class TransitionWithActionFailedTest < Test::Unit::TestCase
|
395
740
|
def setup
|
396
741
|
@klass = Class.new do
|
397
742
|
class << self; attr_accessor :cancelled_transaction; end
|
@@ -406,6 +751,7 @@ class TransitionWithFailedActionTest < Test::Unit::TestCase
|
|
406
751
|
|
407
752
|
@machine = StateMachine::Machine.new(@klass, :action => :save)
|
408
753
|
@machine.state :parked, :idling
|
754
|
+
@machine.event :ignite
|
409
755
|
class << @machine
|
410
756
|
def within_transaction(object)
|
411
757
|
owner_class.cancelled_transaction = yield == false
|
@@ -424,8 +770,8 @@ class TransitionWithFailedActionTest < Test::Unit::TestCase
|
|
424
770
|
assert !@result
|
425
771
|
end
|
426
772
|
|
427
|
-
def
|
428
|
-
|
773
|
+
def test_should_not_change_current_state
|
774
|
+
assert_nil @object.state
|
429
775
|
end
|
430
776
|
|
431
777
|
def test_should_run_before_callbacks
|
@@ -440,3 +786,318 @@ class TransitionWithFailedActionTest < Test::Unit::TestCase
|
|
440
786
|
assert @klass.cancelled_transaction
|
441
787
|
end
|
442
788
|
end
|
789
|
+
|
790
|
+
class TransitionWithActionErrorTest < Test::Unit::TestCase
|
791
|
+
def setup
|
792
|
+
@klass = Class.new do
|
793
|
+
def save
|
794
|
+
raise ArgumentError
|
795
|
+
end
|
796
|
+
end
|
797
|
+
|
798
|
+
@machine = StateMachine::Machine.new(@klass, :action => :save)
|
799
|
+
@machine.state :parked, :idling
|
800
|
+
@machine.event :ignite
|
801
|
+
|
802
|
+
@object = @klass.new
|
803
|
+
@object.state = 'parked'
|
804
|
+
@transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
|
805
|
+
|
806
|
+
@raised = true
|
807
|
+
begin
|
808
|
+
@transition.perform
|
809
|
+
@raised = false
|
810
|
+
rescue ArgumentError
|
811
|
+
end
|
812
|
+
end
|
813
|
+
|
814
|
+
def test_should_not_catch_exception
|
815
|
+
assert @raised
|
816
|
+
end
|
817
|
+
|
818
|
+
def test_should_not_change_current_state
|
819
|
+
assert_equal 'parked', @object.state
|
820
|
+
end
|
821
|
+
end
|
822
|
+
|
823
|
+
class TransitionsInParallelTest < Test::Unit::TestCase
|
824
|
+
def setup
|
825
|
+
@klass = Class.new do
|
826
|
+
attr_reader :persisted
|
827
|
+
|
828
|
+
def initialize
|
829
|
+
@persisted = []
|
830
|
+
@state = 'parked'
|
831
|
+
@status = 'first_gear'
|
832
|
+
super
|
833
|
+
end
|
834
|
+
|
835
|
+
def state=(value)
|
836
|
+
@persisted << value
|
837
|
+
@state = value
|
838
|
+
end
|
839
|
+
|
840
|
+
def status=(value)
|
841
|
+
@persisted << value
|
842
|
+
@status = value
|
843
|
+
end
|
844
|
+
end
|
845
|
+
|
846
|
+
@state = StateMachine::Machine.new(@klass, :state)
|
847
|
+
@state.event :ignite
|
848
|
+
@state.state :parked, :idling
|
849
|
+
|
850
|
+
@status = StateMachine::Machine.new(@klass, :status)
|
851
|
+
@status.event :shift_up
|
852
|
+
@status.state :first_gear, :second_gear
|
853
|
+
|
854
|
+
@object = @klass.new
|
855
|
+
@state_transition = StateMachine::Transition.new(@object, @state, :ignite, :parked, :idling)
|
856
|
+
@status_transition = StateMachine::Transition.new(@object, @status, :shift_up, :first_gear, :second_gear)
|
857
|
+
|
858
|
+
@result = StateMachine::Transition.perform([@state_transition, @status_transition])
|
859
|
+
end
|
860
|
+
|
861
|
+
def test_should_raise_exception_if_attempted_on_the_same_state_machine
|
862
|
+
exception = assert_raise(ArgumentError) { StateMachine::Transition.perform([@state_transition, @state_transition]) }
|
863
|
+
assert_equal 'Cannot perform multiple transitions in parallel for the same state machine attribute', exception.message
|
864
|
+
end
|
865
|
+
|
866
|
+
def test_should_perform
|
867
|
+
assert_equal true, @result
|
868
|
+
end
|
869
|
+
|
870
|
+
def test_should_persist_first_state
|
871
|
+
assert_equal 'idling', @object.state
|
872
|
+
end
|
873
|
+
|
874
|
+
def test_should_persist_second_state
|
875
|
+
assert_equal 'second_gear', @object.status
|
876
|
+
end
|
877
|
+
|
878
|
+
def test_should_persist_in_order
|
879
|
+
assert_equal ['idling', 'second_gear'], @object.persisted
|
880
|
+
end
|
881
|
+
|
882
|
+
def test_should_have_args_in_transitions
|
883
|
+
assert_equal [], @state_transition.args
|
884
|
+
assert_equal [], @status_transition.args
|
885
|
+
end
|
886
|
+
end
|
887
|
+
|
888
|
+
class TransitionsInParallelWithCallbacksTest < Test::Unit::TestCase
|
889
|
+
def setup
|
890
|
+
@klass = Class.new
|
891
|
+
|
892
|
+
@before_callbacks = []
|
893
|
+
@after_callbacks = []
|
894
|
+
|
895
|
+
@state = StateMachine::Machine.new(@klass, :state, :initial => :parked)
|
896
|
+
@state.event :ignite
|
897
|
+
@state.state :parked, :idling
|
898
|
+
@state.before_transition lambda {@before_callbacks << :state}
|
899
|
+
@state.after_transition lambda {@after_callbacks << :state}
|
900
|
+
|
901
|
+
@status = StateMachine::Machine.new(@klass, :status, :initial => :first_gear)
|
902
|
+
@status.event :shift_up
|
903
|
+
@status.state :first_gear, :second_gear
|
904
|
+
@status.before_transition lambda {@before_callbacks << :status}
|
905
|
+
@status.after_transition lambda {@after_callbacks << :status}
|
906
|
+
|
907
|
+
@object = @klass.new
|
908
|
+
@state_transition = StateMachine::Transition.new(@object, @state, :ignite, :parked, :idling)
|
909
|
+
@status_transition = StateMachine::Transition.new(@object, @status, :shift_up, :first_gear, :second_gear)
|
910
|
+
end
|
911
|
+
|
912
|
+
def test_should_run_before_callbacks_in_order
|
913
|
+
perform
|
914
|
+
assert_equal [:state, :status], @before_callbacks
|
915
|
+
end
|
916
|
+
|
917
|
+
def test_should_run_after_callbacks_in_order
|
918
|
+
perform
|
919
|
+
assert_equal [:state, :status], @after_callbacks
|
920
|
+
end
|
921
|
+
|
922
|
+
def test_should_not_run_after_callbacks_if_disabled
|
923
|
+
perform(:after => false)
|
924
|
+
assert_equal [], @after_callbacks
|
925
|
+
end
|
926
|
+
|
927
|
+
def test_should_halt_if_before_callback_halted_for_first_transition
|
928
|
+
@state.before_transition lambda {throw :halt}
|
929
|
+
|
930
|
+
assert_equal false, perform
|
931
|
+
assert_equal [:state], @before_callbacks
|
932
|
+
assert_equal 'parked', @object.state
|
933
|
+
assert_equal 'first_gear', @object.status
|
934
|
+
assert_equal [], @after_callbacks
|
935
|
+
end
|
936
|
+
|
937
|
+
def test_should_halt_if_before_callback_halted_for_second_transition
|
938
|
+
@status.before_transition lambda {throw :halt}
|
939
|
+
|
940
|
+
assert_equal false, perform
|
941
|
+
assert_equal [:state, :status], @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_perform_if_after_callback_halted_for_first_transition
|
948
|
+
@state.after_transition lambda {throw :halt}
|
949
|
+
@state.after_transition lambda {@after_callbacks << :invalid}
|
950
|
+
|
951
|
+
assert_equal true, perform
|
952
|
+
assert_equal [:state, :status], @before_callbacks
|
953
|
+
assert_equal 'idling', @object.state
|
954
|
+
assert_equal 'second_gear', @object.status
|
955
|
+
assert_equal [:state, :status], @after_callbacks
|
956
|
+
end
|
957
|
+
|
958
|
+
def test_should_perform_if_after_callback_halted_for_second_transition
|
959
|
+
@status.after_transition lambda {throw :halt}
|
960
|
+
@status.after_transition lambda {@after_callbacks << :invalid}
|
961
|
+
|
962
|
+
assert_equal true, perform
|
963
|
+
assert_equal [:state, :status], @before_callbacks
|
964
|
+
assert_equal 'idling', @object.state
|
965
|
+
assert_equal 'second_gear', @object.status
|
966
|
+
assert_equal [:state, :status], @after_callbacks
|
967
|
+
end
|
968
|
+
|
969
|
+
private
|
970
|
+
def perform(options = {})
|
971
|
+
StateMachine::Transition.perform([@state_transition, @status_transition], options)
|
972
|
+
end
|
973
|
+
end
|
974
|
+
|
975
|
+
class TransitionsInParallelWithActionsTest < Test::Unit::TestCase
|
976
|
+
def setup
|
977
|
+
@klass = Class.new do
|
978
|
+
attr_reader :actions
|
979
|
+
|
980
|
+
def save_state
|
981
|
+
(@actions ||= []) << :save_state
|
982
|
+
:save_state
|
983
|
+
end
|
984
|
+
|
985
|
+
def save_status
|
986
|
+
(@actions ||= []) << :save_status
|
987
|
+
:save_status
|
988
|
+
end
|
989
|
+
end
|
990
|
+
|
991
|
+
@state = StateMachine::Machine.new(@klass, :state, :initial => :parked, :action => :save_state)
|
992
|
+
@state.event :ignite
|
993
|
+
@state.state :parked, :idling
|
994
|
+
|
995
|
+
@status = StateMachine::Machine.new(@klass, :status, :initial => :first_gear, :action => :save_status)
|
996
|
+
@status.event :shift_up
|
997
|
+
@status.state :first_gear, :second_gear
|
998
|
+
|
999
|
+
@object = @klass.new
|
1000
|
+
@state_transition = StateMachine::Transition.new(@object, @state, :ignite, :parked, :idling)
|
1001
|
+
@status_transition = StateMachine::Transition.new(@object, @status, :shift_up, :first_gear, :second_gear)
|
1002
|
+
end
|
1003
|
+
|
1004
|
+
def test_should_run_actions_in_order
|
1005
|
+
perform
|
1006
|
+
assert_equal [:save_state, :save_status], @object.actions
|
1007
|
+
end
|
1008
|
+
|
1009
|
+
def test_should_store_action_specific_results
|
1010
|
+
perform
|
1011
|
+
assert_equal :save_state, @state_transition.result
|
1012
|
+
assert_equal :save_status, @status_transition.result
|
1013
|
+
end
|
1014
|
+
|
1015
|
+
def test_should_not_perform_if_action_fails_for_first_transition
|
1016
|
+
@klass.class_eval do
|
1017
|
+
def save_state
|
1018
|
+
false
|
1019
|
+
end
|
1020
|
+
end
|
1021
|
+
|
1022
|
+
assert_equal false, perform
|
1023
|
+
assert_equal 'parked', @object.state
|
1024
|
+
assert_equal 'first_gear', @object.status
|
1025
|
+
end
|
1026
|
+
|
1027
|
+
def test_should_not_perform_if_action_fails_for_second_transition
|
1028
|
+
@klass.class_eval do
|
1029
|
+
def save_status
|
1030
|
+
false
|
1031
|
+
end
|
1032
|
+
end
|
1033
|
+
|
1034
|
+
assert_equal false, perform
|
1035
|
+
assert_equal 'parked', @object.state
|
1036
|
+
assert_equal 'first_gear', @object.status
|
1037
|
+
end
|
1038
|
+
|
1039
|
+
def test_should_rollback_if_action_errors_for_first_transition
|
1040
|
+
@klass.class_eval do
|
1041
|
+
def save_state
|
1042
|
+
raise ArgumentError
|
1043
|
+
end
|
1044
|
+
end
|
1045
|
+
|
1046
|
+
begin; perform; rescue; end
|
1047
|
+
assert_equal 'parked', @object.state
|
1048
|
+
assert_equal 'first_gear', @object.status
|
1049
|
+
end
|
1050
|
+
|
1051
|
+
def test_should_rollback_if_action_errors_for_second_transition
|
1052
|
+
@klass.class_eval do
|
1053
|
+
def save_status
|
1054
|
+
raise ArgumentError
|
1055
|
+
end
|
1056
|
+
end
|
1057
|
+
|
1058
|
+
begin; perform; rescue; end
|
1059
|
+
assert_equal 'parked', @object.state
|
1060
|
+
assert_equal 'first_gear', @object.status
|
1061
|
+
end
|
1062
|
+
|
1063
|
+
private
|
1064
|
+
def perform(options = {})
|
1065
|
+
StateMachine::Transition.perform([@state_transition, @status_transition], options)
|
1066
|
+
end
|
1067
|
+
end
|
1068
|
+
|
1069
|
+
class TransitionsWithPerformBlockTest < Test::Unit::TestCase
|
1070
|
+
def setup
|
1071
|
+
@klass = Class.new
|
1072
|
+
|
1073
|
+
@state = StateMachine::Machine.new(@klass, :state, :initial => :parked)
|
1074
|
+
@state.event :ignite
|
1075
|
+
@state.state :parked, :idling
|
1076
|
+
|
1077
|
+
@status = StateMachine::Machine.new(@klass, :status, :initial => :first_gear)
|
1078
|
+
@status.event :shift_up
|
1079
|
+
@status.state :first_gear, :second_gear
|
1080
|
+
|
1081
|
+
@object = @klass.new
|
1082
|
+
@state_transition = StateMachine::Transition.new(@object, @state, :ignite, :parked, :idling)
|
1083
|
+
@status_transition = StateMachine::Transition.new(@object, @status, :shift_up, :first_gear, :second_gear)
|
1084
|
+
end
|
1085
|
+
|
1086
|
+
def test_should_be_perform_if_result_is_not_false
|
1087
|
+
assert StateMachine::Transition.perform([@state_transition, @status_transition]) { true }
|
1088
|
+
assert_equal 'idling', @object.state
|
1089
|
+
assert_equal 'second_gear', @object.status
|
1090
|
+
end
|
1091
|
+
|
1092
|
+
def test_should_not_perform_if_result_is_false
|
1093
|
+
assert !StateMachine::Transition.perform([@state_transition, @status_transition]) { false }
|
1094
|
+
assert_equal 'parked', @object.state
|
1095
|
+
assert_equal 'first_gear', @object.status
|
1096
|
+
end
|
1097
|
+
|
1098
|
+
def test_should_use_result_as_transition_result
|
1099
|
+
StateMachine::Transition.perform([@state_transition, @status_transition]) { 1 }
|
1100
|
+
assert_equal 1, @state_transition.result
|
1101
|
+
assert_equal 1, @status_transition.result
|
1102
|
+
end
|
1103
|
+
end
|