Observers

not related to Ruby's Observable mechanism

Observers, i.e. external classes, can hook into state machines using the callback api.

Example

  class Vehicle < ActiveRecord::Base
    state_machine do
      event :ignite do
        transition :idling => :parked
      end
      ...
    end
    ...
  end

Now before/after/after_failure_to observer methods can immediately hook into:

  • _ignite_from_parked_to_idling
  • _ignite_from_parked
  • _ignite_to_idling
  • _ignite
  • _transition_state_from_parked_to_idling
  • _transition_state_from_parked
  • _transition_state_to_idling
  • _transition_state
  • _transition
  Vehicle.state_machine do
    before_transition :on => :park, :do => VehicleObserver.method(:before_park)
    before_transition VehicleObserver.method(:before_transition)

    after_transition :on => :park, :do => VehicleObserver.method(:after_park)
    after_transition VehicleObserver.method(:after_transition)

    around_transition VehicleObserver.method(:around_transition)
  end
Example - External Observer Class
  class VehicleObserver < ActiveRecord::Observer
    def before_save(vehicle)
      # log message
    end

    # Callback for :ignite event *before* the transition is performed
    def before_ignite(vehicle, transition)
      # log message
    end

    # Callback for :ignite event *after* the transition has been performed
    def after_ignite(vehicle, transition)
      # put on seatbelt
    end

    # Generic transition callback *before* the transition is performed
    def after_transition(vehicle, transition)
      Audit.log(vehicle, transition)
    end
  end

More flexible transition callbacks can be defined directly within the model as described in

Multi State Machines Observer

To define a common observer/callback for multiple state machines, you can utilize ActiveRecord::Observer:

  class StateMachineObserver < ActiveRecord::Observer
    observe Vehicle, Switch, Project

    def after_transition(record, transition)
      Audit.log(record, transition)
    end
  end

or you can do it manually:

  class StateMachineObserver
    def self.before_transition(object, transition)
      Audit.log_transition(object.attributes)
    end
  end

  [Vehicle, Switch, Project].each do |klass|
    klass.state_machines.each do |attribute, machine|
      machine.before_transition StateMachineObserver.method(:before_transition)
    end
  end

results matching ""

    No results matching ""