Як я можу динамічно отримувати вихідний код методу, а також у якому файлі знаходиться цей метод


89

Я хотів би знати, чи можу я отримати метод вихідного коду на льоту, і чи можу я отримати, у якому файлі знаходиться цей метод.

люблю

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

Відповіді:


116

Використання 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 може це зробити :)
Тіло

Походить з 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

Зверніть увагу, що ми також можемо переглядати методи 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. -r


Все це робить для мене породжує помилку сегментації. :(
panzi

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

1

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

До речі, я думаю, що у Rubinius вбудована ця функція. З якоїсь причини вона не була включена в МРТ (стандартна реалізація Ruby), отже, і цей злом.

Ооо, мені подобається частина речей у method_source ! Як використання eval, щоб визначити, чи є вираз дійсним (і продовжуйте містити рядки джерела, доки ви не перестанете отримувати помилки синтаксичного аналізу, як це робить Чанк) ...


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.