class ScoutApm::AutoInstrument::Rails::Rewriter

Public Class Methods

new() click to toggle source
Calls superclass method
# 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

Public Instance Methods

instrument(source, file_name, line) click to toggle source
# 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]
  bt = ["#{file_name}:#{line}:in `#{method_name}'"]

  return [
    "::ScoutApm::AutoInstrument("+ source.dump + ",#{bt}){",
    "}"
  ]
end
on_and_asgn(node) click to toggle source
# File lib/scout_apm/auto_instrument/rails.rb, line 112
def on_and_asgn(node)
  process(node.children[1])
end
on_block(node) click to toggle source
# File lib/scout_apm/auto_instrument/rails.rb, line 87
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) click to toggle source
# File lib/scout_apm/auto_instrument/rails.rb, line 99
def on_mlhs(node)
  # Ignore / don't instrument multiple assignment (LHS).
  return
end
on_op_asgn(node) click to toggle source
# File lib/scout_apm/auto_instrument/rails.rb, line 104
def on_op_asgn(node)
  process(node.children[2])
end
on_or_asgn(node) click to toggle source
# File lib/scout_apm/auto_instrument/rails.rb, line 108
def on_or_asgn(node)
  process(node.children[1])
end
on_send(node) click to toggle source

Handle the method call AST node. If this method doesn't call `super`, no futher rewriting is applied to children.

Calls superclass method
# File lib/scout_apm/auto_instrument/rails.rb, line 117
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) click to toggle source

Look up 1 or more nodes to check if the parent exists and matches the given type. @param type [Symbol] the symbol type to match. @param up [Integer] how far up to look.

# File lib/scout_apm/auto_instrument/rails.rb, line 83
def parent_type?(type, up = 1)
  parent = @nesting[@nesting.size - up - 1] and parent.type == type
end
process(node) click to toggle source

Invoked for every AST node as it is processed top to bottom.

Calls superclass method
# File lib/scout_apm/auto_instrument/rails.rb, line 149
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