Class StateMachine::State
In: lib/state_machine/state.rb
Parent: Object

A state defines a value that an attribute can be in after being transitioned 0 or more times. States can represent a value of any type in Ruby, though the most common (and default) type is String.

In addition to defining the machine‘s value, a state can also define a behavioral context for an object when that object is in the state. See StateMachine::Machine#state for more information about how state-driven behavior can be utilized.

Methods

call   context   description   draw   final?   human_name   inspect   matches?   value  

Included Modules

Assertions

External Aliases

initial -> initial?

Attributes

cache  [RW]  Whether this state‘s value should be cached after being evaluated
human_name  [W]  The human-readable name for the state
initial  [RW]  Whether or not this state is the initial state to use for new objects
machine  [RW]  The state machine for which this state is defined
matcher  [RW]  A custom lambda block for determining whether a given value matches this state
methods  [R]  Tracks all of the methods that have been defined for the machine‘s owner class when objects are in this state.

Maps :method_name => UnboundMethod

name  [R]  The unique identifier for the state used in event and callback definitions
qualified_name  [R]  The fully-qualified identifier for the state, scoped by the machine‘s namespace
value  [W]  The value that is written to a machine‘s attribute when an object transitions into this state

Public Instance methods

Calls a method defined in this state‘s context on the given object. All arguments and any block will be passed into the method defined.

If the method has never been defined for this state, then a NoMethodError will be raised.

[Source]

     # File lib/state_machine/state.rb, line 213
213:     def call(object, method, method_missing = nil, *args, &block)
214:       if context_method = methods[method.to_sym]
215:         # Method is defined by the state: proxy it through
216:         context_method.bind(object).call(*args, &block)
217:       else
218:         # Dispatch to the superclass since this state doesn't handle the method
219:         method_missing.call if method_missing
220:       end
221:     end

Defines a context for the state which will be enabled on instances of the owner class when the machine is in this state.

This can be called multiple times. Each time a new context is created, a new module will be included in the owner class.

[Source]

     # File lib/state_machine/state.rb, line 181
181:     def context(&block)
182:       owner_class = machine.owner_class
183:       machine_name = machine.name
184:       name = self.name
185:       
186:       # Evaluate the method definitions
187:       context = StateContext.new(self)
188:       context.class_eval(&block)
189:       context.instance_methods.each do |method|
190:         methods[method.to_sym] = context.instance_method(method)
191:         
192:         # Calls the method defined by the current state of the machine
193:         context.class_eval "def \#{method}(*args, &block)\nself.class.state_machine(\#{machine_name.inspect}).states.match!(self).call(self, \#{method.inspect}, lambda {super}, *args, &block)\nend\n", __FILE__, __LINE__ + 1
194:       end
195:       
196:       # Include the context so that it can be bound to the owner class (the
197:       # context is considered an ancestor, so it's allowed to be bound)
198:       owner_class.class_eval { include context }
199:       
200:       context
201:     end

Generates a human-readable description of this state‘s name / value:

For example,

  State.new(machine, :parked).description                               # => "parked"
  State.new(machine, :parked, :value => :parked).description            # => "parked"
  State.new(machine, :parked, :value => nil).description                # => "parked (nil)"
  State.new(machine, :parked, :value => 1).description                  # => "parked (1)"
  State.new(machine, :parked, :value => lambda {Time.now}).description  # => "parked (*)

[Source]

     # File lib/state_machine/state.rb, line 128
128:     def description
129:       description = name ? name.to_s : name.inspect
130:       description << " (#{@value.is_a?(Proc) ? '*' : @value.inspect})" unless name.to_s == @value.to_s
131:       description
132:     end

Draws a representation of this state on the given machine. This will create a new node on the graph with the following properties:

  • label - The human-friendly description of the state.
  • width - The width of the node. Always 1.
  • height - The height of the node. Always 1.
  • shape - The actual shape of the node. If the state is a final state, then "doublecircle", otherwise "ellipse".

The actual node generated on the graph will be returned.

[Source]

     # File lib/state_machine/state.rb, line 232
232:     def draw(graph)
233:       node = graph.add_node(name ? name.to_s : 'nil',
234:         :label => description,
235:         :width => '1',
236:         :height => '1',
237:         :shape => final? ? 'doublecircle' : 'ellipse'
238:       )
239:       
240:       # Add open arrow for initial state
241:       graph.add_edge(graph.add_node('starting_state', :shape => 'point'), node) if initial?
242:       
243:       node
244:     end

Determines whether there are any states that can be transitioned to from this state. If there are none, then this state is considered final. Any objects in a final state will remain so forever given the current machine‘s definition.

[Source]

     # File lib/state_machine/state.rb, line 103
103:     def final?
104:       !machine.events.any? do |event|
105:         event.branches.any? do |branch|
106:           branch.state_requirements.any? do |requirement|
107:             requirement[:from].matches?(name) && !requirement[:to].matches?(name, :from => name)
108:           end
109:         end
110:       end
111:     end

Transforms the state name into a more human-readable format, such as "first gear" instead of "first_gear"

[Source]

     # File lib/state_machine/state.rb, line 115
115:     def human_name(klass = @machine.owner_class)
116:       @human_name.is_a?(Proc) ? @human_name.call(self, klass) : @human_name
117:     end

Generates a nicely formatted description of this state‘s contents.

For example,

  state = StateMachine::State.new(machine, :parked, :value => 1, :initial => true)
  state   # => #<StateMachine::State name=:parked value=1 initial=true context=[]>

[Source]

     # File lib/state_machine/state.rb, line 252
252:     def inspect
253:       attributes = [[:name, name], [:value, @value], [:initial, initial?], [:context, methods.keys]]
254:       "#<#{self.class} #{attributes.map {|attr, value| "#{attr}=#{value.inspect}"} * ' '}>"
255:     end

Determines whether this state matches the given value. If no matcher is configured, then this will check whether the values are equivalent. Otherwise, the matcher will determine the result.

For example,

  # Without a matcher
  state = State.new(machine, :parked, :value => 1)
  state.matches?(1)           # => true
  state.matches?(2)           # => false

  # With a matcher
  state = State.new(machine, :parked, :value => lambda {Time.now}, :if => lambda {|value| !value.nil?})
  state.matches?(nil)         # => false
  state.matches?(Time.now)    # => true

[Source]

     # File lib/state_machine/state.rb, line 172
172:     def matches?(other_value)
173:       matcher ? matcher.call(other_value) : other_value == value
174:     end

The value that represents this state. This will optionally evaluate the original block if it‘s a lambda block. Otherwise, the static value is returned.

For example,

  State.new(machine, :parked, :value => 1).value                        # => 1
  State.new(machine, :parked, :value => lambda {Time.now}).value        # => Tue Jan 01 00:00:00 UTC 2008
  State.new(machine, :parked, :value => lambda {Time.now}).value(false) # => <Proc:0xb6ea7ca0@...>

[Source]

     # File lib/state_machine/state.rb, line 143
143:     def value(eval = true)
144:       if @value.is_a?(Proc) && eval
145:         if cache_value?
146:           @value = @value.call
147:           machine.states.update(self)
148:           @value
149:         else
150:           @value.call
151:         end
152:       else
153:         @value
154:       end
155:     end

[Validate]