Bagaimana saya bisa mendapatkan kode sumber dari suatu metode secara dinamis dan juga di file mana metode ini ditemukan

90

Saya ingin tahu apakah saya bisa mendapatkan kode sumber suatu metode dengan cepat, dan apakah saya bisa mendapatkan file mana dengan metode ini.

Suka

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

Jawaban:

117

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

Perhatikan bahwa untuk metode bawaan, source_locationmengembalikan nil. Jika ingin memeriksa kode sumber C (selamat bersenang-senang!), Anda harus mencari file C yang tepat (mereka kurang lebih diatur berdasarkan kelas) dan menemukan rb_define_methodmetode untuk (menjelang akhir file ).

Di Ruby 1.8 metode ini tidak ada, tetapi Anda dapat menggunakan permata ini .

Marc-André Lafortune
sumber
2
Hai, Saya dari masa depan, menggunakan Ruby 2.6.1! Saya ingin kode sumber String#include?. Sejauh ini String.instance_method(:include?).source_locationkembali nil.
S.Goswami
39

Sejauh ini tidak ada jawaban yang menunjukkan cara menampilkan kode sumber dari suatu metode dengan cepat ...

Sebenarnya sangat mudah jika Anda menggunakan permata 'method_source' yang mengagumkan dari John Mair (pembuat Pry): Metode ini harus diimplementasikan di Ruby (bukan C), dan harus dimuat dari file (bukan irb).

Berikut adalah contoh yang menampilkan kode sumber metode di konsol Rails dengan 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 

Lihat juga:

Tilo
sumber
1
Saya selalu melewatkan fitur ini di Ruby. Lisp bisa melakukan ini :)
Tilo
Berasal dari Clojure's source. Ini bekerja seperti yang diharapkan.
Sebastian Palma
Saya mendapatkan kesalahan ini: [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_locationbekerja dengan baik meskipun
Abram
17

Berikut adalah cara mencetak kode sumber dari ruby:

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

Tanpa ketergantungan

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]

Jika Anda ingin menggunakan ini lebih nyaman, Anda dapat membuka Methodkelas:

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

Dan kemudian panggil saja method.source

Dengan Pry Anda dapat menggunakan show-methoduntuk melihat sumber metode, dan Anda bahkan dapat melihat beberapa kode sumber ruby ​​c dengan pry-docdiinstal, menurut dokumen pry di codde-browing

Perhatikan bahwa kita juga dapat melihat metode C (dari Ruby Core) menggunakan plugin pry-doc; kami juga memamerkan sintaks alternatif untuk metode-pertunjukan:

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
sumber
itu ide bagus untuk sourcemetode di dalam Methodkelas. Akan lebih baik lagi jika teks itu diproses dan baru kapan harus berhenti mencetak karena sudah mencapai akhir metode.
Toby 1 Kenobi
4

Saya membuat permata "ri_for" untuk tujuan ini

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

... mengeluarkan sumber (dan lokasi, jika Anda menggunakan 1.9).

GL. -r

rogerdpack
sumber
Semua ini bagi saya menghasilkan kesalahan segmentasi. :(
panzi
bagaimana cara mereproduksi kesalahan seg? metode / kelas yang mana?
rogerdpack
1

Saya harus menerapkan fitur serupa (ambil sumber blok) sebagai bagian dari Salah dan Anda dapat melihat bagaimana (dan bahkan mungkin menggunakan kembali kode) di chunk.rb (yang bergantung pada RubyParser Ryan Davis dan juga beberapa yang cukup lucu kode sumber file glomming ). Anda harus memodifikasinya untuk digunakan Method#source_locationdan mungkin mengubah beberapa hal lain sehingga ada atau tidak termasuk file def.

BTW Saya pikir Rubinius memiliki fitur bawaan ini. Untuk beberapa alasan fitur ini telah ditinggalkan dari MRI (implementasi Ruby standar), karenanya hack ini.

Oooh, saya suka beberapa item di method_source ! Seperti menggunakan eval untuk mengetahui apakah ekspresi itu valid (dan terus glomming baris sumber sampai Anda berhenti mendapatkan kesalahan parse, seperti Chunk) ...

AlexChaffee
sumber
1

Metode internal tidak memiliki sumber atau lokasi sumber (misalnya Integer#to_s)

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