Sha256: a1200760560880f4c599be985aa6ed062f6258833dbaa02f67ab49e52728aa00

Contents?: true

Size: 1.92 KB

Versions: 1

Compression:

Stored size: 1.92 KB

Contents

module Mirrors
  module PackageInference
    class ClassToFileResolver
      def initialize
        @files = {}
      end

      def resolve(klass)
        return nil if klass.nil?
        try_fast(klass, klass.name)                   ||
          try_fast(klass.singleton_class, klass.name) ||
          try_slow(klass)                             ||
          try_slow(klass.singleton_class)
      end

      private

      def try_fast(klass, class_name)
        klass.instance_methods(false).each do |name|
          meth = klass.instance_method(name)

          file = begin
            sl = meth.source_location
            next unless sl
            sl[0]
          rescue MethodSource::SourceNotFoundError
            next
          end

          contents = (@files[file] ||= File.open(file, 'r') { |f| f.readpartial(4096) })
          n = class_name.sub(/.*::/, '') # last component of module name
          return file if contents =~ /^\s+(class|module) ([\S]+::)?#{Regexp.quote(n)}\s/
        end
        nil
      end

      def try_slow(klass)
        methods = klass
          .instance_methods(false)
          .map { |n| klass.instance_method(n) }

        defined_directly_on_class = methods
          .select do |meth|
            # as a mostly-useful heuristic, we just eliminate everything that was
            # defined using a template eval or define_method.
            begin
              meth.source =~ /\A\s+def (self\.)?#{Regexp.quote(meth.name)}/
            rescue MethodSource::SourceNotFoundError
              false
            end
          end

        files = Hash.new(0)

        defined_directly_on_class.each do |meth|
          begin
            sl = meth.source_location[0]
            raise unless sl
            files[sl[0]] += 1
          rescue MethodSource::SourceNotFoundError
            raise
          end
        end

        file = files.max_by { |_k, v| v }
        file ? file[0] : nil
      end
    end
  end
end

Version data entries

1 entries across 1 versions & 1 rubygems

Version Path
mirrors-0.0.2 lib/mirrors/package_inference/class_to_file_resolver.rb