Làm cách nào tôi có thể lấy mã nguồn của một phương thức động và cũng là tệp mà phương thức này nằm trong


88

Tôi muốn biết liệu tôi có thể lấy mã nguồn một phương thức ngay lập tức hay không và liệu tôi có thể lấy phương thức này trong tệp nào không.

giống

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

Câu trả lời:


113

Sử dụng 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"

Lưu ý rằng đối với các phương thức nội trang, source_locationtrả về nil. Nếu muốn kiểm tra mã nguồn C (chúc bạn vui vẻ!), Bạn sẽ phải tìm đúng tệp C (chúng ít nhiều được sắp xếp theo lớp) và tìm rb_define_methodphương thức (ở cuối tệp ).

Trong Ruby 1.8, phương thức này không tồn tại, nhưng bạn có thể sử dụng gem này .


2
Xin chào, tôi đến từ tương lai, sử dụng Ruby 2.6.1! Tôi muốn mã nguồn của String#include?. Cho đến nay String.instance_method(:include?).source_locationtrở lại nil.
S.Goswami

39

Cho đến nay, không có câu trả lời nào cho thấy cách hiển thị mã nguồn của một phương thức một cách nhanh chóng ...

Nó thực sự rất dễ dàng nếu bạn sử dụng đá quý 'method_source' tuyệt vời của John Mair (người tạo ra Pry): Phương thức này phải được triển khai bằng Ruby (không phải C) và phải được tải từ một tệp (không phải irb).

Dưới đây là một ví dụ hiển thị mã nguồn phương thức trong bảng điều khiển Rails với 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 

Xem thêm:


1
Tôi luôn bỏ lỡ tính năng này trong Ruby. Lisp có thể làm :) này
Tilo

Đến từ Clojure's source. Điều này hoạt động như mong đợi.
Sebastian Palma

Tôi gặp lỗi này: [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_locationhoạt động tốt mặc dù
Abram

17

Đây là cách in mã nguồn từ ruby:

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

10

Không có phụ thuộc

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]

Nếu bạn muốn sử dụng tiện lợi hơn, bạn có thể mở Methodlớp:

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

Và sau đó chỉ cần gọi method.source

Với Pry, bạn có thể sử dụng show-methodđể xem nguồn phương thức và thậm chí bạn có thể xem một số mã nguồn ruby ​​c pry-docđã được cài đặt, theo tài liệu của pry trong codde-Browing

Lưu ý rằng chúng ta cũng có thể xem các phương thức C (từ Ruby Core) bằng cách sử dụng plugin pry-doc; chúng tôi cũng hiển thị cú pháp thay thế cho phương thức hiển thị:

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

đó là một ý tưởng tuyệt vời cho một sourcephương thức bên trong Methodlớp. Sẽ tốt hơn nữa nếu nó xử lý văn bản và thời điểm dừng in mới vì nó đã đến cuối phương pháp.
Toby 1 Kenobi

4

Tôi đã tạo đá quý "ri_for" cho mục đích này

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

... xuất ra nguồn (và vị trí, nếu bạn đang ở 1.9).

GL. -r


Tất cả điều này đối với tôi là tạo ra một lỗi phân đoạn. :(
panzi

làm thế nào để tái tạo lỗi seg? phương pháp / lớp nào?
rogerdpack

1

Tôi đã phải triển khai một tính năng tương tự (lấy nguồn của khối) như một phần của Wrong và bạn có thể thấy cách (và thậm chí có thể sử dụng lại mã) trong chunk.rb (dựa trên RubyParser của Ryan Davis cũng như một số khá hài hước mã glomming tập tin nguồn ). Bạn sẽ phải sửa đổi nó để sử dụng Method#source_locationvà có thể điều chỉnh một số thứ khác để nó có hoặc không bao gồm def.

BTW Tôi nghĩ rằng Rubinius đã tích hợp sẵn tính năng này. Vì một số lý do, nó đã bị loại khỏi MRI (triển khai Ruby tiêu chuẩn), do đó có vụ hack này.

Ồ, tôi thích một số nội dung trong method_source ! Giống như sử dụng eval để cho biết một biểu thức có hợp lệ hay không (và giữ nguyên các dòng nguồn cho đến khi bạn ngừng nhận được lỗi phân tích cú pháp, như Chunk đã làm) ...


1

Các phương thức nội bộ không có nguồn hoặc vị trí nguồn (ví dụ Integer#to_s)

require 'method_source'
User.method(:last).source
User.method(:last).source_location
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.