module StateBoss::ClassMethods

Public Instance Methods

event(name, to:, &block) click to toggle source
# File lib/state_boss.rb, line 112
def event(name, to:, &block)
  @events[name] = {
    to: to || [],
    callback: block,
  }
end
state(key, values) click to toggle source
# File lib/state_boss.rb, line 96
def state(key, values)
  if values.key?(:as)
    if values[:as] == :default
      if @default_state
        raise InvalidTransitionError, "Already set :#{@default_state} as default state."
      end

      @default_state = key
    end

    values.merge!(to: nil) if values[:as] == :finish
  end

  @transitions[key] = values
end
state_machine(&block) click to toggle source
# File lib/state_boss.rb, line 51
def state_machine(&block)
  class_eval do
    block.call
  end

  raise StateInitializationError, 'state is uninitialized.' if @default_state.nil?

  @transitions.keys.each do |key|
    define_method("#{key}?") do
      _state == key
    end
  end

  @events.keys.each do |key|
    define_method(key) do
      raise InvalidTransitionError, 'state transition finished.' if finished_state?

      events = self.class.instance_variable_get(:@events)
      to = events[key][:to]

      transitions = self.class.instance_variable_get(:@transitions)

      unless transitions[_state][:to].include?(to)
        raise InvalidTransitionError, "can't change state from #{current_state} to #{to}"
      end

      before_state = _state
      @_state = to

      begin
        callback = events[key][:callback]
        result = callback.call(self) if !callback.nil?

        record_event(before_state, key)

        result
      rescue => e
        @_state = before_state

        raise e
      end
    end
  end
end