Class: Omnibot::Workflow

Inherits:
Object
  • Object
show all
Defined in:
lib/omnibot/workflow.rb,
lib/omnibot/workflow/runner.rb

Defined Under Namespace

Classes: ExecutionContext, Runner

Constant Summary collapse

TERMINAL_STEPS =
%i[done expired failed cancelled].freeze
WHILE_RUNNING_MODES =
%i[ignore interrupt].freeze
INTERRUPT =
:__omnibot_workflow_interrupt

Class Method Summary collapse

Class Method Details

.cancel!(run) ⇒ Object



96
97
98
99
100
# File 'lib/omnibot/workflow.rb', line 96

def cancel!(run)
  control(run, allowed: WorkflowRun::ACTIVE_STATUSES) do
    run.update!(status: "cancelled")
  end
end

.on_complete(&blk) ⇒ Object



29
# File 'lib/omnibot/workflow.rb', line 29

def on_complete(&blk) = blk ? @on_complete_hook = blk : nil

.on_complete_hookObject



30
# File 'lib/omnibot/workflow.rb', line 30

def on_complete_hook = @on_complete_hook

.resume(run, input: nil) ⇒ Object



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/omnibot/workflow.rb', line 52

def resume(run, input: nil)
  run.reload
  run.replies = []
  run.with_lock do
    case run.status
    when "waiting_for_input"
      step = run.current_step.to_sym
      ctx = ExecutionContext.new(run, self, input)
      runner = Runner.new(run)
      nxt = runner.send(:next_step_from, step, ctx)
      break if nxt.nil?
      if TERMINAL_STEPS.include?(nxt)
        runner.send(:complete, nxt)
      else
        runner.enter(nxt, input: input)
      end
    when "running"
      if while_running == :interrupt
        raise NotImplementedError, "while_running :interrupt ships in v0.3"
      end
      # :ignore — return unchanged
    when "waiting_for_human"
      raise WorkflowError::StaleResume, "use resume_from_human for waiting_for_human runs"
    else
      raise WorkflowError::StaleResume, "cannot resume a #{run.status} run"
    end
  end
  run
end

.resume_from_human(run, input: nil) ⇒ Object



82
83
84
85
86
87
# File 'lib/omnibot/workflow.rb', line 82

def resume_from_human(run, input: nil)
  control(run, allowed: %w[waiting_for_human]) do
    run.update!(status: "running")
    Runner.new(run).enter(run.current_step.to_sym, input: input)
  end
end

.retry!(run) ⇒ Object



89
90
91
92
93
94
# File 'lib/omnibot/workflow.rb', line 89

def retry!(run)
  control(run, allowed: %w[failed]) do
    run.update!(status: "running", error: nil)
    Runner.new(run).enter(run.current_step.to_sym)
  end
end

.start(ref: nil, state: {}) ⇒ Object



40
41
42
43
44
45
46
47
48
49
50
# File 'lib/omnibot/workflow.rb', line 40

def start(ref: nil, state: {})
  first = steps.keys.first or raise WorkflowError, "#{name} declares no steps"
  run = WorkflowRun.create!(
    type: name, status: "running", current_step: first.to_s,
    state: state.transform_keys(&:to_s), attempts: 0, timer_token: 0,
    step_entered_at: Time.current, ref: ref
  )
  run.replies = []
  run.with_lock { Runner.new(run).enter(first) }
  run
end

.state(*keys) ⇒ Object



7
# File 'lib/omnibot/workflow.rb', line 7

def state(*keys) = keys.any? ? state_keys.concat(keys) : state_keys

.state_keysObject



8
# File 'lib/omnibot/workflow.rb', line 8

def state_keys = @state_keys ||= []

.step(name, poll: nil, &body) ⇒ Object



10
11
12
# File 'lib/omnibot/workflow.rb', line 10

def step(name, poll: nil, &body)
  steps[name.to_sym] = { body: body, poll: poll }
end

.stepsObject



14
# File 'lib/omnibot/workflow.rb', line 14

def steps = @steps ||= {}

.timeout(step, after:, to:) ⇒ Object



23
24
25
# File 'lib/omnibot/workflow.rb', line 23

def timeout(step, after:, to:)
  timeouts[step.to_sym] = { after: after, to: to.to_sym }
end

.timeoutsObject



27
# File 'lib/omnibot/workflow.rb', line 27

def timeouts = @timeouts ||= {}

.transition(from:, to:, if: nil) ⇒ Object



16
17
18
19
# File 'lib/omnibot/workflow.rb', line 16

def transition(from:, to:, if: nil)
  cond = binding.local_variable_get(:if)
  transitions << { from: from.to_sym, to: to.to_sym, if: cond }
end

.transitionsObject



21
# File 'lib/omnibot/workflow.rb', line 21

def transitions = @transitions ||= []

.while_running(mode = nil) ⇒ Object



32
33
34
35
36
37
38
# File 'lib/omnibot/workflow.rb', line 32

def while_running(mode = nil)
  return @while_running || :ignore if mode.nil?
  unless WHILE_RUNNING_MODES.include?(mode)
    raise ArgumentError, "while_running must be :ignore or :interrupt"
  end
  @while_running = mode
end