Class: ScoutApm::AutoInstrument::Rails::Rewriter
- Inherits:
-
Parser::TreeRewriter
- Object
- Parser::TreeRewriter
- ScoutApm::AutoInstrument::Rails::Rewriter
- Defined in:
- lib/scout_apm/auto_instrument/rails.rb
Instance Method Summary collapse
-
#initialize ⇒ Rewriter
constructor
A new instance of Rewriter.
- #instrument(source, file_name, line) ⇒ Object
- #on_and_asgn(node) ⇒ Object
- #on_block(node) ⇒ Object
- #on_mlhs(node) ⇒ Object
- #on_op_asgn(node) ⇒ Object
- #on_or_asgn(node) ⇒ Object
-
#on_send(node) ⇒ Object
Handle the method call AST node.
-
#parent_type?(type, up = 1) ⇒ Boolean
Look up 1 or more nodes to check if the parent exists and matches the given type.
-
#process(node) ⇒ Object
Invoked for every AST node as it is processed top to bottom.
Constructor Details
#initialize ⇒ Rewriter
Returns a new instance of Rewriter.
50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/scout_apm/auto_instrument/rails.rb', line 50 def initialize super # Keeps track of the parent - child relationship between nodes: @nesting = [] # The stack of method nodes (type :def): @method = [] # The stack of class nodes: @scope = [] @cache = Cache.new end |
Instance Method Details
#instrument(source, file_name, line) ⇒ Object
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
# File 'lib/scout_apm/auto_instrument/rails.rb', line 65 def instrument(source, file_name, line) # Don't log huge chunks of code... just the first line: if lines = source.lines and lines.count > 1 source = lines.first.chomp + "..." end method_name = @method.last.children[0] class_name = @scope.last.children[1] bt = ["#{file_name}:#{line}:in `#{method_name}'"] return [ "::ScoutApm::AutoInstrument("+ source.dump + ",#{bt}){", "}" ] end |
#on_and_asgn(node) ⇒ Object
113 114 115 |
# File 'lib/scout_apm/auto_instrument/rails.rb', line 113 def on_and_asgn(node) process(node.children[1]) end |
#on_block(node) ⇒ Object
88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/scout_apm/auto_instrument/rails.rb', line 88 def on_block(node) # If we are not in a method, don't do any instrumentation: return if @method.empty? line = node.location.line || 'line?' column = node.location.column || 'column?' # not used method_name = node.children[0].children[1] || '*unknown*' # not used file_name = @source_rewriter.source_buffer.name wrap(node.location.expression, *instrument(node.location.expression.source, file_name, line)) end |
#on_mlhs(node) ⇒ Object
100 101 102 103 |
# File 'lib/scout_apm/auto_instrument/rails.rb', line 100 def on_mlhs(node) # Ignore / don't instrument multiple assignment (LHS). return end |
#on_op_asgn(node) ⇒ Object
105 106 107 |
# File 'lib/scout_apm/auto_instrument/rails.rb', line 105 def on_op_asgn(node) process(node.children[2]) end |
#on_or_asgn(node) ⇒ Object
109 110 111 |
# File 'lib/scout_apm/auto_instrument/rails.rb', line 109 def on_or_asgn(node) process(node.children[1]) end |
#on_send(node) ⇒ Object
Handle the method call AST node. If this method doesn’t call ‘super`, no futher rewriting is applied to children.
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
# File 'lib/scout_apm/auto_instrument/rails.rb', line 118 def on_send(node) # We aren't interested in top level function calls: return if @method.empty? if @cache.local_assignments?(node) return super end # This ignores both initial block method invocation `*x*{}`, and subsequent nested invocations `x{*y*}`: return if parent_type?(:block) # Extract useful metadata for instrumentation: line = node.location.line || 'line?' column = node.location.column || 'column?' # not used method_name = node.children[1] || '*unknown*' # not used file_name = @source_rewriter.source_buffer.name # Wrap the expression with instrumentation: wrap(node.location.expression, *instrument(node.location.expression.source, file_name, line)) end |
#parent_type?(type, up = 1) ⇒ Boolean
Look up 1 or more nodes to check if the parent exists and matches the given type.
84 85 86 |
# File 'lib/scout_apm/auto_instrument/rails.rb', line 84 def parent_type?(type, up = 1) parent = @nesting[@nesting.size - up - 1] and parent.type == type end |
#process(node) ⇒ Object
Invoked for every AST node as it is processed top to bottom.
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/scout_apm/auto_instrument/rails.rb', line 150 def process(node) # We are nesting inside this node: @nesting.push(node) if node and node.type == :def # If the node is a method, push it on the method stack as well: @method.push(node) super @method.pop elsif node and node.type == :class @scope.push(node.children[0]) super @scope.pop else super end @nesting.pop end |