Class: ChronoForge::DefinitionAnalyzer

Inherits:
Object
  • Object
show all
Defined in:
lib/chrono_forge/definition_analyzer.rb

Overview

Statically analyzes a workflow class's perform method (via Prism) into a conservative Definition graph. Reads SOURCE TEXT ONLY — never the DB, never executes workflow code. Unresolvable Ruby becomes a :dynamic node + warning.

Constant Summary collapse

DURABLE =

The durable DSL calls we recognize -> node kind.

{
  durably_execute: :execute, wait: :wait, wait_until: :wait_until,
  continue_if: :continue_if, branch: :branch, merge_branches: :merge,
  merge_branch: :merge, durably_repeat: :repeat
}.freeze
NAME_ARG =

Which argument carries the step NAME, per the real DSL signatures in executor/methods. pos is the 0-based positional index; kw = whether a name: keyword overrides it. wait(duration, name) is the one primitive whose name is the SECOND positional. merge_branches is special-cased in step_name_for (its name joins all positional branch names).

{
  durably_execute: {pos: 0, kw: true},
  wait: {pos: 1, kw: false},
  wait_until: {pos: 0, kw: false},
  continue_if: {pos: 0, kw: true},
  durably_repeat: {pos: 0, kw: true},
  branch: {pos: 0, kw: false}
}.freeze

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(workflow_class) ⇒ DefinitionAnalyzer

Returns a new instance of DefinitionAnalyzer.



33
34
35
36
37
38
39
# File 'lib/chrono_forge/definition_analyzer.rb', line 33

def initialize(workflow_class)
  @klass = workflow_class
  @nodes = []
  @edges = []
  @warnings = []
  @seq = 0
end

Class Method Details

.call(workflow_class) ⇒ Object



31
# File 'lib/chrono_forge/definition_analyzer.rb', line 31

def self.call(workflow_class) = new(workflow_class).call

Instance Method Details

#callObject



41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/chrono_forge/definition_analyzer.rb', line 41

def call
  _file, method_node, defs = locate_perform
  return unavailable unless method_node

  @defs = defs # name(Symbol) => Prism::DefNode, for same-class helper tracing
  walk(method_node.body, "start") # builds @nodes/@edges as a side effect
  if @nodes.empty?
    @warnings << "no durable steps found in this workflow's perform — it may be " \
      "empty, or drive its steps through code this static analysis can't follow"
  end
  Definition.new(nodes: @nodes, edges: @edges, warnings: @warnings)
rescue => e
  unavailable("analysis error: #{e.class}: #{e.message}")
end