state_machine 0.4.3 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +17 -0
- data/LICENSE +1 -1
- data/README.rdoc +54 -84
- data/Rakefile +1 -1
- data/examples/Car_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/traffic_light.rb +9 -0
- data/examples/vehicle.rb +35 -0
- data/lib/state_machine.rb +65 -52
- data/lib/state_machine/assertions.rb +1 -1
- data/lib/state_machine/callback.rb +13 -9
- data/lib/state_machine/eval_helpers.rb +4 -3
- data/lib/state_machine/event.rb +51 -33
- data/lib/state_machine/extensions.rb +2 -2
- data/lib/state_machine/guard.rb +47 -41
- data/lib/state_machine/integrations.rb +67 -0
- data/lib/state_machine/integrations/active_record.rb +62 -36
- data/lib/state_machine/integrations/active_record/observer.rb +41 -0
- data/lib/state_machine/integrations/data_mapper.rb +23 -37
- data/lib/state_machine/integrations/data_mapper/observer.rb +23 -9
- data/lib/state_machine/integrations/sequel.rb +23 -24
- data/lib/state_machine/machine.rb +380 -277
- data/lib/state_machine/node_collection.rb +142 -0
- data/lib/state_machine/state.rb +114 -69
- data/lib/state_machine/state_collection.rb +38 -0
- data/lib/state_machine/transition.rb +36 -17
- data/test/active_record.log +2940 -85664
- data/test/functional/state_machine_test.rb +49 -53
- data/test/sequel.log +747 -11990
- data/test/unit/assertions_test.rb +2 -1
- data/test/unit/callback_test.rb +14 -12
- data/test/unit/eval_helpers_test.rb +25 -6
- data/test/unit/event_test.rb +144 -124
- data/test/unit/guard_test.rb +118 -140
- data/test/unit/integrations/active_record_test.rb +102 -68
- data/test/unit/integrations/data_mapper_test.rb +48 -37
- data/test/unit/integrations/sequel_test.rb +34 -25
- data/test/unit/integrations_test.rb +42 -0
- data/test/unit/machine_test.rb +460 -531
- data/test/unit/node_collection_test.rb +208 -0
- data/test/unit/state_collection_test.rb +167 -0
- data/test/unit/state_machine_test.rb +1 -1
- data/test/unit/state_test.rb +223 -200
- data/test/unit/transition_test.rb +81 -46
- metadata +17 -3
- data/test/data_mapper.log +0 -30860
data/CHANGELOG.rdoc
CHANGED
@@ -1,5 +1,22 @@
|
|
1
1
|
== master
|
2
2
|
|
3
|
+
== 0.5.0 / 2008-01-11
|
4
|
+
|
5
|
+
* Add to_name and from_name to transition objects
|
6
|
+
* Add nicely formatted #inspect for transitions
|
7
|
+
* Fix ActiveRecord integrations failing when the database doesn't exist yet
|
8
|
+
* Fix states not being drawn in GraphViz graphs in the correct order
|
9
|
+
* Add nicely formatted #inspect for states and events
|
10
|
+
* Simplify machine context-switching
|
11
|
+
* Store events/states in enumerable node collections
|
12
|
+
* No longer allow subclasses to change the integration
|
13
|
+
* Move fire! action logic into the Event class (no longer calls fire action on the object)
|
14
|
+
* Allow states in subclasses to have different values
|
15
|
+
* Recommend that all states be referenced as symbols instead of strings
|
16
|
+
* All states must now be named (and can be associated with other value types)
|
17
|
+
* Add support for customizing the actual stored value for a state
|
18
|
+
* Add compatibility with Ruby 1.9+
|
19
|
+
|
3
20
|
== 0.4.3 / 2008-12-28
|
4
21
|
|
5
22
|
* Allow dm-observer integration to be optional
|
data/LICENSE
CHANGED
data/README.rdoc
CHANGED
@@ -24,7 +24,7 @@ Source
|
|
24
24
|
== Description
|
25
25
|
|
26
26
|
State machines make it dead-simple to manage the behavior of a class. Too often,
|
27
|
-
the
|
27
|
+
the state of an object is kept by creating multiple boolean attributes and
|
28
28
|
deciding how to behave based on the values. This can become cumbersome and
|
29
29
|
difficult to maintain when the complexity of your class starts to increase.
|
30
30
|
|
@@ -41,13 +41,15 @@ Some brief, high-level features include:
|
|
41
41
|
* ActiveRecord integration
|
42
42
|
* DataMapper integration
|
43
43
|
* Sequel integration
|
44
|
-
* States of any data type
|
45
44
|
* State predicates
|
46
45
|
* State-driven behavior
|
46
|
+
* State values of any data type
|
47
|
+
* Dynamically-generated state values
|
48
|
+
* Inheritance
|
47
49
|
* GraphViz visualization creator
|
48
50
|
|
49
51
|
Examples of the usage patterns for some of the above features are shown below.
|
50
|
-
You can find more detailed documentation in the actual API.
|
52
|
+
You can find much more detailed documentation in the actual API.
|
51
53
|
|
52
54
|
== Usage
|
53
55
|
|
@@ -59,79 +61,83 @@ Below is an example of many of the features offered by this plugin, including:
|
|
59
61
|
* Transition callbacks
|
60
62
|
* Conditional transitions
|
61
63
|
* State-driven behavior
|
64
|
+
* Customized state values
|
62
65
|
|
63
66
|
Class definition:
|
64
67
|
|
65
68
|
class Vehicle
|
66
69
|
attr_accessor :seatbelt_on
|
67
70
|
|
68
|
-
state_machine :state, :initial =>
|
69
|
-
before_transition :from =>
|
70
|
-
after_transition :on =>
|
71
|
-
after_transition :on =>
|
72
|
-
after_transition :to =>
|
71
|
+
state_machine :state, :initial => :parked do
|
72
|
+
before_transition :from => [:parked, :idling], :do => :put_on_seatbelt
|
73
|
+
after_transition :on => :crash, :do => :tow
|
74
|
+
after_transition :on => :repair, :do => :fix
|
75
|
+
after_transition :to => :parked do |vehicle, transition|
|
73
76
|
vehicle.seatbelt_on = false
|
74
77
|
end
|
75
78
|
|
76
79
|
event :park do
|
77
|
-
transition :to =>
|
80
|
+
transition :to => :parked, :from => [:idling, :first_gear]
|
78
81
|
end
|
79
82
|
|
80
83
|
event :ignite do
|
81
|
-
transition :to =>
|
82
|
-
transition :to =>
|
84
|
+
transition :to => :stalled, :from => :stalled
|
85
|
+
transition :to => :idling, :from => :parked
|
83
86
|
end
|
84
87
|
|
85
88
|
event :idle do
|
86
|
-
transition :to =>
|
89
|
+
transition :to => :idling, :from => :first_gear
|
87
90
|
end
|
88
91
|
|
89
92
|
event :shift_up do
|
90
|
-
transition :to =>
|
91
|
-
transition :to =>
|
92
|
-
transition :to =>
|
93
|
+
transition :to => :first_gear, :from => :idling
|
94
|
+
transition :to => :second_gear, :from => :first_gear
|
95
|
+
transition :to => :third_gear, :from => :second_gear
|
93
96
|
end
|
94
97
|
|
95
98
|
event :shift_down do
|
96
|
-
transition :to =>
|
97
|
-
transition :to =>
|
99
|
+
transition :to => :second_gear, :from => :third_gear
|
100
|
+
transition :to => :first_gear, :from => :second_gear
|
98
101
|
end
|
99
102
|
|
100
103
|
event :crash do
|
101
|
-
transition :to =>
|
104
|
+
transition :to => :stalled, :from => [:first_gear, :second_gear, :third_gear], :unless => :auto_shop_busy?
|
102
105
|
end
|
103
106
|
|
104
107
|
event :repair do
|
105
|
-
transition :to =>
|
108
|
+
transition :to => :parked, :from => :stalled, :if => :auto_shop_busy?
|
106
109
|
end
|
107
110
|
|
108
|
-
state
|
111
|
+
state :parked do
|
109
112
|
def speed
|
110
113
|
0
|
111
114
|
end
|
112
115
|
end
|
113
116
|
|
114
|
-
state
|
117
|
+
state :idling, :first_gear do
|
115
118
|
def speed
|
116
119
|
10
|
117
120
|
end
|
118
121
|
end
|
119
122
|
|
120
|
-
state
|
123
|
+
state :second_gear do
|
121
124
|
def speed
|
122
125
|
20
|
123
126
|
end
|
124
127
|
end
|
125
128
|
end
|
126
129
|
|
127
|
-
state_machine :hood_state, :initial =>
|
130
|
+
state_machine :hood_state, :initial => :closed, :namespace => 'hood' do
|
128
131
|
event :open do
|
129
|
-
transition :to =>
|
132
|
+
transition :to => :opened
|
130
133
|
end
|
131
134
|
|
132
135
|
event :close do
|
133
|
-
transition :to =>
|
136
|
+
transition :to => :closed
|
134
137
|
end
|
138
|
+
|
139
|
+
state :opened, :value => 1
|
140
|
+
state :closed, :value => 0
|
135
141
|
end
|
136
142
|
|
137
143
|
def initialize
|
@@ -160,9 +166,11 @@ Using the above class as an example, you can interact with the state machine
|
|
160
166
|
like so:
|
161
167
|
|
162
168
|
vehicle = Vehicle.new # => #<Vehicle:0xb7cf4eac @state="parked", @seatbelt_on=false>
|
169
|
+
vehicle.state # => "parked"
|
170
|
+
vehicle.state_name # => :parked
|
163
171
|
vehicle.parked? # => true
|
164
172
|
vehicle.can_ignite? # => true
|
165
|
-
vehicle.next_ignite_transition # => #<StateMachine::Transition
|
173
|
+
vehicle.next_ignite_transition # => #<StateMachine::Transition attribute=:state event=:ignite from="parked" from_name=:parked to="idling" to_name=:idling>
|
166
174
|
vehicle.speed # => 0
|
167
175
|
|
168
176
|
vehicle.ignite # => true
|
@@ -180,15 +188,20 @@ like so:
|
|
180
188
|
vehicle # => #<Vehicle:0xb7cf4eac @state="second_gear", @seatbelt_on=true>
|
181
189
|
|
182
190
|
# The bang (!) operator can raise exceptions if the event fails
|
183
|
-
vehicle.park! # => StateMachine::InvalidTransition: Cannot transition via :park from
|
191
|
+
vehicle.park! # => StateMachine::InvalidTransition: Cannot transition state via :park from :second_gear
|
184
192
|
|
185
193
|
# Generic state predicates can raise exceptions if the value does not exist
|
186
|
-
vehicle.state?(
|
187
|
-
vehicle.state?(
|
194
|
+
vehicle.state?(:parked) # => true
|
195
|
+
vehicle.state?(:invalid) # => ArgumentError: :invalid is an invalid name
|
188
196
|
|
189
197
|
# Namespaced machines have uniquely-generated methods
|
198
|
+
vehicle.hood_state # => 0
|
199
|
+
vehicle.hood_state_name # => :closed
|
200
|
+
|
190
201
|
vehicle.can_open_hood? # => true
|
191
202
|
vehicle.open_hood # => true
|
203
|
+
vehicle.hood_state # => 1
|
204
|
+
vehicle.hood_state_name # => :opened
|
192
205
|
vehicle.can_close_hood? # => true
|
193
206
|
|
194
207
|
vehicle.hood_opened? # => true
|
@@ -218,14 +231,14 @@ The ActiveRecord integration adds support for database transactions, automatical
|
|
218
231
|
saving the record, named scopes, and observers. For example,
|
219
232
|
|
220
233
|
class Vehicle < ActiveRecord::Base
|
221
|
-
state_machine :initial =>
|
222
|
-
before_transition :to =>
|
223
|
-
after_transition :to =>
|
234
|
+
state_machine :initial => :parked do
|
235
|
+
before_transition :to => :idling, :do => :put_on_seatbelt
|
236
|
+
after_transition :to => :parked do |vehicle, transition|
|
224
237
|
vehicle.seatbelt = 'off'
|
225
238
|
end
|
226
239
|
|
227
240
|
event :ignite do
|
228
|
-
transition :to =>
|
241
|
+
transition :to => :idling, :from => :parked
|
229
242
|
end
|
230
243
|
end
|
231
244
|
|
@@ -249,45 +262,6 @@ saving the record, named scopes, and observers. For example,
|
|
249
262
|
For more information about the various behaviors added for ActiveRecord state
|
250
263
|
machines, see StateMachine::Integrations::ActiveRecord.
|
251
264
|
|
252
|
-
==== With enumerations
|
253
|
-
|
254
|
-
Using the acts_as_enumeration[http://github.com/pluginaweek/acts_as_enumeration] plugin
|
255
|
-
with an ActiveRecord integration, states can be transparently stored using
|
256
|
-
record ids in the database like so:
|
257
|
-
|
258
|
-
class VehicleState < ActiveRecord::Base
|
259
|
-
acts_as_enumeration
|
260
|
-
|
261
|
-
create :id => 1, :name => 'parked'
|
262
|
-
create :id => 2, :name => 'idling'
|
263
|
-
...
|
264
|
-
end
|
265
|
-
|
266
|
-
class Vehicle < ActiveRecord::Base
|
267
|
-
belongs_to :state, :class_name => 'VehicleState'
|
268
|
-
|
269
|
-
state_machine :state, :initial => 'parked' do
|
270
|
-
...
|
271
|
-
|
272
|
-
event :park do
|
273
|
-
transition :to => 'parked', :from => %w(idling first_gear)
|
274
|
-
end
|
275
|
-
end
|
276
|
-
|
277
|
-
...
|
278
|
-
end
|
279
|
-
|
280
|
-
Notice that the state machine definition remains *exactly* the same. However,
|
281
|
-
when interacting with the records, the actual state will be stored using the
|
282
|
-
identifiers defined for the enumeration:
|
283
|
-
|
284
|
-
vehicle = Vehicle.create # => #<Vehicle id: 1, seatbelt_on: false, state_id: 1>
|
285
|
-
vehicle.ignite # => true
|
286
|
-
vehicle # => #<Vehicle id: 1, seatbelt_on: true, state_id: 2>
|
287
|
-
|
288
|
-
This allows states to take on more complex functionality other than just being
|
289
|
-
a string value.
|
290
|
-
|
291
265
|
=== DataMapper
|
292
266
|
|
293
267
|
Like the ActiveRecord integration, the DataMapper integration adds support for
|
@@ -300,14 +274,14 @@ callbacks, and observers. For example,
|
|
300
274
|
property :id, Serial
|
301
275
|
property :state, String
|
302
276
|
|
303
|
-
state_machine :initial =>
|
304
|
-
before_transition :to =>
|
305
|
-
after_transition :to =>
|
277
|
+
state_machine :initial => :parked do
|
278
|
+
before_transition :to => :idling, :do => :put_on_seatbelt
|
279
|
+
after_transition :to => :parked do |transition|
|
306
280
|
self.seatbelt = 'off' # self is the record
|
307
281
|
end
|
308
282
|
|
309
283
|
event :ignite do
|
310
|
-
transition :to =>
|
284
|
+
transition :to => :idling, :from => :parked
|
311
285
|
end
|
312
286
|
end
|
313
287
|
|
@@ -345,14 +319,14 @@ database transactions, automatically saving the record, named scopes, and
|
|
345
319
|
callbacks. For example,
|
346
320
|
|
347
321
|
class Vehicle < Sequel::Model
|
348
|
-
state_machine :initial =>
|
349
|
-
before_transition :to =>
|
350
|
-
after_transition :to =>
|
322
|
+
state_machine :initial => :parked do
|
323
|
+
before_transition :to => :idling, :do => :put_on_seatbelt
|
324
|
+
after_transition :to => :parked do |transition|
|
351
325
|
self.seatbelt = 'off' # self is the record
|
352
326
|
end
|
353
327
|
|
354
328
|
event :ignite do
|
355
|
-
transition :to =>
|
329
|
+
transition :to => :idling, :from => :parked
|
356
330
|
end
|
357
331
|
end
|
358
332
|
|
@@ -441,7 +415,3 @@ dependencies are listed below.
|
|
441
415
|
* ActiveRecord[http://rubyonrails.org] integration: 2.1.0 or later
|
442
416
|
* DataMapper[http://datamapper.org] integration: 0.9.0 or later
|
443
417
|
* Sequel[http://sequel.rubyforge.org] integration: 2.8.0 or later
|
444
|
-
|
445
|
-
== References
|
446
|
-
|
447
|
-
* acts_as_enumeration[http://github.com/pluginaweek/acts_as_enumeration]
|
data/Rakefile
CHANGED
@@ -5,7 +5,7 @@ require 'rake/contrib/sshpublisher'
|
|
5
5
|
|
6
6
|
spec = Gem::Specification.new do |s|
|
7
7
|
s.name = 'state_machine'
|
8
|
-
s.version = '0.
|
8
|
+
s.version = '0.5.0'
|
9
9
|
s.platform = Gem::Platform::RUBY
|
10
10
|
s.summary = 'Adds support for creating state machines for attributes on any Ruby class'
|
11
11
|
|
data/examples/Car_state.png
CHANGED
Binary file
|
data/examples/Vehicle_state.png
CHANGED
Binary file
|
data/examples/car.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
class Car < Vehicle
|
2
|
+
state_machine do
|
3
|
+
event :reverse do
|
4
|
+
transition :to => :backing_up, :from => [:parked, :idling, :first_gear]
|
5
|
+
end
|
6
|
+
|
7
|
+
event :park do
|
8
|
+
transition :to => :parked, :from => :backing_up
|
9
|
+
end
|
10
|
+
|
11
|
+
event :idle do
|
12
|
+
transition :to => :idling, :from => :backing_up
|
13
|
+
end
|
14
|
+
|
15
|
+
event :shift_up do
|
16
|
+
transition :to => :first_gear, :from => :backing_up
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/examples/vehicle.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
class Vehicle
|
2
|
+
state_machine :initial => :parked do
|
3
|
+
event :park do
|
4
|
+
transition :to => :parked, :from => [:idling, :first_gear]
|
5
|
+
end
|
6
|
+
|
7
|
+
event :ignite do
|
8
|
+
transition :to => :stalled, :from => :stalled
|
9
|
+
transition :to => :idling, :from => :parked
|
10
|
+
end
|
11
|
+
|
12
|
+
event :idle do
|
13
|
+
transition :to => :idling, :from => :first_gear
|
14
|
+
end
|
15
|
+
|
16
|
+
event :shift_up do
|
17
|
+
transition :to => :first_gear, :from => :idling
|
18
|
+
transition :to => :second_gear, :from => :first_gear
|
19
|
+
transition :to => :third_gear, :from => :second_gear
|
20
|
+
end
|
21
|
+
|
22
|
+
event :shift_down do
|
23
|
+
transition :to => :second_gear, :from => :third_gear
|
24
|
+
transition :to => :first_gear, :from => :second_gear
|
25
|
+
end
|
26
|
+
|
27
|
+
event :crash do
|
28
|
+
transition :to => :stalled, :from => [:first_gear, :second_gear, :third_gear]
|
29
|
+
end
|
30
|
+
|
31
|
+
event :repair do
|
32
|
+
transition :to => :parked, :from => :stalled
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/state_machine.rb
CHANGED
@@ -6,16 +6,27 @@ require 'state_machine/machine'
|
|
6
6
|
module StateMachine
|
7
7
|
module MacroMethods
|
8
8
|
# Creates a new state machine for the given attribute. The default
|
9
|
-
# attribute, if not specified, is
|
9
|
+
# attribute, if not specified, is <tt>:state</tt>.
|
10
10
|
#
|
11
11
|
# Configuration options:
|
12
|
-
# *
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
# *
|
17
|
-
#
|
18
|
-
#
|
12
|
+
# * <tt>:initial</tt> - The initial state of the attribute. This can be a
|
13
|
+
# static state or a lambda block which will be evaluated at runtime
|
14
|
+
# (e.g. lambda {|vehicle| vehicle.speed == 0 ? :parked : :idling}).
|
15
|
+
# Default is nil.
|
16
|
+
# * <tt>:action</tt> - The action to invoke when an object transitions.
|
17
|
+
# Default is nil unless otherwise specified by the configured integration.
|
18
|
+
# * <tt>:plural</tt> - The pluralized name of the attribute. By default,
|
19
|
+
# this will attempt to call +pluralize+ on the attribute, otherwise
|
20
|
+
# an "s" is appended. This is used for generating scopes.
|
21
|
+
# * <tt>:namespace</tt> - The name to use for namespacing all generated
|
22
|
+
# instance methods (e.g. "heater" would generate :turn_on_heater and
|
23
|
+
# :turn_off_header for the :turn_on/:turn_off events). Default is nil.
|
24
|
+
# * <tt>:integration</tt> - The name of the integration to use for adding
|
25
|
+
# library-specific behavior to the machine. Built-in integrations include
|
26
|
+
# :data_mapper, :active_record, and :sequel. By default, this is
|
27
|
+
# determined automatically.
|
28
|
+
#
|
29
|
+
# This also expects a block which will be used to actually configure the
|
19
30
|
# states, events and transitions for the state machine. *Note* that this
|
20
31
|
# block will be executed within the context of the state machine. As a
|
21
32
|
# result, you will not be able to access any class methods unless you refer
|
@@ -36,7 +47,7 @@ module StateMachine
|
|
36
47
|
# end
|
37
48
|
# end
|
38
49
|
#
|
39
|
-
# The above example will define a state machine for the attribute
|
50
|
+
# The above example will define a state machine for the +state+ attribute
|
40
51
|
# on the class. Every vehicle will start without an initial state.
|
41
52
|
#
|
42
53
|
# With a custom attribute:
|
@@ -50,15 +61,15 @@ module StateMachine
|
|
50
61
|
# With a static initial state:
|
51
62
|
#
|
52
63
|
# class Vehicle
|
53
|
-
# state_machine :status, :initial =>
|
64
|
+
# state_machine :status, :initial => :parked do
|
54
65
|
# ...
|
55
66
|
# end
|
56
67
|
# end
|
57
68
|
#
|
58
69
|
# With a dynamic initial state:
|
59
70
|
#
|
60
|
-
# class
|
61
|
-
# state_machine :status, :initial => lambda {|
|
71
|
+
# class Vehicle
|
72
|
+
# state_machine :status, :initial => lambda {|vehicle| vehicle.speed == 0 ? :parked : :idling} do
|
62
73
|
# ...
|
63
74
|
# end
|
64
75
|
# end
|
@@ -69,13 +80,15 @@ module StateMachine
|
|
69
80
|
# of the machine. In order to access this value and modify it during
|
70
81
|
# transitions, a reader/writer must be available. The following methods
|
71
82
|
# will be automatically generated if they are not already defined
|
72
|
-
# (assuming the attribute is called
|
83
|
+
# (assuming the attribute is called +state+):
|
73
84
|
# * <tt>state</tt> - Gets the current value for the attribute
|
74
85
|
# * <tt>state=(value)</tt> - Sets the current value for the attribute
|
75
|
-
# * <tt>state?(
|
86
|
+
# * <tt>state?(name)</tt> - Checks the given state name against the current
|
87
|
+
# state. If the name is not a known state, then an ArgumentError is raised.
|
88
|
+
# * <tt>state_name</tt> - Gets the name of the state for the current value
|
76
89
|
#
|
77
|
-
# For example, the following machine definition will not generate
|
78
|
-
#
|
90
|
+
# For example, the following machine definition will not generate the reader
|
91
|
+
# or writer methods since the class has already defined an attribute
|
79
92
|
# accessor:
|
80
93
|
#
|
81
94
|
# class Vehicle
|
@@ -86,7 +99,7 @@ module StateMachine
|
|
86
99
|
# end
|
87
100
|
# end
|
88
101
|
#
|
89
|
-
# On the other hand, the following state machine will define both a
|
102
|
+
# On the other hand, the following state machine will define *both* a
|
90
103
|
# reader and writer method, which is functionally equivalent to the
|
91
104
|
# example above:
|
92
105
|
#
|
@@ -106,13 +119,13 @@ module StateMachine
|
|
106
119
|
# For example,
|
107
120
|
#
|
108
121
|
# class Vehicle
|
109
|
-
# state_machine :state, :initial =>
|
122
|
+
# state_machine :state, :initial => :parked do
|
110
123
|
# ...
|
111
124
|
# end
|
112
125
|
# end
|
113
126
|
#
|
114
|
-
#
|
115
|
-
#
|
127
|
+
# vehicle = Vehicle.new # => #<Vehicle:0xb7c8dbf8 @state="parked">
|
128
|
+
# vehicle.state # => "parked"
|
116
129
|
#
|
117
130
|
# In the above example, no +initialize+ method is defined. As a result,
|
118
131
|
# the default behavior of initializing the state machine attributes is used.
|
@@ -120,7 +133,7 @@ module StateMachine
|
|
120
133
|
# In the following example, a custom +initialize+ method is defined:
|
121
134
|
#
|
122
135
|
# class Vehicle
|
123
|
-
# state_machine :state, :initial =>
|
136
|
+
# state_machine :state, :initial => :parked do
|
124
137
|
# ...
|
125
138
|
# end
|
126
139
|
#
|
@@ -128,8 +141,8 @@ module StateMachine
|
|
128
141
|
# end
|
129
142
|
# end
|
130
143
|
#
|
131
|
-
#
|
132
|
-
#
|
144
|
+
# vehicle = Vehicle.new # => #<Vehicle:0xb7c77678>
|
145
|
+
# vehicle.state # => nil
|
133
146
|
#
|
134
147
|
# Since the +initialize+ method is defined, the state machine attributes
|
135
148
|
# never get initialized. In order to ensure that all initialization hooks
|
@@ -137,7 +150,7 @@ module StateMachine
|
|
137
150
|
# like so:
|
138
151
|
#
|
139
152
|
# class Vehicle
|
140
|
-
# state_machine :state, :initial =>
|
153
|
+
# state_machine :state, :initial => :parked do
|
141
154
|
# ...
|
142
155
|
# end
|
143
156
|
#
|
@@ -147,19 +160,19 @@ module StateMachine
|
|
147
160
|
# end
|
148
161
|
# end
|
149
162
|
#
|
150
|
-
#
|
151
|
-
#
|
163
|
+
# vehicle = Vehicle.new # => #<Vehicle:0xb7c8dbf8 @state="parked">
|
164
|
+
# vehicle.state # => "parked"
|
152
165
|
#
|
153
|
-
# Because of the way the inclusion of modules works in Ruby, calling
|
154
|
-
# will not only call the superclass's +initialize+, but
|
155
|
-
# all included modules. This allows the original state
|
156
|
-
# called properly.
|
166
|
+
# Because of the way the inclusion of modules works in Ruby, calling
|
167
|
+
# <tt>super()</tt> will not only call the superclass's +initialize+, but
|
168
|
+
# also +initialize+ on all included modules. This allows the original state
|
169
|
+
# machine hook to get called properly.
|
157
170
|
#
|
158
171
|
# If you want to avoid calling the superclass's constructor, but still want
|
159
172
|
# to initialize the state machine attributes:
|
160
173
|
#
|
161
174
|
# class Vehicle
|
162
|
-
# state_machine :state, :initial =>
|
175
|
+
# state_machine :state, :initial => :parked do
|
163
176
|
# ...
|
164
177
|
# end
|
165
178
|
#
|
@@ -169,24 +182,24 @@ module StateMachine
|
|
169
182
|
# end
|
170
183
|
# end
|
171
184
|
#
|
172
|
-
#
|
173
|
-
#
|
185
|
+
# vehicle = Vehicle.new # => #<Vehicle:0xb7c8dbf8 @state="parked">
|
186
|
+
# vehicle.state # => "parked"
|
174
187
|
#
|
175
188
|
# == States
|
176
189
|
#
|
177
190
|
# All of the valid states for the machine are automatically tracked based
|
178
191
|
# on the events, transitions, and callbacks defined for the machine. If
|
179
192
|
# there are additional states that are never referenced, these should be
|
180
|
-
# explicitly added using the StateMachine::Machine#
|
181
|
-
#
|
193
|
+
# explicitly added using the StateMachine::Machine#state or
|
194
|
+
# StateMachine::Machine#other_states helpers.
|
182
195
|
#
|
183
|
-
# When
|
184
|
-
#
|
196
|
+
# When a new state is defined, a predicate method for that state is
|
197
|
+
# generated on the class. For example,
|
185
198
|
#
|
186
199
|
# class Vehicle
|
187
|
-
# state_machine :initial =>
|
200
|
+
# state_machine :initial => :parked do
|
188
201
|
# event :ignite do
|
189
|
-
# transition :to =>
|
202
|
+
# transition :to => :idling
|
190
203
|
# end
|
191
204
|
# end
|
192
205
|
# end
|
@@ -227,23 +240,23 @@ module StateMachine
|
|
227
240
|
# For example,
|
228
241
|
#
|
229
242
|
# class Vehicle
|
230
|
-
# state_machine :heater_state, :initial =>
|
243
|
+
# state_machine :heater_state, :initial => :off :namespace => 'heater' do
|
231
244
|
# event :turn_on do
|
232
|
-
# transition :to =>
|
245
|
+
# transition :to => :on
|
233
246
|
# end
|
234
247
|
#
|
235
248
|
# event :turn_off do
|
236
|
-
# transition :to =>
|
249
|
+
# transition :to => :off
|
237
250
|
# end
|
238
251
|
# end
|
239
252
|
#
|
240
|
-
# state_machine :hood_state, :initial =>
|
253
|
+
# state_machine :hood_state, :initial => :closed, :namespace => 'hood' do
|
241
254
|
# event :open do
|
242
|
-
# transition :to =>
|
255
|
+
# transition :to => :opened
|
243
256
|
# end
|
244
257
|
#
|
245
258
|
# event :close do
|
246
|
-
# transition :to =>
|
259
|
+
# transition :to => :closed
|
247
260
|
# end
|
248
261
|
# end
|
249
262
|
# end
|
@@ -275,19 +288,19 @@ module StateMachine
|
|
275
288
|
#
|
276
289
|
# For integrations that support it, a group of default scope filters will
|
277
290
|
# be automatically created for assisting in finding objects that have the
|
278
|
-
# attribute set to a given
|
291
|
+
# attribute set to the value for a given set of states.
|
279
292
|
#
|
280
293
|
# For example,
|
281
294
|
#
|
282
|
-
# Vehicle.with_state(
|
283
|
-
# Vehicle.with_states(
|
295
|
+
# Vehicle.with_state(:parked) # => Finds all vehicles where the state is parked
|
296
|
+
# Vehicle.with_states(:parked, :idling) # => Finds all vehicles where the state is either parked or idling
|
284
297
|
#
|
285
|
-
# Vehicle.without_state(
|
286
|
-
# Vehicle.without_states(
|
298
|
+
# Vehicle.without_state(:parked) # => Finds all vehicles where the state is *not* parked
|
299
|
+
# Vehicle.without_states(:parked, :idling) # => Finds all vehicles where the state is *not* parked or idling
|
287
300
|
#
|
288
301
|
# *Note* that if class methods already exist with those names (i.e.
|
289
|
-
#
|
290
|
-
#
|
302
|
+
# :with_state, :with_states, :without_state, or :without_states), then a
|
303
|
+
# scope will not be defined for that name.
|
291
304
|
#
|
292
305
|
# See StateMachine::Machine for more information about using
|
293
306
|
# integrations and the individual integration docs for information about
|