class StateMachine::Base
Base
class of a finite state machine (FSM). See {StateMachine} for an overview.
Attributes
@return [State] Current {State} (or nil
if not in any
state, e.g. after exiting and before entering a new state)
@return [Dispatch::Queue] the GCD queue where the state
machine was started (or +nil+ if the state machine has not been started yet)
@return [String] Name of the state machine.
Only used in debug output.
@return [Boolean] Indicates if the machine logs debug output.
Public Class Methods
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
# 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
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
# 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
# File lib/motion-state-machine/base.rb, line 221 def log(text) puts text if @verbose end
@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
@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
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
@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
@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
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
@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
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