Wie kann ich den Quellcode einer Methode dynamisch abrufen und in welcher Datei befindet sich diese Methode?

88

Ich möchte wissen, ob ich im laufenden Betrieb Quellcode für eine Methode erhalten kann und ob ich die Datei erhalten kann, in der sich diese Methode befindet.

mögen

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

Antworten:

113

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

Beachten Sie, dass für builtin Methoden, source_locationkehrt nil. Wenn Sie den C-Quellcode überprüfen möchten (viel Spaß!), Müssen Sie nach der richtigen C-Datei suchen (sie sind mehr oder weniger nach Klassen organisiert) und die rb_define_methodfür die Methode finden (gegen Ende der Datei) ).

In Ruby 1.8 existiert diese Methode nicht, aber Sie können dieses Juwel verwenden .

Marc-André Lafortune
quelle
2
Hallo, ich komme aus der Zukunft und benutze Ruby 2.6.1! Ich möchte den Quellcode von String#include?. Soweit String.instance_method(:include?).source_locationkehrt zurück nil.
Goswami
39

Keine der bisherigen Antworten zeigt, wie der Quellcode einer Methode im laufenden Betrieb angezeigt wird ...

Es ist eigentlich sehr einfach, wenn Sie das großartige Juwel 'method_source' von John Mair (dem Hersteller von Pry) verwenden: Die Methode muss in Ruby (nicht C) implementiert und aus einer Datei (nicht irb) geladen werden.

Hier ist ein Beispiel für die Anzeige des Methodenquellcodes in der Rails-Konsole mit 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 

Siehe auch:

Tilo
quelle
1
Ich habe diese Funktion in Ruby immer verpasst. Lisp kann das :)
Tilo
Ich komme aus der Clojure source. Dies funktioniert wie erwartet.
Sebastian Palma
Ich erhalte diesen Fehler: [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_locationfunktioniert aber gut
Abram
17

So drucken Sie den Quellcode von Ruby aus:

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

Ohne Abhängigkeiten

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]

Wenn Sie dies bequemer verwenden möchten, können Sie die MethodKlasse öffnen :

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

Und dann einfach anrufen method.source

Mit Pry können Sie die verwenden show-method, um eine Methodenquelle anzuzeigen, und Sie können sogar Ruby C-Quellcode mit pry-docinstalliertem Code anzeigen, wie in Prys Dokument in Codde-Browing angegeben

Beachten Sie, dass wir C-Methoden (aus Ruby Core) auch mit dem pry-doc-Plugin anzeigen können. Wir zeigen auch die alternative Syntax für die Show-Methode:

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
quelle
Das ist eine großartige Idee für eine sourceMethode innerhalb der MethodKlasse. Es wäre sogar noch besser, wenn der Text verarbeitet würde und neu, wann der Druckvorgang beendet werden soll, da das Ende der Methode erreicht ist.
Toby 1 Kenobi
4

Zu diesem Zweck habe ich das Juwel "ri_for" erstellt

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

... gibt die Quelle aus (und den Ort, wenn Sie auf 1.9 sind).

GL. -r

Rogerdpack
quelle
Für mich bedeutet dies lediglich einen Segmentierungsfehler. :(
Panzi
Wie reproduziere ich Seg-Fehler? welche Methode / Klasse?
Rogerdpack
1

Ich musste eine ähnliche Funktion (greifen Sie nach der Quelle eines Blocks) als Teil von Wrong implementieren, und Sie können sehen, wie (und vielleicht sogar den Code wiederverwenden) in chunk.rb (das sich auf Ryan Davis 'RubyParser stützt, sowie einige ziemlich lustige Quelldatei glomming Code ). Sie müssten es ändern, um es zu verwenden, Method#source_locationund vielleicht einige andere Dinge optimieren, damit es das enthält oder nicht def.

Übrigens, ich denke, Rubinius hat diese Funktion eingebaut. Aus irgendeinem Grund wurde sie aus der MRT (der Standard-Ruby-Implementierung) herausgelassen, daher dieser Hack.

Oooh, ich mag einige Sachen in method_source ! Wie mit eval zu sagen , ob ein Ausdruck gültig ist (und halten glomming Source - Leitungen , bis Sie aufhören immer Parse - Fehler, wie Chunk der Fall ist) ...

AlexChaffee
quelle
1

Interne Methoden haben keine Quelle oder Quellenort (zB Integer#to_s)

require 'method_source'
User.method(:last).source
User.method(:last).source_location
Dorian
quelle