class Finite::StateMachine

The State Machine class. Represents the whole state machine

Attributes

machines[RW]
callbacks[R]
events[R]
initial[R]
states[R]

Public Class Methods

new(initial_state, klass, &block) click to toggle source

Create a new state machine

@param initial_state [Symbol] the initial state of this state machine @param klass [Class] the class of the state machine @param block [Block] the block of code that creates it

# File lib/finite/machine.rb, line 16
def initialize(initial_state, klass, &block)
  @class = klass
  @initial = initial_state
  @states = Hash.new
  @events = Hash.new
  @callbacks = {before: Hash.new , after: Hash.new}
  instance_eval &block
end

Public Instance Methods

add_event(event_name, &block) click to toggle source

Add an event to the state machine

@param event_name [Symbol] the event you are trying to add @param block [Block] the block of code that creates an event @raise [Exception] if the event already exists

# File lib/finite/machine.rb, line 30
def add_event(event_name, &block)
  # We don't want to randomly override things that we shouldn't
  raise "Method already taken can_#{event_name}?" if @class.methods.include?(:"can_#{event_name}?")
  raise "Method already taken #{event_name}" if @class.methods.include?(:"#{event_name}")
  raise 'Event #{event_name} already exists. Rename or combine the events' if events.include? event_name
  event = Event.new(event_name, &block)
  @events[event_name] = event

  # Add the states from the transition
  event.transitions.each_value do |transition|
    add_state transition.to
    add_state transition.from
  end

  # Creates the method can_event_name?
  @class.send(:define_method, :"can_#{event_name}?") do
    event.transitions.key? current_state.name
  end

  # Creates the method event_name
  @class.send(:define_method, :"#{event_name}") do
    if event.transitions.key? current_state.name

      # Makes sure the transition can happen
      transition = event.transitions[current_state.name]
      if transition.condition.nil? or self.instance_exec(&transition.condition)

        new_state = states[event.transitions[current_state.name].to]

        # Call all of the "before event" callbacks
        event.callbacks[:before].each do |callback|
          self.instance_eval &callback
        end

        # Call all of the before_all callbacks
        if callbacks[:before].key? :all
          callbacks[:before][:all].each do |callback|
            self.instance_eval &callback
          end
        end

        # Call the "before state" callbacks
        if callbacks[:before].key? new_state.name
          callbacks[:before][new_state.name].each do |callback|
            self.instance_eval &callback
          end
        end

        # Set the current state
        @current_state = new_state

        # call the "after state" callbacks
        if callbacks[:after].key? current_state.name
          callbacks[:after][current_state.name].each do |callback|
            self.instance_eval &callback
          end
        end

        # call the "after all" callbacks
        if callbacks[:after].key? :all
          callbacks[:after][:all].each do |callback|
            self.instance_eval &callback
          end
        end

        # Call the "after event" callbacks
        event.callbacks[:after].each do |callback|
          self.instance_eval &callback
        end
        self
      end
    end
  end
end
add_state(state) click to toggle source

Add a state to the the state machine if the state hasn’t already been created

@param state [Symbol] the state you are trying to add

# File lib/finite/machine.rb, line 109
def add_state(state)
  if not @states.include? state
    # Prevents arbitrarily overriding methods that you shouldn't be
    raise "Method already taken #{state}?" if @class.methods.include?(:"#{state}?")
    @states[state] = State.new(state)
    @class.send(:define_method, :"#{state}?"){current_state == state}
  end
end

Private Instance Methods

event(name, &block) click to toggle source

The event method for the dsl

@param name [Symbol] the name of the event @param block [Block] the block of code that creates events

# File lib/finite/machine.rb, line 123
def event(name, &block)
  add_event name, &block
end