state_machine

Class Methods

The following class methods will be automatically generated by the state machine based on the name of the machine. Any existing methods will not be overwritten.

human_state_name(state)

Gets the humanized value for the given state. This may be generated by internationalization libraries if supported by the integration.

human_state_event_name(event)

Gets the humanized value for the given event. This may be generated by internationalization libraries if supported by the integration.

For example,

  class Vehicle
    state_machine :state, :initial => :parked do
      event :ignite do
        transition :parked => :idling
      end

      event :shift_up do
        transition :idling => :first_gear
      end
    end
  end

  Vehicle.human_state_name(:parked)         # => "parked"
  Vehicle.human_state_name(:first_gear)     # => "first gear"
  Vehicle.human_state_event_name(:park)     # => "park"
  Vehicle.human_state_event_name(:shift_up) # => "shift up"

Instance Methods

The following instance methods will be automatically generated by the state machine based on the name of the machine. Any existing methods will not be overwritten.

state

Gets the current value for the attribute

state=(value)

Sets the current value for the attribute

state?(name)

Checks the given state name against the current state. If the name is not a known state, then an ArgumentError is raised.

state_name

Gets the name of the state for the current value

human_state_name

Gets the human-readable name of the state for the current value

state_events(requirements = {})

Gets the list of events that can be fired on the current object's state (uses the unqualified event names)

state_transitions(requirements = {})

Gets the list of transitions that can be made on the current object's state

state_paths(requirements = {})

Gets the list of sequences of transitions that can be run from the current object's state

fire_state_event(name, *args)

Fires an arbitrary event with the given argument list. This is essentially the same as calling the actual event method itself.

The state_events, state_transitions, and state_paths helpers all take an optional set of requirements for determining what's available for the current object. These requirements include:

:from

One or more states to transition from. If none are specified, then this will be the object's current state.

:to

One or more states to transition to. If none are specified, then this will match any to state.

:on

One or more events to transition on. If none are specified, then this will match any event.

:guard

Whether to guard transitions with the if/unless conditionals defined for each one. Default is true.

For example,

  class Vehicle
    state_machine :state, :initial => :parked do
      event :ignite do
        transition :parked => :idling
      end

      event :park do
        transition :idling => :parked
      end
    end
  end

  vehicle = Vehicle.new
  vehicle.state                             # => "parked"
  vehicle.state_name                        # => :parked
  vehicle.human_state_name                  # => "parked"
  vehicle.state?(:parked)                   # => true

  # Changing state
  vehicle.state = 'idling'
  vehicle.state                             # => "idling"
  vehicle.state_name                        # => :idling
  vehicle.state?(:parked)                   # => false

  # Getting current event / transition availability
  vehicle.state_events                      # => [:park]
  vehicle.park                              # => true
  vehicle.state_events                      # => [:ignite]
  vehicle.state_events(:from => :idling)    # => [:park]
  vehicle.state_events(:to => :parked)      # => []

  vehicle.state_transitions                 # => [#<StateMachines::Transition attribute=:state
                                            # event=:ignite from="parked" from_name=:parked
                                            # to="idling" to_name=:idling>]
  vehicle.ignite                            # => true
  vehicle.state_transitions                 # => [#<StateMachines::Transition attribute=:state
                                            # event=:park from="idling" from_name=:idling
                                            # to="parked" to_name=:parked>]

  vehicle.state_transitions(:on => :ignite) # => []

  # Getting current path availability
  vehicle.state_paths                       # => [
                                            # [#<StateMachines::Transition attribute=:state
                                            # event=:park from="idling" from_name=:idling
                                            # to="parked" to_name=:parked>,
                                            # #<StateMachines::Transition attribute=:state
                                            # event=:ignite from="parked" from_name=:parked
                                            # to="idling" to_name=:idling>]
                                            #   ]
  vehicle.state_paths(:guard => false)      # =>
                                            # [#<StateMachines::Transition attribute=:state
                                            # event=:park from="idling" from_name=:idling
                                            # to="parked" to_name=:parked>,
                                            # #<StateMachines::Transition attribute=:state
                                            # event=:ignite from="parked" from_name=:parked
                                            # to="idling" to_name=:idling>]
                                            #   ]

  # Fire arbitrary events
  vehicle.fire_state_event(:park)           # => true

Overriding instance / class methods

Hooking in behavior to the generated instance / class methods from the state machine, events, and states is very simple because of the way these methods are generated on the class. Using the class's ancestors, the original generated method can be referred to via super. For example:

  class Vehicle
    state_machine do
      event :park do
        ...
      end
    end

    def park(*args)
      logger.info "..."
      super
    end
  end

In the above example, the park instance method that's generated on the Vehicle class (by the associated event) is overridden with custom behavior. Once this behavior is complete, the original method from the state machine is invoked by simply calling super.

The same technique can be used for state, state_name, and all other instance and class methods on the Vehicle class.

Method conflicts

By default statemachine does not redefine methods that exist on superclasses (_including Object) or any modules (including Kernel) that were included before it was defined. This is in order to ensure that existing behavior on the class is not broken by the inclusion of state_machine.

If a conflicting method is detected, state_machine will generate a warning. For example, consider the following class:

  class Vehicle
    state_machine do
      event :open do
        ...
      end
    end
  end

In the above class, an event named "open" is defined for its state machine. However, "open" is already defined as an instance method in Ruby's Kernel module that gets included in every Object. As a result, state_machine will generate the following warning:

  Instance method "open" is already defined in Object, use generic helper instead or set
  StateMachines::Machine.ignore_method_conflicts = true.

Even though you may not be using Kernel's implementation of the "open" instance method, state_machine isn't aware of this and, as a result, stays safe and just skips redefining the method.

As with almost all helpers methods defined by state_machine in your class, there are generic methods available for working around this method conflict. In the example above, you can invoke the "open" event like so:

  vehicle = Vehicle.new       # => #<Vehicle:0xb72686b4 @state=nil>
  vehicle.fire_events(:open)  # => true

  # This will not work
  vehicle.open                # => NoMethodError: private method `open' called for
                              #<Vehicle:0xb72686b4 @state=nil>

If you want to take on the risk of overriding existing methods and just ignore method conflicts altogether, you can do so by setting the following configuration:

  StateMachines::Machine.ignore_method_conflicts = true

This will allow you to define events like "open" as described above and still generate the "open" instance helper method. For example:

  StateMachines::Machine.ignore_method_conflicts = true

  class Vehicle
    state_machine do
      event :open do
        ...
    end
  end

  vehicle = Vehicle.new   # => #<Vehicle:0xb72686b4 @state=nil>
  vehicle.open            # => true

By default, state_machine helps prevent you from making mistakes and accidentally overriding methods that you didn't intend to. Once you understand this and what the consequences are, setting the ignore_method_conflicts option is a perfectly reasonable workaround.

results matching ""

    No results matching ""