class LoadTracer

Constants

FileSpec
LOAD_METHODS
VERSION

Public Class Methods

new(format: nil, exclude_files: []) click to toggle source
# File lib/load_tracer.rb, line 52
def initialize(format: nil, exclude_files: [])
  @exclude_files = exclude_files
end
trace(format: nil, exclude_files: [], &block) click to toggle source
# File lib/load_tracer.rb, line 48
def self.trace(format: nil, exclude_files: [], &block)
  new(exclude_files: exclude_files).trace(format: format, &block)
end

Public Instance Methods

report(format:, dependencies:, reverse_dependencies:) click to toggle source
# File lib/load_tracer.rb, line 107
def report(format:, dependencies:, reverse_dependencies:)
  case format
  when :dot
    DotFormatter.export(
      dependencies: dependencies
    )
  when :json
    JsonFormatter.export(
      dependencies: dependencies,
      reverse_dependencies: reverse_dependencies
    )
  else
    DefaultFormatter.export(
      dependencies: dependencies,
      reverse_dependencies: reverse_dependencies
    )
  end
end
trace(format: nil) { || ... } click to toggle source
# File lib/load_tracer.rb, line 56
def trace(format: nil)
  @dependencies = Hash.new { |hash, key| hash[key] = [] }
  @reverse_dependencies = Hash.new { |hash, key| hash[key] = [] }
  @load_checked_features = Hash.new
  @not_found_features = []
  bl = caller_locations[1]
  @exclude_files << bl.absolute_path

  tracer.enable { yield }

  report(format: format, dependencies: @dependencies, reverse_dependencies: @reverse_dependencies)
end
tracer() click to toggle source
# File lib/load_tracer.rb, line 69
def tracer
  TracePoint.new(:return) do |tp|
    next unless LOAD_METHODS.include?(tp.method_id)
    next if tp.defined_class != ::Kernel
    next if tp.path != __FILE__

    bl = caller_locations[1]
    feature = get_feature(tp)

    if bl.absolute_path.nil?
      bl = find_caller_of_internal_library(feature)
    end

    next if @exclude_files.include?(bl.absolute_path)

    path = find_path(feature) || find_path(File.expand_path(feature, File.dirname(bl.absolute_path)))

    if path.nil?
      @not_found_features << feature
      next
    end

    @load_checked_features[bl.absolute_path] = true

    @dependencies[bl.absolute_path] << path
    @reverse_dependencies[path] << bl.absolute_path

    if !tp.return_value && !@load_checked_features[path]
      file_specs = StaticChecker.parse_file(path)

      file_specs.each do |fs|
        @dependencies[fs.path] |= fs.dependencies
        @reverse_dependencies[fs.path] |= fs.reverse_dependencies
      end
    end
  end
end

Private Instance Methods

find_caller_of_internal_library(feature) click to toggle source
# File lib/load_tracer.rb, line 145
def find_caller_of_internal_library(feature)
  index = caller_locations.find_index { |bl| bl.base_label == feature && bl.path == '<internal:prelude>' }
  caller_locations[index + 1]
end
find_path(feature) click to toggle source
# File lib/load_tracer.rb, line 139
def find_path(feature)
  RubyVM.resolve_feature_path(feature).last
rescue LoadError
  nil
end
get_feature(tp) click to toggle source
# File lib/load_tracer.rb, line 128
def get_feature(tp)
  params = tp.self.method(tp.method_id).parameters

  case tp.method_id
  when :require, :require_relative, :load
    tp.binding.local_variable_get(params.first.last)
  when :autoload
    tp.binding.local_variable_get(params.last.last)
  end
end