module Celluloid::FSM - celluloid-0.16.0 Documentation

# module Celluloid::FSM

Simple finite state machines with integrated Celluloid timeout support Inspired by Erlang's gen_fsm (www.erlang.org/doc/man/gen_fsm.html)

Basic usage:

    class MyMachine
      include Celluloid::FSM # NOTE: this does NOT pull in the Celluloid module
    end

Inside an actor:

    #
    machine = MyMachine.new(current_actor)

### Constants

DEFAULT_STATE

### Attributes

actor[R]
state[R]

Obtain the current state of the FSM

### Public Class Methods

included(klass) click to toggle source

Included hook to extend class methods

    # File lib/celluloid/fsm.rb, line 21
    def self.included(klass)
      klass.send :extend, ClassMethods
    end

new(actor = nil) click to toggle source

Be kind and call super if you must redefine initialize

    # File lib/celluloid/fsm.rb, line 63
    def initialize(actor = nil)
      @state = self.class.default_state
      @delayed_transition = nil
      @actor = actor
      @actor ||= Celluloid.current_actor if Celluloid.actor?
    end

### Public Instance Methods

actor=(actor)
Alias for: attach
attach(actor) click to toggle source

Attach this FSM to an actor. This allows FSMs to wait for and initiate events in the context of a particular actor

    # File lib/celluloid/fsm.rb, line 75
    def attach(actor)
      @actor = actor
    end

Also aliased as: actor=
transition(state_name, options = {}) click to toggle source

Transition to another state Options:

- delay: don't transition immediately, wait the given number of seconds.

      This will return a Celluloid::Timer object you can use to
      cancel the pending state transition.

Note: making additional state transitions will cancel delayed transitions

    # File lib/celluloid/fsm.rb, line 87
    def transition(state_name, options = {})
      new_state = validate_and_sanitize_new_state(state_name)
      return unless new_state

      if handle_delayed_transitions(new_state, options[:delay])
        return @delayed_transition
      end

      transition_with_callbacks!(new_state)
    end

transition!(state_name) click to toggle source

Immediate state transition with no sanity checks, or callbacks. “Dangerous!”

    # File lib/celluloid/fsm.rb, line 99
    def transition!(state_name)
      @state = state_name
    end

### Protected Instance Methods

current_state() click to toggle source

    # File lib/celluloid/fsm.rb, line 138
    def current_state
      states[@state]
    end

current_state_name() click to toggle source

    # File lib/celluloid/fsm.rb, line 142
    def current_state_name
      current_state && current_state.name || ''
    end

default_state() click to toggle source

    # File lib/celluloid/fsm.rb, line 134
    def default_state
      self.class.default_state
    end

handle_delayed_transitions(new_state, delay) click to toggle source

    # File lib/celluloid/fsm.rb, line 146
    def handle_delayed_transitions(new_state, delay)
      if delay
        raise UnattachedError, "can't delay unless attached" unless @actor
        @delayed_transition.cancel if @delayed_transition

        @delayed_transition = @actor.after(delay) do
          transition_with_callbacks!(new_state)
        end

        return @delayed_transition
      end

      if defined?(@delayed_transition) and @delayed_transition
        @delayed_transition.cancel
        @delayed_transition = nil
      end
    end

states() click to toggle source

    # File lib/celluloid/fsm.rb, line 130
    def states
      self.class.states
    end

transition_with_callbacks!(state_name) click to toggle source

    # File lib/celluloid/fsm.rb, line 125
    def transition_with_callbacks!(state_name)
      transition! state_name.name
      state_name.call(self)
    end

validate_and_sanitize_new_state(state_name) click to toggle source

    # File lib/celluloid/fsm.rb, line 105
    def validate_and_sanitize_new_state(state_name)
      state_name = state_name.to_sym

      return if current_state_name == state_name

      if current_state and not current_state.valid_transition? state_name
        valid = current_state.transitions.map(&:to_s).join(", ")
        raise ArgumentError, "#{self.class} can't change state from '#{@state}' to '#{state_name}', only to: #{valid}"
      end

      new_state = states[state_name]

      unless new_state
        return if state_name == default_state
        raise ArgumentError, "invalid state for #{self.class}: #{state_name}"
      end

      new_state
    end