state_machine 0.4.3 → 0.5.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 +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
|