Class: Rigor::Plugin::NodeContext

Inherits:
Object
  • Object
show all
Defined in:
lib/rigor/plugin/node_context.rb

Overview

ADR-37 slice 1d — the lexical context of a node, handed to a Base.node_rule block as its fifth argument. Realises the ContextInfo ADR-2 § "Scope Object" promised: the enclosing class / module, method, and block-DSL nesting that a per-node rule needs but that Scope (value/type facts) does not carry.

The engine builds one per matching node from the descent stack (snapshotted, so it is safe to retain). Rules read the bits they need:

node_rule Prism::CallNode do |node, _scope, path, _fc, context|
action = context.enclosing_def&.name           # rails-i18n
model  = context.enclosing_block(:describe)     # shoulda

end

ancestors is the full chain, outermost first, EXCLUDING the node itself — the general primitive; the accessors below are conveniences derived from it.

Constant Summary collapse

EMPTY =

sentinel documented for readers; rules get a real instance

nil

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(ancestors) ⇒ NodeContext

Returns a new instance of NodeContext.



27
28
29
30
# File 'lib/rigor/plugin/node_context.rb', line 27

def initialize(ancestors)
  @ancestors = ancestors.dup.freeze
  freeze
end

Instance Attribute Details

#ancestorsObject (readonly)

Returns the value of attribute ancestors.



25
26
27
# File 'lib/rigor/plugin/node_context.rb', line 25

def ancestors
  @ancestors
end

Instance Method Details

#enclosing_block(method_name) ⇒ Object

The innermost enclosing Prism::CallNode that carries a block and whose method name is method_name — e.g. the RSpec.describe(Model) do … end a shoulda matcher sits in. Returns the CallNode (so the caller can read its arguments / receiver), or nil.



47
48
49
50
51
# File 'lib/rigor/plugin/node_context.rb', line 47

def enclosing_block(method_name)
  ancestors.rfind do |n|
    n.is_a?(Prism::CallNode) && n.block && n.name == method_name
  end
end

#enclosing_defObject

The innermost enclosing Prism::DefNode, or nil. Its #name is the method the node sits in (rails-i18n uses it to expand a lazy t('.key') against the controller action).



34
35
36
# File 'lib/rigor/plugin/node_context.rb', line 34

def enclosing_def
  ancestors.rfind { |n| n.is_a?(Prism::DefNode) }
end

#enclosing_moduleObject

The innermost enclosing Prism::ClassNode / Prism::ModuleNode, or nil (actionpack uses it to resolve the controller a before_action / render sits in).



40
41
42
# File 'lib/rigor/plugin/node_context.rb', line 40

def enclosing_module
  ancestors.rfind { |n| n.is_a?(Prism::ClassNode) || n.is_a?(Prism::ModuleNode) }
end