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

90

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

нравиться

A.new.method(:a).SOURCE_CODE
A.new.method(:a).FILE
Allenwei
источник

Ответы:

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])
Automatico
источник
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;
}
Fangxing
источник
это отличная идея для 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) ...

AlexChaffee
источник
1

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

require 'method_source'
User.method(:last).source
User.method(:last).source_location
Дориан
источник