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