Callbacks

before_transition

Creates a callback to be invoked before a transition is performed so long as all the requirements are met.

Definition

  • As an argument:

        before_transition :set_alarm
        before_transition :set_alarm, all => :parked
    
  • In :do option:

        before_transition all => :parked, :do => :set_alarm
    
  • As a block:

        before_transition all => :parked do |vehicle, transition|
          vehicle.set_alarm
        end
    
  • Even as a group at the same time with different ways of defining:

    class Vehicle
      state_machine do
        before_transition :set_alarm, :lock_doors, all => :parked
        before_transition all => :parked, :do => [:set_alarm, :lock_doors]
        before_transition :set_alarm do |vehicle, transition|
          vehicle.lock_doors
        end
      end
    end
    

Requirements

state Requirements

Requirement so the callback is only invoked on a trasnsition from and to specific states, normally using a Hash syntax mapping beginning to ending states

  before_transition :parked => :idling, :idling => :first_gear, :do => :set_alarm
  # `set_alarm` is only called if the machine is transitioning from
  # `parked` to `idling` or
  # `idling` to `parked`

For more complex matching, see StateMachines::MatcherHelpers.

event Requirements

Requirement so the callback is only invoked on specific events using the on option

  before_transition :on => :ignite, :do => ...                        # Matches only on ignite
  before_transition :on => all - :ignite, :do => ...                  # Matches on every event except ignite
  before_transition :parked => :idling, :on => :ignite, :do => ...    # Matches from parked to idling on ignite

For more complex matching, see StateMachines::MatcherHelpers.

Verbose Requirements

Requirements can be defined directly using verbose options:

:from

One or more states being transitioned from. If none are specified, then all states will match.

:to

One or more states being transitioned to. If none are specified, then all states will match.

:on

One or more events that fired the transition. If none are specified, then all events will match.

:except_from

One or more states not being transitioned from

:except_to

One more states not being transitioned to

:except_on

One or more events that did not fire the transition

Examples:

  before_transition :from => :ignite, :to => :idling, :on => :park, :do => ...
  before_transition :except_from => :ignite, :except_to => :idling, :except_on => :park, :do => ...
Conditions

Conditions can also be defined to determine if the callback should be invoked, using:

:if

A method, proc or string to call to determine if the callback should occur (e.g. :if => :allow_callbacks, or :if => lambda {|user| user.signup_step > 2}). The method, proc or string should return or evaluate to a true or false.

:unless

A method, proc or string to call to determine if the callback should not occur (e.g. :unless => :skip_callbacks, or :unless => lambda {|user| user.signup_step <= 2}). The method, proc or string should return or evaluate to a true or false.

Examples
  before_transition :parked => :idling, :if => :moving?, :do => ...
  before_transition :on => :ignite, :unless => :seatbelt_on?, :do => ...

Accessing the Transition

The actual transition describing the context (e.g. event, from, to) can be be passed in as an argument if callbacks are configured to not be bound to the object involved. This is the default behavior

  class Vehicle
    # Only specifies one parameter (the object being transitioned)
    before_transition all => :parked do |vehicle|
      vehicle.set_alarm
    end

    # Specifies 2 parameters (object being transitioned and actual transition)
    before_transition all => :parked do |vehicle, transition|
      vehicle.set_alarm(transition)
    end
  end

See StateMachines::Transition for more about the attributes available on the transition.

Usage with Delegation

  • state_machine uses the callback method's argument list arity to determine whether to include the transition in the method call
  • If you're using delegates, such as those defined in ActiveSupport or Forwardable, the actual arity of the delegated method gets masked and so callbacks referencing delegates will be passed the transition as an argument

    class Vehicle
      extend Forwardable
      delegate :refresh => :dashboard
    
      state_machine do
        before_transition :refresh
        ...
      end
    
      def dashboard
        @dashboard ||= Dashboard.new
      end
    end
    
    class Dashboard
      def refresh(transition)
        # ...
      end
    end
    

    In the above example, Dashboard#refresh must define a transition argument. Otherwise, an ArgumentError exception will get raised. The only way around this is to avoid the use of delegates and manually define the delegate method so that the correct arity is used.

Examples

  class Vehicle
    state_machine do
      # Before all transitions
      before_transition :update_dashboard

      # Before specific transition:
      before_transition [:first_gear, :idling] => :parked, :on => :park, :do => :take_off_seatbelt

      # With conditional callback:
      before_transition all => :parked, :do => :take_off_seatbelt, :if => :seatbelt_on?

      # Using helpers:
      before_transition all - :stalled => same, :on => any - :crash, :do => :update_dashboard
      ...
    end
  end

results matching ""

    No results matching ""