Как я могу получить исходный код метода динамически, а также в каком файле находится этот метод.


90

Я хотел бы знать, могу ли я получить исходный код метода на лету и могу ли я узнать, в каком файле находится этот метод.

нравиться

A.new.method(:a).SOURCE_CODE
A.new.method(:a).FILE

Ответы:


117

Использование source_location:

class A
  def foo
  end
end

file, line = A.instance_method(:foo).source_location
# or
file, line = A.new.method(:foo).source_location
puts "Method foo is defined in #{file}, line #{line}"
# => "Method foo is defined in temp.rb, line 2"

Обратите внимание, что для встроенных методов source_locationвозвращает nil. Если вы хотите проверить исходный код C (получайте удовольствие!), Вам нужно будет найти правильный файл C (они более или менее организованы по классам) и найти rb_define_methodметод (ближе к концу файла ).

В Ruby 1.8 этого метода не существует, но вы можете использовать этот гем .


2
Привет, я из будущего, использую Ruby 2.6.1! Мне нужен исходный код String#include?. Пока String.instance_method(:include?).source_locationвозвращается nil.
S.Goswami

39

Ни один из ответов пока не показывает, как отображать исходный код метода на лету ...

На самом деле это очень просто, если вы используете замечательный гем method_source от Джона Мэра (создателя Pry): метод должен быть реализован на Ruby (а не на C) и должен быть загружен из файла (не irb).

Вот пример, показывающий исходный код метода в консоли Rails с помощью method_source:

  $ rails console
  > require 'method_source'
  > I18n::Backend::Simple.instance_method(:lookup).source.display
    def lookup(locale, key, scope = [], options = {})
      init_translations unless initialized?
      keys = I18n.normalize_keys(locale, key, scope, options[:separator])

      keys.inject(translations) do |result, _key|
        _key = _key.to_sym
        return nil unless result.is_a?(Hash) && result.has_key?(_key)
        result = result[_key]
        result = resolve(locale, _key, result, options.merge(:scope => nil)) if result.is_a?(Symbol)
        result
      end
    end
    => nil 

Смотрите также:


1
Мне всегда не хватало этой возможности в Ruby. Lisp может это сделать :)
Tilo

Исходя из Clojure's source. Это работает, как ожидалось.
Себастьян Пальма

Я получаю такую ​​ошибку: [1] pry(main)> RSpec.method(:class_exec).source MethodSource::SourceNotFoundError: Could not locate source for class_exec! from /home/vagrant/.bundle/foo/ruby/2.5.0/gems/method_source-0.9.2/lib/method_source.rb:24:in `source_helper'
Абрам

RSpec.method(:to_json).source_locationхотя работает нормально
Абрам

17

Вот как распечатать исходный код из ruby:

puts File.read(OBJECT_TO_GET.method(:METHOD_FROM).source_location[0])

10

Без зависимостей

method = SomeConstant.method(:some_method_name)
file_path, line = method.source_location
# puts 10 lines start from the method define 
IO.readlines(file_path)[line-1, 10]

Если вы хотите использовать это более удобно, вы можете открыть Methodкласс:

# ~/.irbrc
class Method
  def source(limit=10)
    file, line = source_location
    if file && line
      IO.readlines(file)[line-1,limit]
    else
      nil
    end
  end
end

А потом просто позвони method.source

С помощью Pry вы можете использовать show-methodдля просмотра источника метода, и вы даже можете увидеть некоторый исходный код ruby ​​c с pry-docустановленным, согласно документу pry в codde-browing

Обратите внимание, что мы также можем просматривать методы C (из Ruby Core) с помощью плагина pry-doc; мы также демонстрируем альтернативный синтаксис для show-method:

pry(main)> show-method Array#select

From: array.c in Ruby Core (C Method):
Number of lines: 15

static VALUE
rb_ary_select(VALUE ary)
{
    VALUE result;
    long i;

    RETURN_ENUMERATOR(ary, 0, 0);
    result = rb_ary_new2(RARRAY_LEN(ary));
    for (i = 0; i < RARRAY_LEN(ary); i++) {
        if (RTEST(rb_yield(RARRAY_PTR(ary)[i]))) {
            rb_ary_push(result, rb_ary_elt(ary, i));
        }
    }
    return result;
}

это отличная идея для sourceметода внутри Methodкласса. Было бы даже лучше, если бы он обработал текст и новый, когда прекратить печать, потому что он достиг конца метода.
Тоби 1 Кеноби,

4

Я создал для этого гем "ri_for"

 >> require 'ri_for'
 >> A.ri_for :foo

... выводит источник (и местоположение, если вы используете 1.9).

GL. -р


Все это приводит к ошибке сегментации. :(
panzi 09

как воспроизвести ошибку сегмента? какой метод / класс?
rogerdpack

1

Мне пришлось реализовать аналогичную функцию (захватить источник блока) как часть Wrong, и вы можете увидеть, как (и, возможно, даже повторно использовать код) в chunk.rb (который опирается на RubyParser Райана Дэвиса, а также на довольно забавные исходный файл glomming code ). Вам придется изменить его, чтобы использовать Method#source_locationи, возможно, настроить некоторые другие вещи, чтобы он включал или не включал def.

Кстати, я думаю, что в Rubinius встроена эта функция. По какой-то причине она была исключена из MRI (стандартной реализации Ruby), отсюда и этот взлом.

Ооо, мне нравятся некоторые вещи в method_source ! Это похоже на использование eval, чтобы определить, допустимо ли выражение (и продолжайте показывать исходные строки, пока вы не перестанете получать ошибки синтаксического анализа, как это делает Chunk) ...


1

Внутренние методы не имеют источника или местоположения источника (например Integer#to_s)

require 'method_source'
User.method(:last).source
User.method(:last).source_location
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.