Module: Rigor::ModuleGraph::ConstantName

Defined in:
lib/rigor/module_graph/constant_name.rb

Overview

Resolves a fully-qualified constant name from Prism AST nodes and lexical ancestor chains.

Three things this module handles that the bare Prism API does not give you in one place:

  • Owner from lexical nesting. node.constant_path.full_name on a class Billing::Invoice only returns "Invoice"; the outer module Billing does not enter unless we walk ancestors ourselves.
  • Absolute paths (::Foo::Bar). Prism encodes the leading :: as an empty-symbol :"" in full_name_parts; we render it as "::Foo::Bar".
  • Mixed AST shapes. ClassNode#constant_path is either a ConstantReadNode (single name) or a ConstantPathNode (dotted path); same for superclass and include args.

Class Method Summary collapse

Class Method Details

.full_name_for_path(node) ⇒ Object



74
75
76
77
78
79
80
81
82
83
# File 'lib/rigor/module_graph/constant_name.rb', line 74

def full_name_for_path(node)
  parts = node.full_name_parts
  # Prism encodes a leading `::` as an empty-symbol first
  # part. Render it as a literal `"::"` prefix.
  if parts.first == :""
    "::" + parts.drop(1).join("::")
  else
    parts.join("::")
  end
end

.lexical_owner(context) ⇒ Object

Build the lexical-nesting owner string for a node, by walking context.ancestors from outer to inner and joining every enclosing ClassNode/ModuleNode constant path with ::. Returns nil when no class/module encloses the node.



43
44
45
46
47
48
# File 'lib/rigor/module_graph/constant_name.rb', line 43

def lexical_owner(context)
  parts = lexical_parts(context.ancestors)
  return nil if parts.empty?

  parts.join("::")
end

.lexical_owner_with(context, extra) ⇒ Object

Same as #lexical_owner, but with extra appended as the innermost element. Used to build the owner of a class or module decl itself: pass context.ancestors (which does NOT include the node itself) plus the node's own constant path.



54
55
56
57
58
59
60
# File 'lib/rigor/module_graph/constant_name.rb', line 54

def lexical_owner_with(context, extra)
  parts = lexical_parts(context.ancestors)
  parts << extra unless extra.nil? || extra.empty?
  return nil if parts.empty?

  parts.join("::")
end

.lexical_parts(ancestors) ⇒ Object



62
63
64
65
66
67
68
69
70
71
72
# File 'lib/rigor/module_graph/constant_name.rb', line 62

def lexical_parts(ancestors)
  ancestors.flat_map do |ancestor|
    case ancestor
    when Prism::ClassNode, Prism::ModuleNode
      name = render(ancestor.constant_path)
      name ? [name] : []
    else
      []
    end
  end
end

.render(node) ⇒ Object

Render a single Prism constant node into a string like "Foo", "Foo::Bar", or "::Foo::Bar". Returns nil when the node is not a constant carrier (e.g. include SOME_VAR where the arg is a CallNode).



30
31
32
33
34
35
36
37
# File 'lib/rigor/module_graph/constant_name.rb', line 30

def render(node)
  case node
  when Prism::ConstantReadNode
    node.name.to_s
  when Prism::ConstantPathNode
    full_name_for_path(node)
  end
end