Class: MOSAIK::Extractors::Structural::Processor

Inherits:
AST::Processor
  • Object
show all
Defined in:
lib/mosaik/extractors/structural/processor.rb

Overview

Abstract Syntax Tree parser for Ruby code

Constant Summary collapse

MAIN =

Magic value for root namespace

"(main)"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(tree) ⇒ Processor

Returns a new instance of Processor.



16
17
18
19
20
21
# File 'lib/mosaik/extractors/structural/processor.rb', line 16

def initialize(tree)
  super()

  @tree = tree
  @current_class = MAIN
end

Instance Attribute Details

#current_classObject

Returns the value of attribute current_class.



14
15
16
# File 'lib/mosaik/extractors/structural/processor.rb', line 14

def current_class
  @current_class
end

#current_methodObject

Returns the value of attribute current_method.



14
15
16
# File 'lib/mosaik/extractors/structural/processor.rb', line 14

def current_method
  @current_method
end

#treeObject (readonly)

Returns the value of attribute tree.



13
14
15
# File 'lib/mosaik/extractors/structural/processor.rb', line 13

def tree
  @tree
end

Instance Method Details

#on_begin(node) ⇒ Object

Method bodies



108
109
110
# File 'lib/mosaik/extractors/structural/processor.rb', line 108

def on_begin(node)
  node.children.each { |c| process(c) }
end

#on_class(node) ⇒ Object



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/mosaik/extractors/structural/processor.rb', line 23

def on_class(node)
  class_name = constant_name_from(node.children[0])

  # Build fully qualified class name
  self.current_class = current_class == MAIN ? class_name : [current_class, class_name].join("::")

  # Register class
  tree[current_class]

  debug "Class #{current_class} in #{node.loc.expression.source_buffer.name}:#{node.loc.line}"

  # Traverse the AST
  node.children.each { |c| process(c) }

  # Remove current class from the namespace
  self.current_class = current_class
    .split("::")
    .tap { |c| c.delete(class_name) }
    .join("::")
    .presence || MAIN
end

#on_def(node) ⇒ Object

Instance methods



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/mosaik/extractors/structural/processor.rb', line 68

def on_def(node)
  method_name = node.children[0].to_s
  file = node.loc.expression.source_buffer.name
  line_num = node.loc.line

  # Set current method
  self.current_method = method_name

  debug "Class instance method #{current_class}##{method_name} in #{file}:#{line_num}"

  tree[current_class].add_method(method_name, file, line_num)

  # Traverse the AST (first two children are method name and arguments)
  node.children[2..].each { |c| process(c) }

  # Reset current method
  self.current_method = nil
end

#on_defs(node) ⇒ Object

Class methods



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/mosaik/extractors/structural/processor.rb', line 88

def on_defs(node)
  method_name = "self.#{node.children[1]}"
  file = node.loc.expression.source_buffer.name
  line_num = node.loc.line

  # Set current method
  self.current_method = method_name

  debug "Class method #{current_class}.#{node.children[1]} in #{file}:#{line_num}"

  tree[current_class].add_method(method_name, file, line_num)

  # Traverse the AST (first two children are method name and arguments)
  node.children[2..].each { |c| process(c) }

  # Reset current method
  self.current_method = nil
end

#on_module(node) ⇒ Object



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/mosaik/extractors/structural/processor.rb', line 45

def on_module(node)
  module_name = constant_name_from(node.children[0])

  # Build fully qualified class name
  self.current_class = current_class == MAIN ? module_name : [current_class, module_name].join("::")

  # Register module
  tree[current_class]

  debug "Module #{current_class} in #{node.loc.expression.source_buffer.name}:#{node.loc.line}"

  # Traverse the AST
  node.children.each { |c| process(c) }

  # Remove current module from the namespace
  self.current_class = current_class
    .split("::")
    .tap { |c| c.delete(module_name) }
    .join("::")
    .presence || MAIN
end

#on_send(node) ⇒ Object

Method calls



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/mosaik/extractors/structural/processor.rb', line 113

def on_send(node)
  receiver, callee = method_from(node)

  constant_name = constant_name_from(receiver)

  # TODO: handle method calls on variables
  return if constant_name.blank?

  debug "Excluding constant #{constant_name} in #{node.loc.expression.source_buffer.name}:#{node.loc.line}" and return if constant_name.in? exclusions[:classes]

  debug "Reference to #{constant_name}##{callee} from #{current_class}##{current_method} in #{node.loc.expression.source_buffer.name}:#{node.loc.line}"

  warn "No sender for method call #{constant_name}##{callee}" and return if current_class == MAIN

  tree[current_class].methods[current_method].references << Syntax::Reference.new(tree[constant_name], callee)
end