transition
Explicit Transitions
- Every event defined generates an instance method that allows the event to be explicitly triggered. Most examples here use that explicit transition
- By default
:save
is the action invoked underneath when a state is transitioned. If:save
- succeded: transitioned state remains persisted
- failed (returning
false
or an exception was raised while calling the action): transitioned state is rolled back
Calling a transition is equivielant to calling
:save
All other changes made to the record prior to transition will be saved as well
vehicle = Vehicle.create ### => #<Vehicle id: 1, name: nil, state: "parked"> vehicle.name = 'Ford Explorer' vehicle.ignite ### => true vehicle.reload ### => #<Vehicle id: 1, name: "Ford Explorer", state: "idling">
Important: If you want a transition to update additional attributes of the record, either:
- Make the needed changes in a
before_transition
callback, or- Save the record manually
class Vehicle
attr_accessor :fail, :saving_state
state_machine :initial => :parked, :action => :save do
event :ignite do
transition :parked => :idling
end
event :park do
transition :idling => :parked
end
end
def save
@saving_state = state
fail != true
end
end
vehicle = Vehicle.new # => #<Vehicle:0xb7c27024 @state="parked">
vehicle.save # => true
vehicle.saving_state # => "parked" # The state was "parked" when save was called
# Successful event
vehicle.ignite # => true
vehicle.saving_state # => "idling" # The state was "idling" when save was called
vehicle.state # => "idling"
# Failed event
vehicle.fail = true
vehicle.park # => false
vehicle.saving_state # => "parked"
vehicle.state # => "idling"
Implicit/Indirect Transitions
In addition to the action,
:save
, being run as the result of an event, it can also be used to run events itself!! This is done by1) Setting the state event attribute
vehicle.state_event = 'ignite'
2) Invoking the
:save
actionvehicle.save # => true
Which results in
vehicle.state # => "idling" vehicle.state_event # => nil
This is particularly useful if you want to allow users to drive the state transitions from a web API.
Examples
class Vehicle
state_machine :initial => :parked do
event :ignite do
transition :parked => :idling
end
end
state_machine :alarm_state, :initial => :active do
event :disable do
transition all => :off
end
end
end
Example - Explicit Transition
vehicle = Vehicle.create # => #<Vehicle id=1 state="parked" alarm_state="active">
vehicle.ignite # => true
vehicle.state # => "idling"
vehicle.alarm_state # => "active"
Example - Implicit Transition
vehicle = Vehicle.create # => #<Vehicle id=1 state="parked" alarm_state="active">
vehicle.state_event = "ignite" # => "ignite"
vehicle.save # => true
vehicle.state # => "idling"
vehicle.state_event # => nil
vehicle.alarm_state_event = 'disable'
vehicle.save # => true
vehicle.alarm_state # => "off"
ignite
event here was automatically triggered when thesave
action was calledalarm_state
attribute is transitioned using thealarm_state_event
attribute that automatically gets fired when the machine's action (save
) is invoked.
(Advanced)
- If the class in which the state machine is defined also defines the action being invoked (and
not a superclass), then it must manually run the
StateMachine
hook that checks for event attributes. - On the other hand, the Vehicle class from above defined its own
save
method (and there is nosave
method in its superclass). As a result, it must be modified like so:
def save
self.class.state_machines.transitions(self, :save).perform do
@saving_state = state
fail != true
end
end
This will add in the functionality for firing the event stored in the state_event
attribute.