pluginaweek-state_machine 0.7.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.rdoc +273 -0
- data/LICENSE +20 -0
- data/README.rdoc +466 -0
- data/Rakefile +98 -0
- data/examples/AutoShop_state.png +0 -0
- data/examples/Car_state.png +0 -0
- data/examples/TrafficLight_state.png +0 -0
- data/examples/Vehicle_state.png +0 -0
- data/examples/auto_shop.rb +11 -0
- data/examples/car.rb +19 -0
- data/examples/merb-rest/controller.rb +51 -0
- data/examples/merb-rest/model.rb +28 -0
- data/examples/merb-rest/view_edit.html.erb +24 -0
- data/examples/merb-rest/view_index.html.erb +23 -0
- data/examples/merb-rest/view_new.html.erb +13 -0
- data/examples/merb-rest/view_show.html.erb +17 -0
- data/examples/rails-rest/controller.rb +43 -0
- data/examples/rails-rest/migration.rb +11 -0
- data/examples/rails-rest/model.rb +23 -0
- data/examples/rails-rest/view_edit.html.erb +25 -0
- data/examples/rails-rest/view_index.html.erb +23 -0
- data/examples/rails-rest/view_new.html.erb +14 -0
- data/examples/rails-rest/view_show.html.erb +17 -0
- data/examples/traffic_light.rb +7 -0
- data/examples/vehicle.rb +31 -0
- data/init.rb +1 -0
- data/lib/state_machine.rb +429 -0
- data/lib/state_machine/assertions.rb +36 -0
- data/lib/state_machine/callback.rb +189 -0
- data/lib/state_machine/condition_proxy.rb +94 -0
- data/lib/state_machine/eval_helpers.rb +67 -0
- data/lib/state_machine/event.rb +251 -0
- data/lib/state_machine/event_collection.rb +113 -0
- data/lib/state_machine/extensions.rb +158 -0
- data/lib/state_machine/guard.rb +219 -0
- data/lib/state_machine/integrations.rb +68 -0
- data/lib/state_machine/integrations/active_record.rb +444 -0
- data/lib/state_machine/integrations/active_record/locale.rb +10 -0
- data/lib/state_machine/integrations/active_record/observer.rb +41 -0
- data/lib/state_machine/integrations/data_mapper.rb +325 -0
- data/lib/state_machine/integrations/data_mapper/observer.rb +139 -0
- data/lib/state_machine/integrations/sequel.rb +292 -0
- data/lib/state_machine/machine.rb +1431 -0
- data/lib/state_machine/machine_collection.rb +146 -0
- data/lib/state_machine/matcher.rb +123 -0
- data/lib/state_machine/matcher_helpers.rb +54 -0
- data/lib/state_machine/node_collection.rb +152 -0
- data/lib/state_machine/state.rb +249 -0
- data/lib/state_machine/state_collection.rb +112 -0
- data/lib/state_machine/transition.rb +367 -0
- data/tasks/state_machine.rake +1 -0
- data/tasks/state_machine.rb +30 -0
- data/test/classes/switch.rb +11 -0
- data/test/functional/state_machine_test.rb +941 -0
- data/test/test_helper.rb +4 -0
- data/test/unit/assertions_test.rb +40 -0
- data/test/unit/callback_test.rb +455 -0
- data/test/unit/condition_proxy_test.rb +328 -0
- data/test/unit/eval_helpers_test.rb +129 -0
- data/test/unit/event_collection_test.rb +293 -0
- data/test/unit/event_test.rb +605 -0
- data/test/unit/guard_test.rb +862 -0
- data/test/unit/integrations/active_record_test.rb +1001 -0
- data/test/unit/integrations/data_mapper_test.rb +694 -0
- data/test/unit/integrations/sequel_test.rb +486 -0
- data/test/unit/integrations_test.rb +42 -0
- data/test/unit/invalid_event_test.rb +7 -0
- data/test/unit/invalid_transition_test.rb +7 -0
- data/test/unit/machine_collection_test.rb +710 -0
- data/test/unit/machine_test.rb +1910 -0
- data/test/unit/matcher_helpers_test.rb +37 -0
- data/test/unit/matcher_test.rb +155 -0
- data/test/unit/node_collection_test.rb +207 -0
- data/test/unit/state_collection_test.rb +280 -0
- data/test/unit/state_machine_test.rb +31 -0
- data/test/unit/state_test.rb +795 -0
- data/test/unit/transition_test.rb +1113 -0
- metadata +161 -0
@@ -0,0 +1,293 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
|
2
|
+
|
3
|
+
class EventCollectionByDefaultTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@machine = StateMachine::Machine.new(Class.new)
|
6
|
+
@events = StateMachine::EventCollection.new(@machine)
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_should_not_have_any_nodes
|
10
|
+
assert_equal 0, @events.length
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_should_have_a_machine
|
14
|
+
assert_equal @machine, @events.machine
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_should_not_have_any_valid_events_for_an_object
|
18
|
+
assert @events.valid_for(@object).empty?
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_should_not_have_any_transitions_for_an_object
|
22
|
+
assert @events.transitions_for(@object).empty?
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class EventCollectionTest < Test::Unit::TestCase
|
27
|
+
def setup
|
28
|
+
machine = StateMachine::Machine.new(Class.new, :namespace => 'alarm')
|
29
|
+
@events = StateMachine::EventCollection.new(machine)
|
30
|
+
|
31
|
+
@events << @open = StateMachine::Event.new(machine, :enable)
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_should_index_by_name
|
35
|
+
assert_equal @open, @events[:enable, :name]
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_should_index_by_name_by_default
|
39
|
+
assert_equal @open, @events[:enable]
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_should_index_by_qualified_name
|
43
|
+
assert_equal @open, @events[:enable_alarm, :qualified_name]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class EventCollectionWithEventsWithTransitionsTest < Test::Unit::TestCase
|
48
|
+
def setup
|
49
|
+
@klass = Class.new
|
50
|
+
@machine = StateMachine::Machine.new(@klass, :initial => :parked)
|
51
|
+
@events = StateMachine::EventCollection.new(@machine)
|
52
|
+
|
53
|
+
@machine.state :idling, :stalled
|
54
|
+
@machine.event :ignite
|
55
|
+
|
56
|
+
@events << @ignite = StateMachine::Event.new(@machine, :ignite)
|
57
|
+
@ignite.transition :parked => :idling
|
58
|
+
@ignite.transition :stalled => :idling
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_should_only_include_valid_events_for_an_object
|
62
|
+
object = @klass.new
|
63
|
+
object.state = 'parked'
|
64
|
+
assert_equal [@ignite], @events.valid_for(object)
|
65
|
+
|
66
|
+
object.state = 'stalled'
|
67
|
+
assert_equal [@ignite], @events.valid_for(object)
|
68
|
+
|
69
|
+
object.state = 'idling'
|
70
|
+
assert_equal [], @events.valid_for(object)
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_should_only_include_valid_transitions_for_an_object
|
74
|
+
object = @klass.new
|
75
|
+
object.state = 'parked'
|
76
|
+
assert_equal [{:object => object, :attribute => :state, :event => :ignite, :from => 'parked', :to => 'idling'}], @events.transitions_for(object).map {|transition| transition.attributes}
|
77
|
+
|
78
|
+
object.state = 'stalled'
|
79
|
+
assert_equal [{:object => object, :attribute => :state, :event => :ignite, :from => 'stalled', :to => 'idling'}], @events.transitions_for(object).map {|transition| transition.attributes}
|
80
|
+
|
81
|
+
object.state = 'idling'
|
82
|
+
assert_equal [], @events.transitions_for(object)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
class EventCollectionWithMultipleEventsTest < Test::Unit::TestCase
|
87
|
+
def setup
|
88
|
+
@klass = Class.new
|
89
|
+
@machine = StateMachine::Machine.new(@klass, :initial => :parked)
|
90
|
+
@events = StateMachine::EventCollection.new(@machine)
|
91
|
+
|
92
|
+
@machine.state :first_gear
|
93
|
+
@machine.event :park, :shift_down
|
94
|
+
|
95
|
+
@events << @park = StateMachine::Event.new(@machine, :park)
|
96
|
+
@park.transition :first_gear => :parked
|
97
|
+
|
98
|
+
@events << @shift_down = StateMachine::Event.new(@machine, :shift_down)
|
99
|
+
@shift_down.transition :first_gear => :parked
|
100
|
+
end
|
101
|
+
|
102
|
+
def test_should_only_include_all_valid_events_for_an_object
|
103
|
+
object = @klass.new
|
104
|
+
object.state = 'first_gear'
|
105
|
+
assert_equal [@park, @shift_down], @events.valid_for(object)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
class EventCollectionWithoutMachineActionTest < Test::Unit::TestCase
|
110
|
+
def setup
|
111
|
+
@klass = Class.new
|
112
|
+
@machine = StateMachine::Machine.new(@klass, :initial => :parked)
|
113
|
+
@events = StateMachine::EventCollection.new(@machine)
|
114
|
+
|
115
|
+
@machine.event :ignite
|
116
|
+
@events << StateMachine::Event.new(@machine, :ignite)
|
117
|
+
|
118
|
+
@object = @klass.new
|
119
|
+
end
|
120
|
+
|
121
|
+
def test_should_not_have_an_attribute_transition
|
122
|
+
assert_nil @events.attribute_transition_for(@object)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
class EventCollectionAttributeWithMachineActionTest < Test::Unit::TestCase
|
127
|
+
def setup
|
128
|
+
@klass = Class.new do
|
129
|
+
def save
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
@machine = StateMachine::Machine.new(@klass, :initial => :parked, :action => :save)
|
134
|
+
@events = StateMachine::EventCollection.new(@machine)
|
135
|
+
|
136
|
+
@machine.event :ignite
|
137
|
+
@machine.state :parked, :idling
|
138
|
+
@events << @ignite = StateMachine::Event.new(@machine, :ignite)
|
139
|
+
|
140
|
+
@object = @klass.new
|
141
|
+
end
|
142
|
+
|
143
|
+
def test_should_not_have_transition_if_nil
|
144
|
+
@object.state_event = nil
|
145
|
+
assert_nil @events.attribute_transition_for(@object)
|
146
|
+
end
|
147
|
+
|
148
|
+
def test_should_not_have_transition_if_empty
|
149
|
+
@object.state_event = ''
|
150
|
+
assert_nil @events.attribute_transition_for(@object)
|
151
|
+
end
|
152
|
+
|
153
|
+
def test_should_have_invalid_transition_if_invalid_event_specified
|
154
|
+
@object.state_event = 'invalid'
|
155
|
+
assert_equal false, @events.attribute_transition_for(@object)
|
156
|
+
end
|
157
|
+
|
158
|
+
def test_should_have_invalid_transition_if_event_cannot_be_fired
|
159
|
+
@object.state_event = 'ignite'
|
160
|
+
assert_equal false, @events.attribute_transition_for(@object)
|
161
|
+
end
|
162
|
+
|
163
|
+
def test_should_have_valid_transition_if_event_can_be_fired
|
164
|
+
@ignite.transition :parked => :idling
|
165
|
+
@object.state_event = 'ignite'
|
166
|
+
|
167
|
+
assert_instance_of StateMachine::Transition, @events.attribute_transition_for(@object)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
class EventCollectionAttributeWithNamespacedMachineTest < Test::Unit::TestCase
|
172
|
+
def setup
|
173
|
+
@klass = Class.new do
|
174
|
+
def save
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
@machine = StateMachine::Machine.new(@klass, :namespace => 'alarm', :initial => :active, :action => :save)
|
179
|
+
@events = StateMachine::EventCollection.new(@machine)
|
180
|
+
|
181
|
+
@machine.event :disable
|
182
|
+
@machine.state :active, :off
|
183
|
+
@events << @disable = StateMachine::Event.new(@machine, :disable)
|
184
|
+
|
185
|
+
@object = @klass.new
|
186
|
+
end
|
187
|
+
|
188
|
+
def test_should_not_have_transition_if_nil
|
189
|
+
@object.state_event = nil
|
190
|
+
assert_nil @events.attribute_transition_for(@object)
|
191
|
+
end
|
192
|
+
|
193
|
+
def test_should_have_invalid_transition_if_event_cannot_be_fired
|
194
|
+
@object.state_event = 'disable'
|
195
|
+
assert_equal false, @events.attribute_transition_for(@object)
|
196
|
+
end
|
197
|
+
|
198
|
+
def test_should_have_valid_transition_if_event_can_be_fired
|
199
|
+
@disable.transition :active => :off
|
200
|
+
@object.state_event = 'disable'
|
201
|
+
|
202
|
+
assert_instance_of StateMachine::Transition, @events.attribute_transition_for(@object)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
class EventCollectionWithValidationsTest < Test::Unit::TestCase
|
207
|
+
def setup
|
208
|
+
StateMachine::Integrations.const_set('Custom', Module.new do
|
209
|
+
def invalidate(object, attribute, message, values = [])
|
210
|
+
(object.errors ||= []) << generate_message(message, values)
|
211
|
+
end
|
212
|
+
|
213
|
+
def reset(object)
|
214
|
+
object.errors = []
|
215
|
+
end
|
216
|
+
end)
|
217
|
+
|
218
|
+
@klass = Class.new do
|
219
|
+
attr_accessor :errors
|
220
|
+
|
221
|
+
def initialize
|
222
|
+
@errors = []
|
223
|
+
super
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
@machine = StateMachine::Machine.new(@klass, :initial => :parked, :action => :save, :integration => :custom)
|
228
|
+
@events = StateMachine::EventCollection.new(@machine)
|
229
|
+
|
230
|
+
@machine.event :ignite
|
231
|
+
@machine.state :parked, :idling
|
232
|
+
@events << @ignite = StateMachine::Event.new(@machine, :ignite)
|
233
|
+
|
234
|
+
@object = @klass.new
|
235
|
+
end
|
236
|
+
|
237
|
+
def test_should_invalidate_if_invalid_event_specified
|
238
|
+
@object.state_event = 'invalid'
|
239
|
+
@events.attribute_transition_for(@object, true)
|
240
|
+
|
241
|
+
assert_equal ['is invalid'], @object.errors
|
242
|
+
end
|
243
|
+
|
244
|
+
def test_should_invalidate_if_event_cannot_be_fired
|
245
|
+
@object.state = 'idling'
|
246
|
+
@object.state_event = 'ignite'
|
247
|
+
@events.attribute_transition_for(@object, true)
|
248
|
+
|
249
|
+
assert_equal ['cannot transition when idling'], @object.errors
|
250
|
+
end
|
251
|
+
|
252
|
+
def test_should_not_invalidate_event_can_be_fired
|
253
|
+
@ignite.transition :parked => :idling
|
254
|
+
@object.state_event = 'ignite'
|
255
|
+
@events.attribute_transition_for(@object, true)
|
256
|
+
|
257
|
+
assert_equal [], @object.errors
|
258
|
+
end
|
259
|
+
|
260
|
+
def teardown
|
261
|
+
StateMachine::Integrations.send(:remove_const, 'Custom')
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
class EventCollectionWithCustomMachineNameTest < Test::Unit::TestCase
|
266
|
+
def setup
|
267
|
+
@klass = Class.new do
|
268
|
+
def save
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
@machine = StateMachine::Machine.new(@klass, :state_id, :as => 'state', :initial => :parked, :action => :save)
|
273
|
+
@events = StateMachine::EventCollection.new(@machine)
|
274
|
+
|
275
|
+
@machine.event :ignite
|
276
|
+
@machine.state :parked, :idling
|
277
|
+
@events << @ignite = StateMachine::Event.new(@machine, :ignite)
|
278
|
+
|
279
|
+
@object = @klass.new
|
280
|
+
end
|
281
|
+
|
282
|
+
def test_should_not_have_transition_if_nil
|
283
|
+
@object.state_event = nil
|
284
|
+
assert_nil @events.attribute_transition_for(@object)
|
285
|
+
end
|
286
|
+
|
287
|
+
def test_should_have_valid_transition_if_event_can_be_fired
|
288
|
+
@ignite.transition :parked => :idling
|
289
|
+
@object.state_event = 'ignite'
|
290
|
+
|
291
|
+
assert_instance_of StateMachine::Transition, @events.attribute_transition_for(@object)
|
292
|
+
end
|
293
|
+
end
|
@@ -0,0 +1,605 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
|
2
|
+
|
3
|
+
class EventByDefaultTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@klass = Class.new
|
6
|
+
@machine = StateMachine::Machine.new(@klass)
|
7
|
+
@event = StateMachine::Event.new(@machine, :ignite)
|
8
|
+
|
9
|
+
@object = @klass.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_should_have_a_machine
|
13
|
+
assert_equal @machine, @event.machine
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_should_have_a_name
|
17
|
+
assert_equal :ignite, @event.name
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_should_have_a_qualified_name
|
21
|
+
assert_equal :ignite, @event.qualified_name
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_should_not_have_any_guards
|
25
|
+
assert @event.guards.empty?
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_should_have_no_known_states
|
29
|
+
assert @event.known_states.empty?
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_should_not_be_able_to_fire
|
33
|
+
assert !@event.can_fire?(@object)
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_should_not_have_a_transition
|
37
|
+
assert_nil @event.transition_for(@object)
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_should_define_a_predicate
|
41
|
+
assert @object.respond_to?(:can_ignite?)
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_should_define_a_transition_accessor
|
45
|
+
assert @object.respond_to?(:ignite_transition)
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_should_define_an_action
|
49
|
+
assert @object.respond_to?(:ignite)
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_should_define_a_bang_action
|
53
|
+
assert @object.respond_to?(:ignite!)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class EventTest < Test::Unit::TestCase
|
58
|
+
def setup
|
59
|
+
@machine = StateMachine::Machine.new(Class.new)
|
60
|
+
@event = StateMachine::Event.new(@machine, :ignite)
|
61
|
+
@event.transition :parked => :idling
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_should_allow_changing_machine
|
65
|
+
new_machine = StateMachine::Machine.new(Class.new)
|
66
|
+
@event.machine = new_machine
|
67
|
+
assert_equal new_machine, @event.machine
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_should_provide_matcher_helpers_during_initialization
|
71
|
+
matchers = []
|
72
|
+
|
73
|
+
@event.instance_eval do
|
74
|
+
matchers = [all, any, same]
|
75
|
+
end
|
76
|
+
|
77
|
+
assert_equal [StateMachine::AllMatcher.instance, StateMachine::AllMatcher.instance, StateMachine::LoopbackMatcher.instance], matchers
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_should_use_pretty_inspect
|
81
|
+
assert_match "#<StateMachine::Event name=:ignite transitions=[:parked => :idling]>", @event.inspect
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class EventWithConflictingHelpersTest < Test::Unit::TestCase
|
86
|
+
def setup
|
87
|
+
@klass = Class.new do
|
88
|
+
def can_ignite?
|
89
|
+
0
|
90
|
+
end
|
91
|
+
|
92
|
+
def ignite_transition
|
93
|
+
0
|
94
|
+
end
|
95
|
+
|
96
|
+
def ignite
|
97
|
+
0
|
98
|
+
end
|
99
|
+
|
100
|
+
def ignite!
|
101
|
+
0
|
102
|
+
end
|
103
|
+
end
|
104
|
+
@machine = StateMachine::Machine.new(@klass)
|
105
|
+
@state = StateMachine::Event.new(@machine, :ignite)
|
106
|
+
@object = @klass.new
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_should_not_redefine_predicate
|
110
|
+
assert_equal 0, @object.can_ignite?
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_should_not_redefine_transition_accessor
|
114
|
+
assert_equal 0, @object.ignite_transition
|
115
|
+
end
|
116
|
+
|
117
|
+
def test_should_not_redefine_action
|
118
|
+
assert_equal 0, @object.ignite
|
119
|
+
end
|
120
|
+
|
121
|
+
def test_should_not_redefine_bang_action
|
122
|
+
assert_equal 0, @object.ignite!
|
123
|
+
end
|
124
|
+
|
125
|
+
def test_should_allow_super_chaining
|
126
|
+
@klass.class_eval do
|
127
|
+
def can_ignite?
|
128
|
+
super ? 1 : 0
|
129
|
+
end
|
130
|
+
|
131
|
+
def ignite_transition
|
132
|
+
super ? 1 : 0
|
133
|
+
end
|
134
|
+
|
135
|
+
def ignite
|
136
|
+
super ? 1 : 0
|
137
|
+
end
|
138
|
+
|
139
|
+
def ignite!
|
140
|
+
begin
|
141
|
+
super
|
142
|
+
1
|
143
|
+
rescue Exception => ex
|
144
|
+
0
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
assert_equal 0, @object.can_ignite?
|
150
|
+
assert_equal 0, @object.ignite_transition
|
151
|
+
assert_equal 0, @object.ignite
|
152
|
+
assert_equal 1, @object.ignite!
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
class EventWithNamespaceTest < Test::Unit::TestCase
|
157
|
+
def setup
|
158
|
+
@klass = Class.new
|
159
|
+
@machine = StateMachine::Machine.new(@klass, :namespace => 'alarm')
|
160
|
+
@event = StateMachine::Event.new(@machine, :enable)
|
161
|
+
@object = @klass.new
|
162
|
+
end
|
163
|
+
|
164
|
+
def test_should_have_a_name
|
165
|
+
assert_equal :enable, @event.name
|
166
|
+
end
|
167
|
+
|
168
|
+
def test_should_have_a_qualified_name
|
169
|
+
assert_equal :enable_alarm, @event.qualified_name
|
170
|
+
end
|
171
|
+
|
172
|
+
def test_should_namespace_predicate
|
173
|
+
assert @object.respond_to?(:can_enable_alarm?)
|
174
|
+
end
|
175
|
+
|
176
|
+
def test_should_namespace_transition_accessor
|
177
|
+
assert @object.respond_to?(:enable_alarm_transition)
|
178
|
+
end
|
179
|
+
|
180
|
+
def test_should_namespace_action
|
181
|
+
assert @object.respond_to?(:enable_alarm)
|
182
|
+
end
|
183
|
+
|
184
|
+
def test_should_namespace_bang_action
|
185
|
+
assert @object.respond_to?(:enable_alarm!)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
class EventTransitionsTest < Test::Unit::TestCase
|
190
|
+
def setup
|
191
|
+
@machine = StateMachine::Machine.new(Class.new)
|
192
|
+
@event = StateMachine::Event.new(@machine, :ignite)
|
193
|
+
end
|
194
|
+
|
195
|
+
def test_should_not_raise_exception_if_implicit_option_specified
|
196
|
+
assert_nothing_raised {@event.transition(:invalid => :valid)}
|
197
|
+
end
|
198
|
+
|
199
|
+
def test_should_not_allow_on_option
|
200
|
+
exception = assert_raise(ArgumentError) {@event.transition(:on => :ignite)}
|
201
|
+
assert_equal 'Invalid key(s): on', exception.message
|
202
|
+
end
|
203
|
+
|
204
|
+
def test_should_not_allow_except_to_option
|
205
|
+
exception = assert_raise(ArgumentError) {@event.transition(:except_to => :parked)}
|
206
|
+
assert_equal 'Invalid key(s): except_to', exception.message
|
207
|
+
end
|
208
|
+
|
209
|
+
def test_should_not_allow_except_on_option
|
210
|
+
exception = assert_raise(ArgumentError) {@event.transition(:except_on => :ignite)}
|
211
|
+
assert_equal 'Invalid key(s): except_on', exception.message
|
212
|
+
end
|
213
|
+
|
214
|
+
def test_should_allow_transitioning_without_a_to_state
|
215
|
+
assert_nothing_raised {@event.transition(:from => :parked)}
|
216
|
+
end
|
217
|
+
|
218
|
+
def test_should_allow_transitioning_without_a_from_state
|
219
|
+
assert_nothing_raised {@event.transition(:to => :idling)}
|
220
|
+
end
|
221
|
+
|
222
|
+
def test_should_allow_except_from_option
|
223
|
+
assert_nothing_raised {@event.transition(:except_from => :idling)}
|
224
|
+
end
|
225
|
+
|
226
|
+
def test_should_allow_transitioning_from_a_single_state
|
227
|
+
assert @event.transition(:parked => :idling)
|
228
|
+
end
|
229
|
+
|
230
|
+
def test_should_allow_transitioning_from_multiple_states
|
231
|
+
assert @event.transition([:parked, :idling] => :idling)
|
232
|
+
end
|
233
|
+
|
234
|
+
def test_should_have_transitions
|
235
|
+
guard = @event.transition(:to => :idling)
|
236
|
+
assert_equal [guard], @event.guards
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
class EventAfterBeingCopiedTest < Test::Unit::TestCase
|
241
|
+
def setup
|
242
|
+
@machine = StateMachine::Machine.new(Class.new)
|
243
|
+
@event = StateMachine::Event.new(@machine, :ignite)
|
244
|
+
@copied_event = @event.dup
|
245
|
+
end
|
246
|
+
|
247
|
+
def test_should_not_have_the_same_collection_of_guards
|
248
|
+
assert_not_same @event.guards, @copied_event.guards
|
249
|
+
end
|
250
|
+
|
251
|
+
def test_should_not_have_the_same_collection_of_known_states
|
252
|
+
assert_not_same @event.known_states, @copied_event.known_states
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
class EventWithoutTransitionsTest < Test::Unit::TestCase
|
257
|
+
def setup
|
258
|
+
@klass = Class.new
|
259
|
+
@machine = StateMachine::Machine.new(@klass)
|
260
|
+
@event = StateMachine::Event.new(@machine, :ignite)
|
261
|
+
@object = @klass.new
|
262
|
+
end
|
263
|
+
|
264
|
+
def test_should_not_be_able_to_fire
|
265
|
+
assert !@event.can_fire?(@object)
|
266
|
+
end
|
267
|
+
|
268
|
+
def test_should_not_have_a_transition
|
269
|
+
assert_nil @event.transition_for(@object)
|
270
|
+
end
|
271
|
+
|
272
|
+
def test_should_not_fire
|
273
|
+
assert !@event.fire(@object)
|
274
|
+
end
|
275
|
+
|
276
|
+
def test_should_not_change_the_current_state
|
277
|
+
@event.fire(@object)
|
278
|
+
assert_nil @object.state
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
class EventWithTransitionsTest < Test::Unit::TestCase
|
283
|
+
def setup
|
284
|
+
@klass = Class.new
|
285
|
+
@machine = StateMachine::Machine.new(@klass)
|
286
|
+
@event = StateMachine::Event.new(@machine, :ignite)
|
287
|
+
@event.transition(:parked => :idling)
|
288
|
+
@event.transition(:first_gear => :idling)
|
289
|
+
end
|
290
|
+
|
291
|
+
def test_should_include_all_transition_states_in_known_states
|
292
|
+
assert_equal [:parked, :idling, :first_gear], @event.known_states
|
293
|
+
end
|
294
|
+
|
295
|
+
def test_should_include_new_transition_states_after_calling_known_states
|
296
|
+
@event.known_states
|
297
|
+
@event.transition(:stalled => :idling)
|
298
|
+
|
299
|
+
assert_equal [:parked, :idling, :first_gear, :stalled], @event.known_states
|
300
|
+
end
|
301
|
+
|
302
|
+
def test_should_use_pretty_inspect
|
303
|
+
assert_match "#<StateMachine::Event name=:ignite transitions=[:parked => :idling, :first_gear => :idling]>", @event.inspect
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
class EventWithoutMatchingTransitionsTest < Test::Unit::TestCase
|
308
|
+
def setup
|
309
|
+
@klass = Class.new
|
310
|
+
@machine = StateMachine::Machine.new(@klass)
|
311
|
+
@machine.state :parked, :idling
|
312
|
+
|
313
|
+
@event = StateMachine::Event.new(@machine, :ignite)
|
314
|
+
@event.transition(:parked => :idling)
|
315
|
+
|
316
|
+
@object = @klass.new
|
317
|
+
@object.state = 'idling'
|
318
|
+
end
|
319
|
+
|
320
|
+
def test_should_not_be_able_to_fire
|
321
|
+
assert !@event.can_fire?(@object)
|
322
|
+
end
|
323
|
+
|
324
|
+
def test_should_not_have_a_transition
|
325
|
+
assert_nil @event.transition_for(@object)
|
326
|
+
end
|
327
|
+
|
328
|
+
def test_should_not_fire
|
329
|
+
assert !@event.fire(@object)
|
330
|
+
end
|
331
|
+
|
332
|
+
def test_should_not_change_the_current_state
|
333
|
+
@event.fire(@object)
|
334
|
+
assert_equal 'idling', @object.state
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
class EventWithMatchingDisabledTransitionsTest < Test::Unit::TestCase
|
339
|
+
def setup
|
340
|
+
StateMachine::Integrations.const_set('Custom', Module.new do
|
341
|
+
def invalidate(object, attribute, message, values = [])
|
342
|
+
(object.errors ||= []) << generate_message(message, values)
|
343
|
+
end
|
344
|
+
|
345
|
+
def reset(object)
|
346
|
+
object.errors = []
|
347
|
+
end
|
348
|
+
end)
|
349
|
+
|
350
|
+
@klass = Class.new do
|
351
|
+
attr_accessor :errors
|
352
|
+
end
|
353
|
+
|
354
|
+
@machine = StateMachine::Machine.new(@klass, :integration => :custom)
|
355
|
+
@machine.state :parked, :idling
|
356
|
+
|
357
|
+
@event = StateMachine::Event.new(@machine, :ignite)
|
358
|
+
@event.transition(:parked => :idling, :if => lambda {false})
|
359
|
+
|
360
|
+
@object = @klass.new
|
361
|
+
@object.state = 'parked'
|
362
|
+
end
|
363
|
+
|
364
|
+
def test_should_not_be_able_to_fire
|
365
|
+
assert !@event.can_fire?(@object)
|
366
|
+
end
|
367
|
+
|
368
|
+
def test_should_not_have_a_transition
|
369
|
+
assert_nil @event.transition_for(@object)
|
370
|
+
end
|
371
|
+
|
372
|
+
def test_should_not_fire
|
373
|
+
assert !@event.fire(@object)
|
374
|
+
end
|
375
|
+
|
376
|
+
def test_should_not_change_the_current_state
|
377
|
+
@event.fire(@object)
|
378
|
+
assert_equal 'parked', @object.state
|
379
|
+
end
|
380
|
+
|
381
|
+
def test_should_invalidate_the_state
|
382
|
+
@event.fire(@object)
|
383
|
+
assert_equal ['cannot transition via "ignite"'], @object.errors
|
384
|
+
end
|
385
|
+
|
386
|
+
def test_should_reset_existing_error
|
387
|
+
@object.errors = ['invalid']
|
388
|
+
|
389
|
+
@event.fire(@object)
|
390
|
+
assert_equal ['cannot transition via "ignite"'], @object.errors
|
391
|
+
end
|
392
|
+
|
393
|
+
def teardown
|
394
|
+
StateMachine::Integrations.send(:remove_const, 'Custom')
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
class EventWithMatchingEnabledTransitionsTest < Test::Unit::TestCase
|
399
|
+
def setup
|
400
|
+
StateMachine::Integrations.const_set('Custom', Module.new do
|
401
|
+
def invalidate(object, attribute, message, values = [])
|
402
|
+
(object.errors ||= []) << generate_message(message, values)
|
403
|
+
end
|
404
|
+
|
405
|
+
def reset(object)
|
406
|
+
object.errors = []
|
407
|
+
end
|
408
|
+
end)
|
409
|
+
|
410
|
+
@klass = Class.new do
|
411
|
+
attr_accessor :errors
|
412
|
+
end
|
413
|
+
|
414
|
+
@machine = StateMachine::Machine.new(@klass, :integration => :custom)
|
415
|
+
@machine.state :parked, :idling
|
416
|
+
@machine.event :ignite
|
417
|
+
|
418
|
+
@event = StateMachine::Event.new(@machine, :ignite)
|
419
|
+
@event.transition(:parked => :idling)
|
420
|
+
|
421
|
+
@object = @klass.new
|
422
|
+
@object.state = 'parked'
|
423
|
+
end
|
424
|
+
|
425
|
+
def test_should_be_able_to_fire
|
426
|
+
assert @event.can_fire?(@object)
|
427
|
+
end
|
428
|
+
|
429
|
+
def test_should_have_a_transition
|
430
|
+
transition = @event.transition_for(@object)
|
431
|
+
assert_not_nil transition
|
432
|
+
assert_equal 'parked', transition.from
|
433
|
+
assert_equal 'idling', transition.to
|
434
|
+
assert_equal :ignite, transition.event
|
435
|
+
end
|
436
|
+
|
437
|
+
def test_should_fire
|
438
|
+
assert @event.fire(@object)
|
439
|
+
end
|
440
|
+
|
441
|
+
def test_should_change_the_current_state
|
442
|
+
@event.fire(@object)
|
443
|
+
assert_equal 'idling', @object.state
|
444
|
+
end
|
445
|
+
|
446
|
+
def test_should_reset_existing_error
|
447
|
+
@object.errors = ['invalid']
|
448
|
+
|
449
|
+
@event.fire(@object)
|
450
|
+
assert_equal [], @object.errors
|
451
|
+
end
|
452
|
+
|
453
|
+
def test_should_not_invalidate_the_state
|
454
|
+
@event.fire(@object)
|
455
|
+
assert_equal [], @object.errors
|
456
|
+
end
|
457
|
+
|
458
|
+
def teardown
|
459
|
+
StateMachine::Integrations.send(:remove_const, 'Custom')
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
class EventWithTransitionWithoutToStateTest < Test::Unit::TestCase
|
464
|
+
def setup
|
465
|
+
@klass = Class.new
|
466
|
+
@machine = StateMachine::Machine.new(@klass)
|
467
|
+
@machine.state :parked
|
468
|
+
@machine.event :park
|
469
|
+
|
470
|
+
@event = StateMachine::Event.new(@machine, :park)
|
471
|
+
@event.transition(:from => :parked)
|
472
|
+
|
473
|
+
@object = @klass.new
|
474
|
+
@object.state = 'parked'
|
475
|
+
end
|
476
|
+
|
477
|
+
def test_should_be_able_to_fire
|
478
|
+
assert @event.can_fire?(@object)
|
479
|
+
end
|
480
|
+
|
481
|
+
def test_should_have_a_transition
|
482
|
+
transition = @event.transition_for(@object)
|
483
|
+
assert_not_nil transition
|
484
|
+
assert_equal 'parked', transition.from
|
485
|
+
assert_equal 'parked', transition.to
|
486
|
+
assert_equal :park, transition.event
|
487
|
+
end
|
488
|
+
|
489
|
+
def test_should_fire
|
490
|
+
assert @event.fire(@object)
|
491
|
+
end
|
492
|
+
|
493
|
+
def test_should_not_change_the_current_state
|
494
|
+
@event.fire(@object)
|
495
|
+
assert_equal 'parked', @object.state
|
496
|
+
end
|
497
|
+
end
|
498
|
+
|
499
|
+
class EventWithTransitionWithNilToStateTest < Test::Unit::TestCase
|
500
|
+
def setup
|
501
|
+
@klass = Class.new
|
502
|
+
@machine = StateMachine::Machine.new(@klass)
|
503
|
+
@machine.state nil, :idling
|
504
|
+
@machine.event :park
|
505
|
+
|
506
|
+
@event = StateMachine::Event.new(@machine, :park)
|
507
|
+
@event.transition(:idling => nil)
|
508
|
+
|
509
|
+
@object = @klass.new
|
510
|
+
@object.state = 'idling'
|
511
|
+
end
|
512
|
+
|
513
|
+
def test_should_be_able_to_fire
|
514
|
+
assert @event.can_fire?(@object)
|
515
|
+
end
|
516
|
+
|
517
|
+
def test_should_have_a_transition
|
518
|
+
transition = @event.transition_for(@object)
|
519
|
+
assert_not_nil transition
|
520
|
+
assert_equal 'idling', transition.from
|
521
|
+
assert_equal nil, transition.to
|
522
|
+
assert_equal :park, transition.event
|
523
|
+
end
|
524
|
+
|
525
|
+
def test_should_fire
|
526
|
+
assert @event.fire(@object)
|
527
|
+
end
|
528
|
+
|
529
|
+
def test_should_not_change_the_current_state
|
530
|
+
@event.fire(@object)
|
531
|
+
assert_equal nil, @object.state
|
532
|
+
end
|
533
|
+
end
|
534
|
+
|
535
|
+
class EventWithMultipleTransitionsTest < Test::Unit::TestCase
|
536
|
+
def setup
|
537
|
+
@klass = Class.new
|
538
|
+
@machine = StateMachine::Machine.new(@klass)
|
539
|
+
@machine.state :parked, :idling
|
540
|
+
@machine.event :ignite
|
541
|
+
|
542
|
+
@event = StateMachine::Event.new(@machine, :ignite)
|
543
|
+
@event.transition(:idling => :idling)
|
544
|
+
@event.transition(:parked => :idling) # This one should get used
|
545
|
+
|
546
|
+
@object = @klass.new
|
547
|
+
@object.state = 'parked'
|
548
|
+
end
|
549
|
+
|
550
|
+
def test_should_be_able_to_fire
|
551
|
+
assert @event.can_fire?(@object)
|
552
|
+
end
|
553
|
+
|
554
|
+
def test_should_have_a_transition
|
555
|
+
transition = @event.transition_for(@object)
|
556
|
+
assert_not_nil transition
|
557
|
+
assert_equal 'parked', transition.from
|
558
|
+
assert_equal 'idling', transition.to
|
559
|
+
assert_equal :ignite, transition.event
|
560
|
+
end
|
561
|
+
|
562
|
+
def test_should_fire
|
563
|
+
assert @event.fire(@object)
|
564
|
+
end
|
565
|
+
|
566
|
+
def test_should_change_the_current_state
|
567
|
+
@event.fire(@object)
|
568
|
+
assert_equal 'idling', @object.state
|
569
|
+
end
|
570
|
+
end
|
571
|
+
|
572
|
+
begin
|
573
|
+
# Load library
|
574
|
+
require 'rubygems'
|
575
|
+
require 'graphviz'
|
576
|
+
|
577
|
+
class EventDrawingTest < Test::Unit::TestCase
|
578
|
+
def setup
|
579
|
+
states = [:parked, :idling, :first_gear]
|
580
|
+
|
581
|
+
@machine = StateMachine::Machine.new(Class.new, :initial => :parked)
|
582
|
+
@machine.other_states(*states)
|
583
|
+
|
584
|
+
graph = GraphViz.new('G')
|
585
|
+
states.each {|state| graph.add_node(state.to_s)}
|
586
|
+
|
587
|
+
@event = StateMachine::Event.new(@machine , :park)
|
588
|
+
@event.transition :parked => :idling
|
589
|
+
@event.transition :first_gear => :parked
|
590
|
+
@event.transition :except_from => :parked, :to => :parked
|
591
|
+
|
592
|
+
@edges = @event.draw(graph)
|
593
|
+
end
|
594
|
+
|
595
|
+
def test_should_generate_edges_for_each_transition
|
596
|
+
assert_equal 4, @edges.size
|
597
|
+
end
|
598
|
+
|
599
|
+
def test_should_use_event_name_for_edge_label
|
600
|
+
assert_equal 'park', @edges.first['label']
|
601
|
+
end
|
602
|
+
end
|
603
|
+
rescue LoadError
|
604
|
+
$stderr.puts 'Skipping GraphViz StateMachine::Event tests. `gem install ruby-graphviz` and try again.'
|
605
|
+
end
|