Class: Plumb::MermaidVisitor

Inherits:
Object
  • Object
show all
Includes:
VisitorHandlers
Defined in:
lib/plumb/mermaid_visitor.rb

Overview

Renders a type composition as a Mermaid flowchart, so the flow-control algebra can be visualised. >> (And) becomes sequential arrows; | (Or) becomes a fork — the predecessor fans out to each alternative.

Every visit returns { entries:, exits: } — the node ids an incoming arrow should point INTO, and the ids outgoing arrows leave FROM — and accumulates node/edge lines as a side effect (the same stateful pattern JSONSchemaVisitor uses for $defs). And connects left.exits -> right.entries (a join over a preceding Or's branches); Or unions both branches' entries and exits without a node of its own, so whatever precedes it forks to both.

Examples:

((A >> B) | (C >> (D | B))).to_mermaid

Constant Summary

Constants included from VisitorHandlers

VisitorHandlers::NODE_NAME_FALLBACKS

Class Method Summary collapse

Instance Method Summary collapse

Methods included from VisitorHandlers

included, #visit, #visit_children, #visit_name

Constructor Details

#initializeMermaidVisitor

Returns a new instance of MermaidVisitor.



26
27
28
29
30
# File 'lib/plumb/mermaid_visitor.rb', line 26

def initialize
  @nodes = []
  @edges = []
  @counter = 0
end

Class Method Details

.call(node, direction: 'LR') ⇒ Object



22
23
24
# File 'lib/plumb/mermaid_visitor.rb', line 22

def self.call(node, direction: 'LR')
  new.render(node, direction:)
end

Instance Method Details

#on_missing_handler(node, _props, _method_name) ⇒ Object

Unknown nodes render as a single labeled box rather than raising, so any leaf type (Step, Transform, Constraint, Hash, Array, Boolean, …) degrades gracefully — mirrors MetadataVisitor's recursing override.



50
51
52
# File 'lib/plumb/mermaid_visitor.rb', line 50

def on_missing_handler(node, _props, _method_name)
  box(node)
end

#render(node, direction: 'LR') ⇒ Object



32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/plumb/mermaid_visitor.rb', line 32

def render(node, direction: 'LR')
  root = visit(node)
  # A top-level Or has more than one entry; anchor them to a synthetic start
  # node so the diagram reads as a single connected fork.
  entries = root[:entries]
  if entries.size > 1
    @nodes.unshift('start(( ))')
    @edges = entries.map { |e| "start --> #{e}" } + @edges
  end

  lines = ["flowchart #{direction}"]
  (@nodes + @edges).each { |line| lines << "  #{line}" }
  lines.join("\n")
end