module Workflow::InstanceMethods

Public Instance Methods

current_state() click to toggle source
# File lib/workflow.rb, line 79
def current_state
  loaded_state = load_workflow_state
  res = spec.states[loaded_state.to_sym] if loaded_state
  res || spec.initial_state
end
halt(reason = nil) click to toggle source
# File lib/workflow.rb, line 134
def halt(reason = nil)
  @halted_because = reason
  @halted = true
end
halt!(reason = nil) click to toggle source
# File lib/workflow.rb, line 139
def halt!(reason = nil)
  @halted_because = reason
  @halted = true
  raise TransitionHalted.new(reason)
end
halted?() click to toggle source

See the 'Guards' section in the README @return true if the last transition was halted by one of the transition callbacks.

# File lib/workflow.rb, line 87
def halted?
  @halted
end
halted_because() click to toggle source

@return the reason of the last transition abort as set by the previous call of `halt` or `halt!` method.

# File lib/workflow.rb, line 93
def halted_because
  @halted_because
end
process_event!(name, *args) click to toggle source
# File lib/workflow.rb, line 97
def process_event!(name, *args)
  event = current_state.events.first_applicable(name, self)
  raise NoTransitionAllowed.new(
    "There is no event #{name.to_sym} defined for the #{current_state} state") \
    if event.nil?
  @halted_because = nil
  @halted = false

  check_transition(event)

  from = current_state
  to = spec.states[event.transitions_to]

  run_before_transition(from, to, name, *args)
  return false if @halted

  begin
    return_value = run_action(event.action, *args) || run_action_callback(event.name, *args)
  rescue StandardError => e
    run_on_error(e, from, to, name, *args)
  end

  return false if @halted

  run_on_transition(from, to, name, *args)

  run_on_exit(from, to, name, *args)

  transition_value = persist_workflow_state to.to_s

  run_on_entry(to, from, name, *args)

  run_after_transition(from, to, name, *args)

  return_value.nil? ? transition_value : return_value
end
spec() click to toggle source
# File lib/workflow.rb, line 145
def spec
  # check the singleton class first
  class << self
    return workflow_spec if workflow_spec
  end

  c = self.class
  # using a simple loop instead of class_inheritable_accessor to avoid
  # dependency on Rails' ActiveSupport
  until c.workflow_spec || !(c.include? Workflow)
    c = c.superclass
  end
  c.workflow_spec
end

Private Instance Methods

check_transition(event) click to toggle source
# File lib/workflow.rb, line 162
def check_transition(event)
  # Create a meaningful error message instead of
  # "undefined method `on_entry' for nil:NilClass"
  # Reported by Kyle Burton
  if !spec.states[event.transitions_to]
    raise WorkflowError.new("Event[#{event.name}]'s " +
        "transitions_to[#{event.transitions_to}] is not a declared state.")
  end
end
has_callback?(action) click to toggle source
# File lib/workflow.rb, line 199
def has_callback?(action)
  # 1. public callback method or
  # 2. protected method somewhere in the class hierarchy or
  # 3. private in the immediate class (parent classes ignored)
  action = action.to_sym
  self.respond_to?(action) or
    self.class.protected_method_defined?(action) or
    self.private_methods(false).map(&:to_sym).include?(action)
end
load_workflow_state() click to toggle source

load_workflow_state and persist_workflow_state can be overriden to handle the persistence of the workflow state.

Default (non ActiveRecord) implementation stores the current state in a variable.

Default ActiveRecord implementation uses a 'workflow_state' database column.

# File lib/workflow.rb, line 241
def load_workflow_state
  @workflow_state if instance_variable_defined? :@workflow_state
end
persist_workflow_state(new_value) click to toggle source
# File lib/workflow.rb, line 245
def persist_workflow_state(new_value)
  @workflow_state = new_value
end
run_action(action, *args) click to toggle source
# File lib/workflow.rb, line 195
def run_action(action, *args)
  instance_exec(*args, &action) if action
end
run_action_callback(action_name, *args) click to toggle source
# File lib/workflow.rb, line 209
def run_action_callback(action_name, *args)
  action = action_name.to_sym
  self.send(action, *args) if has_callback?(action)
end
run_after_transition(from, to, event, *args) click to toggle source
# File lib/workflow.rb, line 190
def run_after_transition(from, to, event, *args)
  instance_exec(from.name, to.name, event, *args, &spec.after_transition_proc) if
    spec.after_transition_proc
end
run_before_transition(from, to, event, *args) click to toggle source
# File lib/workflow.rb, line 172
def run_before_transition(from, to, event, *args)
  instance_exec(from.name, to.name, event, *args, &spec.before_transition_proc) if
    spec.before_transition_proc
end
run_on_entry(state, prior_state, triggering_event, *args) click to toggle source
# File lib/workflow.rb, line 214
def run_on_entry(state, prior_state, triggering_event, *args)
  if state.on_entry
    instance_exec(prior_state.name, triggering_event, *args, &state.on_entry)
  else
    hook_name = "on_#{state}_entry"
    self.send hook_name, prior_state, triggering_event, *args if has_callback?(hook_name)
  end
end
run_on_error(error, from, to, event, *args) click to toggle source
# File lib/workflow.rb, line 177
def run_on_error(error, from, to, event, *args)
  if spec.on_error_proc
    instance_exec(error, from.name, to.name, event, *args, &spec.on_error_proc)
    halt(error.message)
  else
    raise error
  end
end
run_on_exit(state, new_state, triggering_event, *args) click to toggle source
# File lib/workflow.rb, line 223
def run_on_exit(state, new_state, triggering_event, *args)
  if state
    if state.on_exit
      instance_exec(new_state.name, triggering_event, *args, &state.on_exit)
    else
      hook_name = "on_#{state}_exit"
      self.send hook_name, new_state, triggering_event, *args if has_callback?(hook_name)
    end
  end
end
run_on_transition(from, to, event, *args) click to toggle source
# File lib/workflow.rb, line 186
def run_on_transition(from, to, event, *args)
  instance_exec(from.name, to.name, event, *args, &spec.on_transition_proc) if spec.on_transition_proc
end