class NxtStateMachine::StateMachine

Attributes

callbacks[R]
class_context[R]
defuse_registry[R]
error_callback_registry[R]
events[R]
initial_state[RW]
name[R]
options[R]

Public Class Methods

new(name, class_context, event_registry, **opts) click to toggle source
# File lib/nxt_state_machine/state_machine.rb, line 3
def initialize(name, class_context, event_registry, **opts)
  @name = name
  @class_context = class_context
  @options = opts

  @states = NxtStateMachine::StateRegistry.new
  @transitions = []
  @events = event_registry
  @callbacks = CallbackRegistry.new
  @error_callback_registry = ErrorCallbackRegistry.new
  @defuse_registry = DefuseRegistry.new

  @initial_state = nil
end

Public Instance Methods

after_transition(from:, to:, run: nil, &block) click to toggle source
# File lib/nxt_state_machine/state_machine.rb, line 117
def after_transition(from:, to:, run: nil, &block)
  callbacks.register(from, to, :after, run, block)
end
all_states()
Alias for: any_state
all_states_except(*excluded) click to toggle source
# File lib/nxt_state_machine/state_machine.rb, line 80
def all_states_except(*excluded)
  all_states - excluded
end
all_transitions_from_to(from: all_states, to: all_states) click to toggle source
# File lib/nxt_state_machine/state_machine.rb, line 66
def all_transitions_from_to(from: all_states, to: all_states)
  transitions.select { |transition| transition.transitions_from_to?(from, to) }
end
any_state() click to toggle source
# File lib/nxt_state_machine/state_machine.rb, line 74
def any_state
  states.values.map(&:enum)
end
Also aliased as: all_states
around_transition(from:, to:, run: nil, &block) click to toggle source
# File lib/nxt_state_machine/state_machine.rb, line 137
def around_transition(from:, to:, run: nil, &block)
  callbacks.register(from, to, :around, run, block)
end
before_transition(from:, to:, run: nil, &block) click to toggle source
# File lib/nxt_state_machine/state_machine.rb, line 113
def before_transition(from:, to:, run: nil, &block)
  callbacks.register(from, to, :before, run, block)
end
can_transition!(event, from) click to toggle source
# File lib/nxt_state_machine/state_machine.rb, line 108
def can_transition!(event, from)
  return true if can_transition?(event, from)
  raise NxtStateMachine::Errors::TransitionNotDefined, "No transition :#{event} for state :#{from} defined"
end
can_transition?(event_name, from) click to toggle source
# File lib/nxt_state_machine/state_machine.rb, line 103
def can_transition?(event_name, from)
  event = events.resolve!(event_name)
  event && event.event_transitions.key?(from)
end
configure(&block) click to toggle source
# File lib/nxt_state_machine/state_machine.rb, line 141
def configure(&block)
  instance_exec(&block)
  self
end
current_state_name(context) click to toggle source
# File lib/nxt_state_machine/state_machine.rb, line 174
def current_state_name(context)
  Callable.new(get_state_with).bind(context).call(target(context))
end
defuse(errors = [], from:, to:) click to toggle source
# File lib/nxt_state_machine/state_machine.rb, line 125
def defuse(errors = [], from:, to:)
  defuse_registry.register(from, to, errors)
end
event(name, **options, &block) click to toggle source
# File lib/nxt_state_machine/state_machine.rb, line 84
def event(name, **options, &block)
  name = name.to_sym
  event = Event.new(name, self, **options, &block)
  events.register(name, event)

  Event::Names.set_state_method_map(name).each do |event_name, set_state_method|
    class_context.define_method event_name do |*args, **opts|
      event.state_machine.can_transition!(name, event.state_machine.current_state_name(self))
      transition = event.event_transitions.resolve!(event.state_machine.current_state_name(self))
      # Transition is build every time and thus should be thread safe!
      transition.build_transition(event, self, set_state_method, *args, **opts)
    end
  end

  class_context.define_method "can_#{name}?" do
    event.state_machine.can_transition?(name, event.state_machine.current_state_name(self))
  end
end
events_for_state(state) click to toggle source
# File lib/nxt_state_machine/state_machine.rb, line 70
def events_for_state(state)
  events.values.select { |event| event.transitions_from?(state) }
end
find_error_callback(error, transition) click to toggle source
# File lib/nxt_state_machine/state_machine.rb, line 159
def find_error_callback(error, transition)
  error_callback_registry.resolve(error, transition)
end
get_state_with(method = nil, &block) click to toggle source
# File lib/nxt_state_machine/state_machine.rb, line 21
def get_state_with(method = nil, &block)
  @get_state_with ||= (method || block) || raise_missing_configuration_error(:get_state_with)
end
on_error(error = StandardError, from:, to:, run: nil, &block) click to toggle source
# File lib/nxt_state_machine/state_machine.rb, line 129
def on_error(error = StandardError, from:, to:, run: nil, &block)
  error_callback_registry.register(from, to, error, run, block)
end
on_error!(error = StandardError, from:, to:, run: nil, &block) click to toggle source
# File lib/nxt_state_machine/state_machine.rb, line 133
def on_error!(error = StandardError, from:, to:, run: nil, &block)
  error_callback_registry.register!(from, to, error, run, block)
end
on_success(from:, to:, run: nil, &block) click to toggle source
# File lib/nxt_state_machine/state_machine.rb, line 121
def on_success(from:, to:, run: nil, &block)
  callbacks.register(from, to, :success, run, block)
end
run_after_callbacks(transition, context) click to toggle source
# File lib/nxt_state_machine/state_machine.rb, line 151
def run_after_callbacks(transition, context)
  run_callbacks(transition, :after, context)
end
run_before_callbacks(transition, context) click to toggle source

TODO: Everything that require context should live in some sort of proxy

# File lib/nxt_state_machine/state_machine.rb, line 147
def run_before_callbacks(transition, context)
  run_callbacks(transition, :before, context)
end
run_callbacks(transition, kind, context) click to toggle source
# File lib/nxt_state_machine/state_machine.rb, line 163
def run_callbacks(transition, kind, context)
  current_callbacks = callbacks.resolve!(transition, kind)
  return unless current_callbacks.any?

  current_callbacks.each do |callback|
    Callable.new(callback).bind(context).call(transition)
  end

  true
end
run_success_callbacks(transition, context) click to toggle source
# File lib/nxt_state_machine/state_machine.rb, line 155
def run_success_callbacks(transition, context)
  run_callbacks(transition, :success, context)
end
set_state_with(method = nil, &block) click to toggle source
# File lib/nxt_state_machine/state_machine.rb, line 25
def set_state_with(method = nil, &block)
  @set_state_with ||= (method || block) || raise_missing_configuration_error(:set_state_with)
end
set_state_with!(method = nil, &block) click to toggle source
# File lib/nxt_state_machine/state_machine.rb, line 29
def set_state_with!(method = nil, &block)
  @set_state_with_bang ||= (method || block) || raise_missing_configuration_error(:set_state_with!)
end
state(*names, **opts, &block) click to toggle source
# File lib/nxt_state_machine/state_machine.rb, line 33
def state(*names, **opts, &block)
  defaults = { initial: false }
  opts.reverse_merge!(defaults)
  machine = self

  Array(names).map do |name|
    if opts.fetch(:initial) && initial_state.present?
      raise NxtStateMachine::Errors::InitialStateAlreadyDefined, ":#{initial_state.enum} was already defined as the initial state"
    else
      state = new_state_class(&block).new(name, self, **opts.reverse_merge(index: states.size))
      states.register(name, state)
      self.initial_state = state if opts.fetch(:initial)

      class_context.define_method "#{name}?" do
        machine.current_state_name(self) == name
      end

      state
    end
  end
end
states(*names, **opts, &block) click to toggle source
# File lib/nxt_state_machine/state_machine.rb, line 55
def states(*names, **opts, &block)
  # method overloading in ruby ;-)
  return @states unless names.any?

  state(*names, **opts, &block)
end
target(context) click to toggle source
# File lib/nxt_state_machine/state_machine.rb, line 178
def target(context)
  @target_method ||= (options[:target] || :itself)
  context.send(@target_method)
end
transitions() click to toggle source
# File lib/nxt_state_machine/state_machine.rb, line 62
def transitions
  @transitions ||= events.values.flat_map(&:event_transitions)
end

Private Instance Methods

new_state_class(&block) click to toggle source
# File lib/nxt_state_machine/state_machine.rb, line 189
def new_state_class(&block)
  if block
    Class.new(NxtStateMachine::State, &block)
  else
    NxtStateMachine::State
  end
end
raise_missing_configuration_error(method) click to toggle source
# File lib/nxt_state_machine/state_machine.rb, line 185
def raise_missing_configuration_error(method)
  raise NxtStateMachine::Errors::MissingConfiguration, "Configuration method :#{method} was not defined"
end