class StateMachine::Base

Base class of a finite state machine (FSM). See {StateMachine} for an overview.

Attributes

current_state[RW]

@return [State] Current {State} (or nil if not in any

state, e.g. after exiting and before entering a new state)
initial_queue[R]

@return [Dispatch::Queue] the GCD queue where the state

machine was started (or +nil+ if the state machine has
not been started yet)
name[R]

@return [String] Name of the state machine.

Only used in debug output.
verbose[R]

@return [Boolean] Indicates if the machine logs debug output.

Public Class Methods

new(options) click to toggle source

Initializes a new StateMachine.

@param options [Hash]

Configuration options for the FSM.

@option options [Symbol] :start_state

First state after start

@option options [String] :name (“State machine”)

Name used in debugging output (optional)

@option options [Boolean] :verbose (false)

Indicate if the machine should output log texts to console.

@example

fsm = StateMachine::Base.new start_state: :awake

@return [StateMachine::Base] a new StateMachine object

Calls superclass method
# File lib/motion-state-machine/base.rb, line 57
def initialize(options)
  super(options)

  @name = options[:name] || "State machine"
  @verbose = !!options[:verbose]
  @state_symbols_to_states = {}

  waiting_for_start_state = state :waiting_for_start,
    "waiting for start (internal state)"
  start_state = options[:start_state].to_sym
  if start_state.nil?
    raise ArgumentError, "You have to supply a :start_state option."
  end
  state start_state, options[:start_state_name]
  self.when :waiting_for_start, do |state|
    state.transition_to start_state, on: :start
  end

            @current_state = waiting_for_start_state
  @current_state.send :enter!

  self
end

Public Instance Methods

event(event_symbol) click to toggle source

Sends an event to the state machine. If a matching transition was defined, the transition will be executed. If no transition matches, the event will just be ignored.

@note You should call this method from the same queue / thread

where the state machine was started.

@param event_symbol [Symbol] The event to trigger on the

state machine.

@example

my_state_machine.event :some_event
# File lib/motion-state-machine/base.rb, line 140
def event(event_symbol)
  transition = @events[event_symbol]
  transition.send(:handle_in_source_state) unless transition.nil?
end
inspect() click to toggle source
# File lib/motion-state-machine/base.rb, line 168
def inspect
  # Overridden to avoid debug output overhead
  # (default output would include all attributes)

  "#<#{self.class}:#{object_id.to_s(16)}>"
end
log(text) click to toggle source
# File lib/motion-state-machine/base.rb, line 221
def log(text)
  puts text if @verbose
end
raise_outside_initial_queue() click to toggle source

@api private

# File lib/motion-state-machine/base.rb, line 212
def raise_outside_initial_queue
  outside = Dispatch::Queue.current.to_s != @initial_queue.to_s
  if @initial_queue && outside
    raise RuntimeError,
      "Can't call this from outside #{@initial_queue} "\
      "(called from #{Dispatch::Queue.current})."
  end
end
register_event_handler(event_symbol, transition) click to toggle source

@api private

Registers a block that should be called when {#event} is called with the given symbol as parameter.

@param event_symbol [Symbol]

symbol that identifies the block

@param transition [Transition]

transition that should be executed when calling {#event} with
+event_symbol+ as parameter
# File lib/motion-state-machine/base.rb, line 205
def register_event_handler(event_symbol, transition)
  (@events ||= {})[event_symbol] = transition
end
start!() click to toggle source

Starts the finite state machine. The machine will be in its start state afterwards. For synchronization, it will remember from which queue/thread it was started.

# File lib/motion-state-machine/base.rb, line 121
def start!
    @initial_queue = Dispatch::Queue.current
  event :start
end
state(symbol, name = nil) click to toggle source

@api private Returns a State object identified by the given symbol.

# File lib/motion-state-machine/base.rb, line 179
def state(symbol, name = nil)
  unless symbol.is_a?(Symbol)
    raise ArgumentError,
      "You have to supply a symbol to #state. "\
      "Maybe you wanted to call #current_state?"
  end
  raise_outside_initial_queue
  name ||= symbol.to_s
  @state_symbols_to_states[symbol] ||= State.new(self,
    symbol: symbol,
    name: name)
end
states() click to toggle source

@return an array of registered {StateMachine::State} objects.

# File lib/motion-state-machine/base.rb, line 112
def states
  @state_symbols_to_states.values
end
stop_and_cleanup() click to toggle source

Should stop the machine and clean up memory. Should call exit actions on the current state, if defined.

Not yet tested / really implemented yet, so use with care and make a pull request if you should implement it ;)

# File lib/motion-state-machine/base.rb, line 159
def stop_and_cleanup
  raise_outside_initial_queue
  @state_machine.log "Stopping #{self}..." if @verbose
  @current_state.send :exit!
  @current_state = nil
  @state_symbols_to_states.values.each(&:cleanup)
end
terminated?() click to toggle source

@returns [Boolean] true if the machine has been terminated, false otherwise.

# File lib/motion-state-machine/base.rb, line 149
def terminated?
  current_state.terminating?
end
when(source_state_symbol, &block) click to toggle source

Adds defined transitions to the state machine. States that you refer to with symbols are created automatically, on-the-fly, so you do not have to define them with an extra statement.

@param source_state_symbol [Symbol]

Identifier of the state from which the transitions begins.

@example Define transitions from a state :awake to other states:

fsm.when :awake do |state|
   state.transition_to ...
   state.die :on => ...
   state.on_entry { ... }
   state.on_exit { ... }
end

@yieldparam [TransitionDefinitionDSL] Call configuration methods

on this object to define transitions. See
{TransitionDefinitionDSL} for a list of possible methods.

@see State::TransitionDefinitionDSL

# File lib/motion-state-machine/base.rb, line 103
def when(source_state_symbol, &block)
  raise_outside_initial_queue
  source_state = state source_state_symbol
  source_state.send :add_transition_map_defined_in, &block
end