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 by

    • 1) Setting the state event attribute

      vehicle.state_event = 'ignite'
      
    • 2) Invoking the :save action

      vehicle.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 the save action was called
  • alarm_state attribute is transitioned using the alarm_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 no save 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.

results matching ""

    No results matching ""