¿Cómo puedo obtener el código fuente de un método de forma dinámica y también en qué archivo se encuentra este método?

90

Me gustaría saber si puedo obtener el código fuente de un método sobre la marcha, y si puedo obtener en qué archivo se encuentra este método.

me gusta

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

Respuestas:

117

Utilizar 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"

Tenga en cuenta que, para los métodos integrados, source_locationdevuelve nil. Si desea consultar el código fuente de C (¡diviértase!), Tendrá que buscar el archivo C correcto (están más o menos organizados por clase) y encontrar rb_define_methodel método (hacia el final del archivo ).

En Ruby 1.8 este método no existe, pero puedes usar esta gema .

Marc-André Lafortune
fuente
2
Hola, soy del futuro y estoy usando Ruby 2.6.1. Quiero el código fuente de String#include?. Hasta ahora String.instance_method(:include?).source_locationvuelve nil.
S.Goswami
39

Ninguna de las respuestas hasta ahora muestra cómo mostrar el código fuente de un método sobre la marcha ...

En realidad, es muy fácil si usa la increíble gema 'method_source' de John Mair (el creador de Pry): el método debe implementarse en Ruby (no en C) y debe cargarse desde un archivo (no irb).

Aquí hay un ejemplo que muestra el código fuente del método en la consola de Rails con 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 

Ver también:

Tilo
fuente
1
Siempre eché de menos esta función en Ruby. Lisp puede hacer esto :)
Tilo
Viniendo de Clojure's source. Esto funciona como se esperaba.
Sebastian Palma
Recibo este error: [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'
Abram
RSpec.method(:to_json).source_locationaunque funciona bien
Abram
17

A continuación se explica cómo imprimir el código fuente de ruby:

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

Sin dependencias

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]

Si desea usar esto de manera más conveniente, puede abrir la Methodclase:

# ~/.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

Y luego solo llama method.source

Con palanca , puede usar show-methodpara ver la fuente de un método, e incluso puede ver un código fuente de ruby ​​c pry-docinstalado, según el documento de pry en codde-browing

Tenga en cuenta que también podemos ver los métodos C (de Ruby Core) usando el complemento pry-doc; también mostramos la sintaxis alternativa para 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
fuente
esa es una gran idea para un sourcemétodo dentro de la Methodclase. Sería incluso mejor si procesara el texto y cuándo dejar de imprimir porque llegó al final del método.
Toby 1 Kenobi
4

Creé la gema "ri_for" para este propósito

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

... genera la fuente (y la ubicación, si está en 1.9).

GL. -r

Rogerdpack
fuente
Todo esto para mí está produciendo una falla de segmentación. :(
panzi
¿Cómo reproducir la falla seg? que método / clase?
rogerdpack
1

Tuve que implementar una función similar (tomar la fuente de un bloque) como parte de Wrong y puedes ver cómo (y tal vez incluso reutilizar el código) en chunk.rb (que se basa en RubyParser de Ryan Davis, así como en algunos bastante divertidos código glomming del archivo fuente ). Tendrías que modificarlo para usarlo Method#source_locationy tal vez ajustar algunas otras cosas para que incluya o no eldef .

Por cierto, creo que Rubinius tiene esta función incorporada. Por alguna razón, se ha dejado fuera de MRI (la implementación estándar de Ruby), de ahí este truco.

¡Oooh, me gustan algunas de las cosas en method_source ! Como usar eval para saber si una expresión es válida (y seguir glomming líneas fuente hasta que deje de recibir errores de análisis, como lo hace Chunk) ...

AlexChaffee
fuente
1

Los métodos internos no tienen origen ni ubicación de origen (p Integer#to_s. Ej. )

require 'method_source'
User.method(:last).source
User.method(:last).source_location
dorio
fuente