Bir yöntemin kaynak kodunu dinamik olarak nasıl alabilirim ve ayrıca bu yöntemin hangi dosyada bulunduğunu


90

Anında bir yöntem kaynak kodu alıp alamayacağımı ve bu yöntemin hangi dosyaya ulaşıp ulaşamayacağını bilmek istiyorum.

sevmek

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

Yanıtlar:


117

Kullanım 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"

Yerleşik yöntemler için source_locationdöndüğünü unutmayın nil. C kaynak kodunu kontrol etmek istiyorsanız (iyi eğlenceler!), Doğru C dosyasını aramanız (aşağı yukarı sınıfa göre düzenlenmiştir) ve rb_define_methodyöntemi bulmanız gerekir (dosyanın sonuna doğru) ).

Ruby 1.8'de bu yöntem yoktur, ancak bu mücevheri kullanabilirsiniz .


2
Merhaba, ben gelecekten geliyorum, Ruby 2.6.1 kullanıyorum! Kaynak kodunu istiyorum String#include?. Şimdiye kadar String.instance_method(:include?).source_locationgeri dönüyor nil.
S.Goswami

39

Şimdiye kadarki yanıtların hiçbiri, bir yöntemin kaynak kodunun anında nasıl görüntüleneceğini göstermiyor ...

John Mair'in (Pry'nin yapımcısı) harika 'method_source' mücevherini kullanırsanız, aslında çok kolaydır: Yöntem Ruby'de uygulanmalıdır (C değil) ve bir dosyadan (irb değil) yüklenmelidir.

Method_source ile Rails konsolundaki yöntem kaynak kodunu görüntüleyen bir örnek:

  $ 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 

Ayrıca bakınız:


1
Ruby'de bu özelliği hep kaçırmışımdır. Lisp bunu yapabilir :)
Tilo

Clojure'dan geliyor source. Bu beklendiği gibi çalışıyor.
Sebastian Palma

Şu hatayı alıyorum: [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_locationyine de iyi çalışıyor
Abram

17

Ruby'den kaynak kodunu şu şekilde yazdırabilirsiniz:

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

10

Bağımlılıklar olmadan

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]

Bunu daha rahat kullanmak isterseniz Methodsınıfı açabilirsiniz :

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

Ve sonra sadece ara method.source

Pry ileshow-method bir yöntem kaynağını görüntülemek için kullanabilirsiniz ve hatta kodda pry-docgöz atma belgesine göre , yüklü bir Ruby c kaynak kodunu bile görebilirsiniz.

Ayrıca pry-doc eklentisini kullanarak C yöntemlerini (Ruby Core'dan) görüntüleyebileceğimizi unutmayın; show-method için alternatif sözdizimini de gösteriyoruz:

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;
}

bu source, Methodsınıfın içindeki bir yöntem için harika bir fikir . Metni işlese ve yöntemin sonuna geldiği için yazdırmayı ne zaman durdurması gerektiğini yenerse daha da iyi olurdu.
Toby 1 Kenobi

4

Bu amaçla "ri_for" mücevherini yarattım

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

... kaynağı (ve 1.9'daysanız konumu) verir.

GL. -r


Bütün bunlar benim için bir segmentasyon hatası üretiyor. :(
panzi

segment hatası nasıl yeniden üretilir? hangi yöntem / sınıf?
rogerdpack

1

Ben bir parçası olarak benzer bir özellik (kapmak bir bloğun kaynağı) uygulamak zorunda Yanlış ve içinde (hatta kodu yeniden ve belki) nasıl görebilirsiniz chunk.rb oldukça komik bazı yanı sıra Ryan Davis' RubyParser dayanır ( kaynak dosyası glomming kodu ). Kullanmak için değiştirmeniz Method#source_locationve belki başka şeyler üzerinde ince ayar yapmanız gerekir, böylece def.

BTW Bence Rubinius bu özelliğe sahip. Nedense MRI (standart Ruby uygulaması) dışında bırakıldı, dolayısıyla bu hack.

Oooh, method_source'daki bazı şeyleri beğendim ! Bir ifadenin geçerli olup olmadığını söylemek için eval kullanmak gibi (ve Chunk'ın yaptığı gibi, ayrıştırma hataları almayı bırakana kadar kaynak satırlarını parlatmaya devam edin) ...


1

Dahili yöntemlerin kaynak veya kaynak konumu yoktur (ör. Integer#to_s)

require 'method_source'
User.method(:last).source
User.method(:last).source_location
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.