如何动态获取方法的源代码以及该方法位于哪个文件中

我想知道我是否可以得到一个动态的源代码方法,以及我是否可以得到哪个文件是这个方法在。

喜欢

A.new.method(:a).SOURCE_CODE
A.new.method(:a).FILE
54237 次浏览

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

Note that for builtin methods, source_location returns nil. If want to check out the C source code (have fun!), you'll have to look for the right C file (they're more or less organized by class) and find the rb_define_method for the method (towards the end of the file).

In Ruby 1.8 this method does not exist, but you can use this gem.

I created the "ri_for" gem for this purpose

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

... outputs the source (and location, if you're on 1.9).

-r

None of the answers so far show how to display the source code of a method on the fly...

It's actually very easy if you use the awesome 'method_source' gem by John Mair (the maker of Pry): The method has to be implemented in Ruby (not C), and has to be loaded from a file (not irb).

Here's an example displaying the method source code in the Rails console with 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

See also:

I had to implement a similar feature (grab the source of a block) as part of Wrong and you can see how (and maybe even reuse the code) in chunk.rb (which relies on Ryan Davis' RubyParser as well as some pretty funny source file glomming code). You'd have to modify it to use Method#source_location and maybe tweak some other things so it does or doesn't include the def.

BTW I think Rubinius has this feature built in. For some reason it's been left out of MRI (the standard Ruby implementation), hence this hack.

Oooh, I like some of the stuff in method_source! Like using eval to tell if an expression is valid (and keep glomming source lines until you stop getting parse errors, like Chunk does)...

Here is how to print out the source code from ruby:

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

Internal methods don't have source or source location (e.g. Integer#to_s)

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

Without dependencies

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]

If you want use this more conveniently, your can open the Method class:

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

And then just call method.source

With Pry you can use the show-method to view a method source, and you can even see some ruby c source code with pry-doc installed, according pry's doc in codde-browing

Note that we can also view C methods (from Ruby Core) using the pry-doc plugin; we also show off the alternate syntax for 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;
}