state_machine 1.1.0 → 1.1.1

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.
Files changed (77) hide show
  1. data/.yardopts +0 -1
  2. data/CHANGELOG.md +7 -0
  3. data/LICENSE +1 -1
  4. data/README.md +97 -5
  5. data/gemfiles/active_model-3.0.0.gemfile.lock +1 -1
  6. data/gemfiles/active_model-3.0.5.gemfile.lock +1 -1
  7. data/gemfiles/active_model-3.1.1.gemfile.lock +1 -1
  8. data/gemfiles/active_record-2.0.0.gemfile.lock +1 -1
  9. data/gemfiles/active_record-2.0.5.gemfile.lock +1 -1
  10. data/gemfiles/active_record-2.1.0.gemfile.lock +1 -1
  11. data/gemfiles/active_record-2.1.2.gemfile.lock +1 -1
  12. data/gemfiles/active_record-2.2.3.gemfile.lock +1 -1
  13. data/gemfiles/active_record-2.3.12.gemfile.lock +1 -1
  14. data/gemfiles/active_record-3.0.0.gemfile.lock +1 -1
  15. data/gemfiles/active_record-3.0.5.gemfile.lock +1 -1
  16. data/gemfiles/active_record-3.1.1.gemfile.lock +1 -1
  17. data/gemfiles/data_mapper-0.10.2.gemfile.lock +1 -1
  18. data/gemfiles/data_mapper-0.9.11.gemfile.lock +1 -1
  19. data/gemfiles/data_mapper-0.9.4.gemfile.lock +1 -1
  20. data/gemfiles/data_mapper-0.9.7.gemfile.lock +1 -1
  21. data/gemfiles/data_mapper-1.0.0.gemfile.lock +1 -1
  22. data/gemfiles/data_mapper-1.0.1.gemfile.lock +1 -1
  23. data/gemfiles/data_mapper-1.0.2.gemfile.lock +1 -1
  24. data/gemfiles/data_mapper-1.1.0.gemfile.lock +1 -1
  25. data/gemfiles/data_mapper-1.2.0.gemfile.lock +1 -1
  26. data/gemfiles/default.gemfile.lock +1 -1
  27. data/gemfiles/graphviz-0.9.0.gemfile.lock +1 -1
  28. data/gemfiles/graphviz-0.9.21.gemfile.lock +1 -1
  29. data/gemfiles/graphviz-1.0.0.gemfile.lock +1 -1
  30. data/gemfiles/mongo_mapper-0.10.0.gemfile.lock +1 -1
  31. data/gemfiles/mongo_mapper-0.5.5.gemfile.lock +1 -1
  32. data/gemfiles/mongo_mapper-0.5.8.gemfile.lock +1 -1
  33. data/gemfiles/mongo_mapper-0.6.0.gemfile.lock +1 -1
  34. data/gemfiles/mongo_mapper-0.6.10.gemfile.lock +1 -1
  35. data/gemfiles/mongo_mapper-0.7.0.gemfile.lock +1 -1
  36. data/gemfiles/mongo_mapper-0.7.5.gemfile.lock +1 -1
  37. data/gemfiles/mongo_mapper-0.8.0.gemfile.lock +1 -1
  38. data/gemfiles/mongo_mapper-0.8.3.gemfile.lock +1 -1
  39. data/gemfiles/mongo_mapper-0.8.4.gemfile.lock +1 -1
  40. data/gemfiles/mongo_mapper-0.8.6.gemfile.lock +1 -1
  41. data/gemfiles/mongo_mapper-0.9.0.gemfile.lock +1 -1
  42. data/gemfiles/mongoid-2.0.0.gemfile.lock +1 -1
  43. data/gemfiles/mongoid-2.1.4.gemfile.lock +1 -1
  44. data/gemfiles/mongoid-2.2.4.gemfile.lock +1 -1
  45. data/gemfiles/mongoid-2.3.3.gemfile.lock +1 -1
  46. data/gemfiles/sequel-2.11.0.gemfile.lock +1 -1
  47. data/gemfiles/sequel-2.12.0.gemfile.lock +1 -1
  48. data/gemfiles/sequel-2.8.0.gemfile.lock +1 -1
  49. data/gemfiles/sequel-3.0.0.gemfile.lock +1 -1
  50. data/gemfiles/sequel-3.13.0.gemfile.lock +1 -1
  51. data/gemfiles/sequel-3.14.0.gemfile.lock +1 -1
  52. data/gemfiles/sequel-3.23.0.gemfile.lock +1 -1
  53. data/gemfiles/sequel-3.24.0.gemfile.lock +1 -1
  54. data/gemfiles/sequel-3.29.0.gemfile.lock +1 -1
  55. data/lib/state_machine.rb +2 -499
  56. data/lib/state_machine/core.rb +2 -0
  57. data/lib/state_machine/core_ext.rb +1 -0
  58. data/lib/state_machine/core_ext/class/state_machine.rb +5 -0
  59. data/lib/state_machine/event.rb +0 -5
  60. data/lib/state_machine/event_collection.rb +1 -1
  61. data/lib/state_machine/integrations/mongo_mapper.rb +1 -1
  62. data/lib/state_machine/integrations/mongoid.rb +1 -1
  63. data/lib/state_machine/machine.rb +12 -0
  64. data/lib/state_machine/macro_methods.rb +493 -0
  65. data/lib/state_machine/node_collection.rb +50 -18
  66. data/lib/state_machine/state.rb +0 -5
  67. data/lib/state_machine/state_collection.rb +1 -1
  68. data/lib/state_machine/version.rb +1 -1
  69. data/test/unit/event_collection_test.rb +34 -0
  70. data/test/unit/integrations/data_mapper_test.rb +38 -0
  71. data/test/unit/integrations/mongo_mapper_test.rb +28 -0
  72. data/test/unit/integrations/mongoid_test.rb +28 -0
  73. data/test/unit/machine_test.rb +15 -0
  74. data/test/unit/node_collection_test.rb +37 -9
  75. data/test/unit/state_collection_test.rb +38 -0
  76. data/test/unit/state_test.rb +1 -1
  77. metadata +11 -6
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /home/aaron/Projects/Personal/pluginaweek/state_machine
3
3
  specs:
4
- state_machine (1.0.3)
4
+ state_machine (1.1.0)
5
5
 
6
6
  GEM
7
7
  remote: http://www.rubygems.org/
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /home/aaron/Projects/Personal/pluginaweek/state_machine
3
3
  specs:
4
- state_machine (1.0.3)
4
+ state_machine (1.1.0)
5
5
 
6
6
  GEM
7
7
  remote: http://www.rubygems.org/
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /home/aaron/Projects/Personal/pluginaweek/state_machine
3
3
  specs:
4
- state_machine (1.0.3)
4
+ state_machine (1.1.0)
5
5
 
6
6
  GEM
7
7
  remote: http://www.rubygems.org/
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /home/aaron/Projects/Personal/pluginaweek/state_machine
3
3
  specs:
4
- state_machine (1.0.3)
4
+ state_machine (1.1.0)
5
5
 
6
6
  GEM
7
7
  remote: http://www.rubygems.org/
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /home/aaron/Projects/Personal/pluginaweek/state_machine
3
3
  specs:
4
- state_machine (1.0.3)
4
+ state_machine (1.1.0)
5
5
 
6
6
  GEM
7
7
  remote: http://www.rubygems.org/
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /home/aaron/Projects/Personal/pluginaweek/state_machine
3
3
  specs:
4
- state_machine (1.0.3)
4
+ state_machine (1.1.0)
5
5
 
6
6
  GEM
7
7
  remote: http://www.rubygems.org/
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /home/aaron/Projects/Personal/pluginaweek/state_machine
3
3
  specs:
4
- state_machine (1.0.3)
4
+ state_machine (1.1.0)
5
5
 
6
6
  GEM
7
7
  remote: http://www.rubygems.org/
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /home/aaron/Projects/Personal/pluginaweek/state_machine
3
3
  specs:
4
- state_machine (1.0.3)
4
+ state_machine (1.1.0)
5
5
 
6
6
  GEM
7
7
  remote: http://www.rubygems.org/
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /home/aaron/Projects/Personal/pluginaweek/state_machine
3
3
  specs:
4
- state_machine (1.0.3)
4
+ state_machine (1.1.0)
5
5
 
6
6
  GEM
7
7
  remote: http://www.rubygems.org/
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /home/aaron/Projects/Personal/pluginaweek/state_machine
3
3
  specs:
4
- state_machine (1.0.3)
4
+ state_machine (1.1.0)
5
5
 
6
6
  GEM
7
7
  remote: http://www.rubygems.org/
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /home/aaron/Projects/Personal/pluginaweek/state_machine
3
3
  specs:
4
- state_machine (1.0.3)
4
+ state_machine (1.1.0)
5
5
 
6
6
  GEM
7
7
  remote: http://www.rubygems.org/
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /home/aaron/Projects/Personal/pluginaweek/state_machine
3
3
  specs:
4
- state_machine (1.0.3)
4
+ state_machine (1.1.0)
5
5
 
6
6
  GEM
7
7
  remote: http://www.rubygems.org/
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /home/aaron/Projects/Personal/pluginaweek/state_machine
3
3
  specs:
4
- state_machine (1.0.3)
4
+ state_machine (1.1.0)
5
5
 
6
6
  GEM
7
7
  remote: http://www.rubygems.org/
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /home/aaron/Projects/Personal/pluginaweek/state_machine
3
3
  specs:
4
- state_machine (1.0.3)
4
+ state_machine (1.1.0)
5
5
 
6
6
  GEM
7
7
  remote: http://www.rubygems.org/
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /home/aaron/Projects/Personal/pluginaweek/state_machine
3
3
  specs:
4
- state_machine (1.0.3)
4
+ state_machine (1.1.0)
5
5
 
6
6
  GEM
7
7
  remote: http://www.rubygems.org/
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /home/aaron/Projects/Personal/pluginaweek/state_machine
3
3
  specs:
4
- state_machine (1.0.3)
4
+ state_machine (1.1.0)
5
5
 
6
6
  GEM
7
7
  remote: http://www.rubygems.org/
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /home/aaron/Projects/Personal/pluginaweek/state_machine
3
3
  specs:
4
- state_machine (1.0.3)
4
+ state_machine (1.1.0)
5
5
 
6
6
  GEM
7
7
  remote: http://www.rubygems.org/
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /home/aaron/Projects/Personal/pluginaweek/state_machine
3
3
  specs:
4
- state_machine (1.0.3)
4
+ state_machine (1.1.0)
5
5
 
6
6
  GEM
7
7
  remote: http://www.rubygems.org/
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /home/aaron/Projects/Personal/pluginaweek/state_machine
3
3
  specs:
4
- state_machine (1.0.3)
4
+ state_machine (1.1.0)
5
5
 
6
6
  GEM
7
7
  remote: http://www.rubygems.org/
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /home/aaron/Projects/Personal/pluginaweek/state_machine
3
3
  specs:
4
- state_machine (1.0.3)
4
+ state_machine (1.1.0)
5
5
 
6
6
  GEM
7
7
  remote: http://www.rubygems.org/
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /home/aaron/Projects/Personal/pluginaweek/state_machine
3
3
  specs:
4
- state_machine (1.0.3)
4
+ state_machine (1.1.0)
5
5
 
6
6
  GEM
7
7
  remote: http://www.rubygems.org/
@@ -1,499 +1,2 @@
1
- require 'state_machine/machine'
2
-
3
- # A state machine is a model of behavior composed of states, events, and
4
- # transitions. This helper adds support for defining this type of
5
- # functionality on any Ruby class.
6
- module StateMachine
7
- module MacroMethods
8
- # Creates a new state machine with the given name. The default name, if not
9
- # specified, is <tt>:state</tt>.
10
- #
11
- # Configuration options:
12
- # * <tt>:attribute</tt> - The name of the attribute to store the state value
13
- # in. By default, this is the same as the name of the machine.
14
- # * <tt>:initial</tt> - The initial state of the attribute. This can be a
15
- # static state or a lambda block which will be evaluated at runtime
16
- # (e.g. lambda {|vehicle| vehicle.speed == 0 ? :parked : :idling}).
17
- # Default is nil.
18
- # * <tt>:initialize</tt> - Whether to automatically initialize the attribute
19
- # by hooking into #initialize on the owner class. Default is true.
20
- # * <tt>:action</tt> - The instance method to invoke when an object
21
- # transitions. Default is nil unless otherwise specified by the
22
- # configured integration.
23
- # * <tt>:namespace</tt> - The name to use for namespacing all generated
24
- # state / event instance methods (e.g. "heater" would generate
25
- # :turn_on_heater and :turn_off_heater for the :turn_on/:turn_off events).
26
- # Default is nil.
27
- # * <tt>:integration</tt> - The name of the integration to use for adding
28
- # library-specific behavior to the machine. Built-in integrations
29
- # include :active_model, :active_record, :data_mapper, :mongo_mapper, and
30
- # :sequel. By default, this is determined automatically.
31
- #
32
- # Configuration options relevant to ORM integrations:
33
- # * <tt>:plural</tt> - The pluralized version of the name. By default, this
34
- # will attempt to call +pluralize+ on the name. If this method is not
35
- # available, an "s" is appended. This is used for generating scopes.
36
- # * <tt>:messages</tt> - The error messages to use when invalidating
37
- # objects due to failed transitions. Messages include:
38
- # * <tt>:invalid</tt>
39
- # * <tt>:invalid_event</tt>
40
- # * <tt>:invalid_transition</tt>
41
- # * <tt>:use_transactions</tt> - Whether transactions should be used when
42
- # firing events. Default is true unless otherwise specified by the
43
- # configured integration.
44
- #
45
- # This also expects a block which will be used to actually configure the
46
- # states, events and transitions for the state machine. *Note* that this
47
- # block will be executed within the context of the state machine. As a
48
- # result, you will not be able to access any class methods unless you refer
49
- # to them directly (i.e. specifying the class name).
50
- #
51
- # For examples on the types of state machine configurations and blocks, see
52
- # the section below.
53
- #
54
- # == Examples
55
- #
56
- # With the default name/attribute and no configuration:
57
- #
58
- # class Vehicle
59
- # state_machine do
60
- # event :park do
61
- # ...
62
- # end
63
- # end
64
- # end
65
- #
66
- # The above example will define a state machine named "state" that will
67
- # store the value in the +state+ attribute. Every vehicle will start
68
- # without an initial state.
69
- #
70
- # With a custom name / attribute:
71
- #
72
- # class Vehicle
73
- # state_machine :status, :attribute => :status_value do
74
- # ...
75
- # end
76
- # end
77
- #
78
- # With a static initial state:
79
- #
80
- # class Vehicle
81
- # state_machine :status, :initial => :parked do
82
- # ...
83
- # end
84
- # end
85
- #
86
- # With a dynamic initial state:
87
- #
88
- # class Vehicle
89
- # state_machine :status, :initial => lambda {|vehicle| vehicle.speed == 0 ? :parked : :idling} do
90
- # ...
91
- # end
92
- # end
93
- #
94
- # == Class Methods
95
- #
96
- # The following class methods will be automatically generated by the
97
- # state machine based on the *name* of the machine. Any existing methods
98
- # will not be overwritten.
99
- # * <tt>human_state_name(state)</tt> - Gets the humanized value for the
100
- # given state. This may be generated by internationalization libraries if
101
- # supported by the integration.
102
- # * <tt>human_state_event_name(event)</tt> - Gets the humanized value for
103
- # the given event. This may be generated by internationalization
104
- # libraries if supported by the integration.
105
- #
106
- # For example,
107
- #
108
- # class Vehicle
109
- # state_machine :state, :initial => :parked do
110
- # event :ignite do
111
- # transition :parked => :idling
112
- # end
113
- #
114
- # event :shift_up do
115
- # transition :idling => :first_gear
116
- # end
117
- # end
118
- # end
119
- #
120
- # Vehicle.human_state_name(:parked) # => "parked"
121
- # Vehicle.human_state_name(:first_gear) # => "first gear"
122
- # Vehicle.human_state_event_name(:park) # => "park"
123
- # Vehicle.human_state_event_name(:shift_up) # => "shift up"
124
- #
125
- # == Instance Methods
126
- #
127
- # The following instance methods will be automatically generated by the
128
- # state machine based on the *name* of the machine. Any existing methods
129
- # will not be overwritten.
130
- # * <tt>state</tt> - Gets the current value for the attribute
131
- # * <tt>state=(value)</tt> - Sets the current value for the attribute
132
- # * <tt>state?(name)</tt> - Checks the given state name against the current
133
- # state. If the name is not a known state, then an ArgumentError is raised.
134
- # * <tt>state_name</tt> - Gets the name of the state for the current value
135
- # * <tt>human_state_name</tt> - Gets the human-readable name of the state
136
- # for the current value
137
- # * <tt>state_events(requirements = {})</tt> - Gets the list of events that
138
- # can be fired on the current object's state (uses the *unqualified* event
139
- # names)
140
- # * <tt>state_transitions(requirements = {})</tt> - Gets the list of
141
- # transitions that can be made on the current object's state
142
- # * <tt>state_paths(requirements = {})</tt> - Gets the list of sequences of
143
- # transitions that can be run from the current object's state
144
- # * <tt>fire_state_event(name, *args)</tt> - Fires an arbitrary event with
145
- # the given argument list. This is essentially the same as calling the
146
- # actual event method itself.
147
- #
148
- # The <tt>state_events</tt>, <tt>state_transitions</tt>, and <tt>state_paths</tt>
149
- # helpers all take an optional set of requirements for determining what's
150
- # available for the current object. These requirements include:
151
- # * <tt>:from</tt> - One or more states to transition from. If none are
152
- # specified, then this will be the object's current state.
153
- # * <tt>:to</tt> - One or more states to transition to. If none are
154
- # specified, then this will match any to state.
155
- # * <tt>:on</tt> - One or more events to transition on. If none are
156
- # specified, then this will match any event.
157
- # * <tt>:guard</tt> - Whether to guard transitions with the if/unless
158
- # conditionals defined for each one. Default is true.
159
- #
160
- # For example,
161
- #
162
- # class Vehicle
163
- # state_machine :state, :initial => :parked do
164
- # event :ignite do
165
- # transition :parked => :idling
166
- # end
167
- #
168
- # event :park do
169
- # transition :idling => :parked
170
- # end
171
- # end
172
- # end
173
- #
174
- # vehicle = Vehicle.new
175
- # vehicle.state # => "parked"
176
- # vehicle.state_name # => :parked
177
- # vehicle.human_state_name # => "parked"
178
- # vehicle.state?(:parked) # => true
179
- #
180
- # # Changing state
181
- # vehicle.state = 'idling'
182
- # vehicle.state # => "idling"
183
- # vehicle.state_name # => :idling
184
- # vehicle.state?(:parked) # => false
185
- #
186
- # # Getting current event / transition availability
187
- # vehicle.state_events # => [:park]
188
- # vehicle.park # => true
189
- # vehicle.state_events # => [:ignite]
190
- # vehicle.state_events(:from => :idling) # => [:park]
191
- # vehicle.state_events(:to => :parked) # => []
192
- #
193
- # vehicle.state_transitions # => [#<StateMachine::Transition attribute=:state event=:ignite from="parked" from_name=:parked to="idling" to_name=:idling>]
194
- # vehicle.ignite # => true
195
- # vehicle.state_transitions # => [#<StateMachine::Transition attribute=:state event=:park from="idling" from_name=:idling to="parked" to_name=:parked>]
196
- #
197
- # vehicle.state_transitions(:on => :ignite) # => []
198
- #
199
- # # Getting current path availability
200
- # vehicle.state_paths # => [
201
- # # [#<StateMachine::Transition attribute=:state event=:park from="idling" from_name=:idling to="parked" to_name=:parked>,
202
- # # #<StateMachine::Transition attribute=:state event=:ignite from="parked" from_name=:parked to="idling" to_name=:idling>]
203
- # # ]
204
- # vehicle.state_paths(:guard => false) # =>
205
- # # [#<StateMachine::Transition attribute=:state event=:park from="idling" from_name=:idling to="parked" to_name=:parked>,
206
- # # #<StateMachine::Transition attribute=:state event=:ignite from="parked" from_name=:parked to="idling" to_name=:idling>]
207
- # # ]
208
- #
209
- # # Fire arbitrary events
210
- # vehicle.fire_state_event(:park) # => true
211
- #
212
- # == Attribute initialization
213
- #
214
- # For most classes, the initial values for state machine attributes are
215
- # automatically assigned when a new object is created. However, this
216
- # behavior will *not* work if the class defines an +initialize+ method
217
- # without properly calling +super+.
218
- #
219
- # For example,
220
- #
221
- # class Vehicle
222
- # state_machine :state, :initial => :parked do
223
- # ...
224
- # end
225
- # end
226
- #
227
- # vehicle = Vehicle.new # => #<Vehicle:0xb7c8dbf8 @state="parked">
228
- # vehicle.state # => "parked"
229
- #
230
- # In the above example, no +initialize+ method is defined. As a result,
231
- # the default behavior of initializing the state machine attributes is used.
232
- #
233
- # In the following example, a custom +initialize+ method is defined:
234
- #
235
- # class Vehicle
236
- # state_machine :state, :initial => :parked do
237
- # ...
238
- # end
239
- #
240
- # def initialize
241
- # end
242
- # end
243
- #
244
- # vehicle = Vehicle.new # => #<Vehicle:0xb7c77678>
245
- # vehicle.state # => nil
246
- #
247
- # Since the +initialize+ method is defined, the state machine attributes
248
- # never get initialized. In order to ensure that all initialization hooks
249
- # are called, the custom method *must* call +super+ without any arguments
250
- # like so:
251
- #
252
- # class Vehicle
253
- # state_machine :state, :initial => :parked do
254
- # ...
255
- # end
256
- #
257
- # def initialize(attributes = {})
258
- # ...
259
- # super()
260
- # end
261
- # end
262
- #
263
- # vehicle = Vehicle.new # => #<Vehicle:0xb7c8dbf8 @state="parked">
264
- # vehicle.state # => "parked"
265
- #
266
- # Because of the way the inclusion of modules works in Ruby, calling
267
- # <tt>super()</tt> will not only call the superclass's +initialize+, but
268
- # also +initialize+ on all included modules. This allows the original state
269
- # machine hook to get called properly.
270
- #
271
- # If you want to avoid calling the superclass's constructor, but still want
272
- # to initialize the state machine attributes:
273
- #
274
- # class Vehicle
275
- # state_machine :state, :initial => :parked do
276
- # ...
277
- # end
278
- #
279
- # def initialize(attributes = {})
280
- # ...
281
- # initialize_state_machines
282
- # end
283
- # end
284
- #
285
- # vehicle = Vehicle.new # => #<Vehicle:0xb7c8dbf8 @state="parked">
286
- # vehicle.state # => "parked"
287
- #
288
- # == States
289
- #
290
- # All of the valid states for the machine are automatically tracked based
291
- # on the events, transitions, and callbacks defined for the machine. If
292
- # there are additional states that are never referenced, these should be
293
- # explicitly added using the StateMachine::Machine#state or
294
- # StateMachine::Machine#other_states helpers.
295
- #
296
- # When a new state is defined, a predicate method for that state is
297
- # generated on the class. For example,
298
- #
299
- # class Vehicle
300
- # state_machine :initial => :parked do
301
- # event :ignite do
302
- # transition all => :idling
303
- # end
304
- # end
305
- # end
306
- #
307
- # ...will generate the following instance methods (assuming they're not
308
- # already defined in the class):
309
- # * <tt>parked?</tt>
310
- # * <tt>idling?</tt>
311
- #
312
- # Each predicate method will return true if it matches the object's
313
- # current state. Otherwise, it will return false.
314
- #
315
- # == Attribute access
316
- #
317
- # The actual value for a state is stored in the attribute configured for the
318
- # state machine. In most cases, this is the same as the name of the state
319
- # machine. For example:
320
- #
321
- # class Vehicle
322
- # attr_accessor :state
323
- #
324
- # state_machine :state, :initial => :parked do
325
- # ...
326
- # state :parked, :value => 0
327
- # start :idling, :value => 1
328
- # end
329
- # end
330
- #
331
- # vehicle = Vehicle.new # => #<Vehicle:0xb712da60 @state=0>
332
- # vehicle.state # => 0
333
- # vehicle.parked? # => true
334
- # vehicle.state = 1
335
- # vehicle.idling? # => true
336
- #
337
- # The most important thing to note from the example above is what it means
338
- # to read from and write to the state machine's attribute. In particular,
339
- # state_machine treats the attribute (+state+ in this case) like a basic
340
- # attr_accessor that's been defined on the class. There are no special
341
- # behaviors added, such as allowing the attribute to be written to based on
342
- # the name of a state in the machine. This is the case for a few reasons:
343
- # * Setting the attribute directly is an edge case that is meant to only be
344
- # used when you want to skip state_machine altogether. This means that
345
- # state_machine shouldn't have any effect on the attribute accessor
346
- # methods. If you want to change the state, you should be using one of
347
- # the events defined in the state machine.
348
- # * Many ORMs provide custom behavior for the attribute reader / writer - it
349
- # may even be defined by your own framework / method implementation just
350
- # the example above showed. In order to avoid having to worry about the
351
- # different ways an attribute can get written, state_machine just makes
352
- # sure that the configured value for a state is always used when writing
353
- # to the attribute.
354
- #
355
- # If you were interested in accessing the name of a state (instead of its
356
- # actual value through the attribute), you could do the following:
357
- #
358
- # vehicle.state_name # => :idling
359
- #
360
- # == Events and Transitions
361
- #
362
- # Events defined on the machine are the interface to transitioning states
363
- # for an object. Events can be fired either directly (through the method
364
- # generated for the event) or indirectly (through attributes defined on
365
- # the machine).
366
- #
367
- # For example,
368
- #
369
- # class Vehicle
370
- # include DataMapper::Resource
371
- # property :id, Serial
372
- #
373
- # state_machine :initial => :parked do
374
- # event :ignite do
375
- # transition :parked => :idling
376
- # end
377
- # end
378
- #
379
- # state_machine :alarm_state, :initial => :active do
380
- # event :disable do
381
- # transition all => :off
382
- # end
383
- # end
384
- # end
385
- #
386
- # # Fire +ignite+ event directly
387
- # vehicle = Vehicle.create # => #<Vehicle id=1 state="parked" alarm_state="active">
388
- # vehicle.ignite # => true
389
- # vehicle.state # => "idling"
390
- # vehicle.alarm_state # => "active"
391
- #
392
- # # Fire +disable+ event automatically
393
- # vehicle.alarm_state_event = 'disable'
394
- # vehicle.save # => true
395
- # vehicle.alarm_state # => "off"
396
- #
397
- # In the above example, the +state+ attribute is transitioned using the
398
- # +ignite+ action that's generated from the state machine. On the other
399
- # hand, the +alarm_state+ attribute is transitioned using the +alarm_state_event+
400
- # attribute that automatically gets fired when the machine's action (+save+)
401
- # is invoked.
402
- #
403
- # For more information about how to configure an event and its associated
404
- # transitions, see StateMachine::Machine#event.
405
- #
406
- # == Defining callbacks
407
- #
408
- # Within the +state_machine+ block, you can also define callbacks for
409
- # transitions. For more information about defining these callbacks,
410
- # see StateMachine::Machine#before_transition, StateMachine::Machine#after_transition,
411
- # and StateMachine::Machine#around_transition, and StateMachine::Machine#after_failure.
412
- #
413
- # == Namespaces
414
- #
415
- # When a namespace is configured for a state machine, the name provided
416
- # will be used in generating the instance methods for interacting with
417
- # states/events in the machine. This is particularly useful when a class
418
- # has multiple state machines and it would be difficult to differentiate
419
- # between the various states / events.
420
- #
421
- # For example,
422
- #
423
- # class Vehicle
424
- # state_machine :heater_state, :initial => :off, :namespace => 'heater' do
425
- # event :turn_on do
426
- # transition all => :on
427
- # end
428
- #
429
- # event :turn_off do
430
- # transition all => :off
431
- # end
432
- # end
433
- #
434
- # state_machine :alarm_state, :initial => :active, :namespace => 'alarm' do
435
- # event :turn_on do
436
- # transition all => :active
437
- # end
438
- #
439
- # event :turn_off do
440
- # transition all => :off
441
- # end
442
- # end
443
- # end
444
- #
445
- # The above class defines two state machines: +heater_state+ and +alarm_state+.
446
- # For the +heater_state+ machine, the following methods are generated since
447
- # it's namespaced by "heater":
448
- # * <tt>can_turn_on_heater?</tt>
449
- # * <tt>turn_on_heater</tt>
450
- # * ...
451
- # * <tt>can_turn_off_heater?</tt>
452
- # * <tt>turn_off_heater</tt>
453
- # * ..
454
- # * <tt>heater_off?</tt>
455
- # * <tt>heater_on?</tt>
456
- #
457
- # As shown, each method is unique to the state machine so that the states
458
- # and events don't conflict. The same goes for the +alarm_state+ machine:
459
- # * <tt>can_turn_on_alarm?</tt>
460
- # * <tt>turn_on_alarm</tt>
461
- # * ...
462
- # * <tt>can_turn_off_alarm?</tt>
463
- # * <tt>turn_off_alarm</tt>
464
- # * ..
465
- # * <tt>alarm_active?</tt>
466
- # * <tt>alarm_off?</tt>
467
- #
468
- # == Scopes
469
- #
470
- # For integrations that support it, a group of default scope filters will
471
- # be automatically created for assisting in finding objects that have the
472
- # attribute set to one of a given set of states.
473
- #
474
- # For example,
475
- #
476
- # Vehicle.with_state(:parked) # => All vehicles where the state is parked
477
- # Vehicle.with_states(:parked, :idling) # => All vehicles where the state is either parked or idling
478
- #
479
- # Vehicle.without_state(:parked) # => All vehicles where the state is *not* parked
480
- # Vehicle.without_states(:parked, :idling) # => All vehicles where the state is *not* parked or idling
481
- #
482
- # *Note* that if class methods already exist with those names (i.e.
483
- # :with_state, :with_states, :without_state, or :without_states), then a
484
- # scope will not be defined for that name.
485
- #
486
- # See StateMachine::Machine for more information about using integrations
487
- # and the individual integration docs for information about the actual
488
- # scopes that are generated.
489
- def state_machine(*args, &block)
490
- StateMachine::Machine.find_or_create(self, *args, &block)
491
- end
492
- end
493
- end
494
-
495
- Class.class_eval do
496
- include StateMachine::MacroMethods
497
- end
498
-
499
- require 'state_machine/initializers'
1
+ require 'state_machine/core'
2
+ require 'state_machine/core_ext'