module Packwerk::Node

Constants

BLOCK
CLASS
CONSTANT
CONSTANT_ASSIGNMENT
CONSTANT_ROOT_NAMESPACE
HASH
HASH_PAIR
Location
METHOD_CALL
MODULE
SELF
STRING
SYMBOL

Public Class Methods

class?(node) click to toggle source
# File lib/packwerk/node.rb, line 107
def class?(node)
  type_of(node) == CLASS
end
class_or_module_name(class_or_module_node) click to toggle source
# File lib/packwerk/node.rb, line 15
def class_or_module_name(class_or_module_node)
  case type_of(class_or_module_node)
  when CLASS, MODULE
    # (class (const nil :Foo) (const nil :Bar) (nil))
    #   "class Foo < Bar; end"
    # (module (const nil :Foo) (nil))
    #   "module Foo; end"
    identifier = class_or_module_node.children[0]
    constant_name(identifier)
  else
    raise TypeError
  end
end
constant?(node) click to toggle source
# File lib/packwerk/node.rb, line 99
def constant?(node)
  type_of(node) == CONSTANT
end
constant_assignment?(node) click to toggle source
# File lib/packwerk/node.rb, line 103
def constant_assignment?(node)
  type_of(node) == CONSTANT_ASSIGNMENT
end
constant_name(constant_node) click to toggle source
# File lib/packwerk/node.rb, line 29
def constant_name(constant_node)
  case type_of(constant_node)
  when CONSTANT_ROOT_NAMESPACE
    ""
  when CONSTANT, CONSTANT_ASSIGNMENT, SELF
    # (const nil :Foo)
    #   "Foo"
    # (const (cbase) :Foo)
    #   "::Foo"
    # (const (lvar :a) :Foo)
    #   "a::Foo"
    # (casgn nil :Foo (int 1))
    #   "Foo = 1"
    # (casgn (cbase) :Foo (int 1))
    #   "::Foo = 1"
    # (casgn (lvar :a) :Foo (int 1))
    #   "a::Foo = 1"
    # (casgn (self) :Foo (int 1))
    #   "self::Foo = 1"
    namespace, name = constant_node.children
    if namespace
      [constant_name(namespace), name].join("::")
    else
      name.to_s
    end
  else
    raise TypeError
  end
end
each_child(node) { |child| ... } click to toggle source
# File lib/packwerk/node.rb, line 59
def each_child(node)
  if block_given?
    node.children.each do |child|
      yield child if child.is_a?(Parser::AST::Node)
    end
  else
    enum_for(:each_child, node)
  end
end
enclosing_namespace_path(starting_node, ancestors:) click to toggle source
# File lib/packwerk/node.rb, line 69
def enclosing_namespace_path(starting_node, ancestors:)
  ancestors.select { |n| [CLASS, MODULE].include?(type_of(n)) }
    .each_with_object([]) do |node, namespace|
    # when evaluating `class Child < Parent`, the const node for `Parent` is a child of the class
    # node, so it'll be an ancestor, but `Parent` is not evaluated in the namespace of `Child`, so
    # we need to skip it here
    next if type_of(node) == CLASS && parent_class(node) == starting_node

    namespace.prepend(class_or_module_name(node))
  end
end
hash?(node) click to toggle source
# File lib/packwerk/node.rb, line 115
def hash?(node)
  type_of(node) == HASH
end
literal_value(string_or_symbol_node) click to toggle source
# File lib/packwerk/node.rb, line 81
def literal_value(string_or_symbol_node)
  case type_of(string_or_symbol_node)
  when STRING, SYMBOL
    # (str "foo")
    #   "'foo'"
    # (sym :foo)
    #   ":foo"
    string_or_symbol_node.children[0]
  else
    raise TypeError
  end
end
location(node) click to toggle source
# File lib/packwerk/node.rb, line 94
def location(node)
  location = node.location
  Location.new(location.line, location.column)
end
method_arguments(method_call_node) click to toggle source
# File lib/packwerk/node.rb, line 127
def method_arguments(method_call_node)
  raise TypeError unless method_call?(method_call_node)

  # (send (lvar :foo) :bar (int 1))
  #   "foo.bar(1)"
  method_call_node.children.slice(2..-1)
end
method_call?(node) click to toggle source
# File lib/packwerk/node.rb, line 111
def method_call?(node)
  type_of(node) == METHOD_CALL
end
method_name(method_call_node) click to toggle source
# File lib/packwerk/node.rb, line 135
def method_name(method_call_node)
  raise TypeError unless method_call?(method_call_node)

  # (send (lvar :foo) :bar (int 1))
  #   "foo.bar(1)"
  method_call_node.children[1]
end
module_name_from_definition(node) click to toggle source
# File lib/packwerk/node.rb, line 143
def module_name_from_definition(node)
  case type_of(node)
  when CLASS, MODULE
    # "class My::Class; end"
    # "module My::Module; end"
    class_or_module_name(node)
  when CONSTANT_ASSIGNMENT
    # "My::Class = ..."
    # "My::Module = ..."
    rvalue = node.children.last

    case type_of(rvalue)
    when METHOD_CALL
      # "Class.new"
      # "Module.new"
      constant_name(node) if module_creation?(rvalue)
    when BLOCK
      # "Class.new do end"
      # "Module.new do end"
      constant_name(node) if module_creation?(method_call_node(rvalue))
    end
  end
end
name_location(node) click to toggle source
# File lib/packwerk/node.rb, line 167
def name_location(node)
  location = node.location

  if location.respond_to?(:name)
    name = location.name
    Location.new(name.line, name.column)
  end
end
parent_class(class_node) click to toggle source
# File lib/packwerk/node.rb, line 176
def parent_class(class_node)
  raise TypeError unless type_of(class_node) == CLASS

  # (class (const nil :Foo) (const nil :Bar) (nil))
  #   "class Foo < Bar; end"
  class_node.children[1]
end
parent_module_name(ancestors:) click to toggle source
# File lib/packwerk/node.rb, line 185
def parent_module_name(ancestors:)
  definitions = ancestors
    .select { |n| [CLASS, MODULE, CONSTANT_ASSIGNMENT, BLOCK].include?(type_of(n)) }

  names = definitions.map do |definition|
    name_part_from_definition(definition)
  end.compact

  names.empty? ? "Object" : names.reverse.join("::")
end
string?(node) click to toggle source
# File lib/packwerk/node.rb, line 119
def string?(node)
  type_of(node) == STRING
end
symbol?(node) click to toggle source
# File lib/packwerk/node.rb, line 123
def symbol?(node)
  type_of(node) == SYMBOL
end
value_from_hash(hash_node, key) click to toggle source
# File lib/packwerk/node.rb, line 196
def value_from_hash(hash_node, key)
  raise TypeError unless hash?(hash_node)
  pair = hash_pairs(hash_node).detect { |pair_node| literal_value(hash_pair_key(pair_node)) == key }
  hash_pair_value(pair) if pair
end

Public Instance Methods

hash_pair_key(hash_pair_node) click to toggle source
# File lib/packwerk/node.rb, line 226
def hash_pair_key(hash_pair_node)
  raise TypeError unless type_of(hash_pair_node) == HASH_PAIR

  # (pair (int 1) (int 2))
  #   "1 => 2"
  # (pair (sym :answer) (int 42))
  #   "answer: 42"
  hash_pair_node.children[0]
end
hash_pair_value(hash_pair_node) click to toggle source
# File lib/packwerk/node.rb, line 236
def hash_pair_value(hash_pair_node)
  raise TypeError unless type_of(hash_pair_node) == HASH_PAIR

  # (pair (int 1) (int 2))
  #   "1 => 2"
  # (pair (sym :answer) (int 42))
  #   "answer: 42"
  hash_pair_node.children[1]
end
hash_pairs(hash_node) click to toggle source
# File lib/packwerk/node.rb, line 246
def hash_pairs(hash_node)
  raise TypeError unless hash?(hash_node)

  # (hash (pair (int 1) (int 2)) (pair (int 3) (int 4)))
  #   "{1 => 2, 3 => 4}"
  hash_node.children.select { |n| type_of(n) == HASH_PAIR }
end
method_call_node(block_node) click to toggle source
# File lib/packwerk/node.rb, line 254
def method_call_node(block_node)
  raise TypeError unless type_of(block_node) == BLOCK

  # (block (send (lvar :foo) :bar) (args) (int 42))
  #   "foo.bar do 42 end"
  block_node.children[0]
end
module_creation?(node) click to toggle source
# File lib/packwerk/node.rb, line 262
def module_creation?(node)
  # "Class.new"
  # "Module.new"
  method_call?(node) &&
    constant?(receiver(node)) &&
    ["Class", "Module"].include?(constant_name(receiver(node))) &&
    method_name(node) == :new
end
name_from_block_definition(node) click to toggle source
# File lib/packwerk/node.rb, line 271
def name_from_block_definition(node)
  if method_name(method_call_node(node)) == :class_eval
    receiver = receiver(node)
    constant_name(receiver) if receiver && constant?(receiver)
  end
end
name_part_from_definition(node) click to toggle source
# File lib/packwerk/node.rb, line 278
def name_part_from_definition(node)
  case type_of(node)
  when CLASS, MODULE, CONSTANT_ASSIGNMENT
    module_name_from_definition(node)
  when BLOCK
    name_from_block_definition(node)
  end
end
receiver(method_call_or_block_node) click to toggle source
# File lib/packwerk/node.rb, line 287
def receiver(method_call_or_block_node)
  case type_of(method_call_or_block_node)
  when METHOD_CALL
    method_call_or_block_node.children[0]
  when BLOCK
    receiver(method_call_node(method_call_or_block_node))
  else
    raise TypeError
  end
end