开发者

How can I get source code of a method dynamically and also which file is this method locate in

开发者 https://www.devze.com 2023-02-08 23:20 出处:网络
I would l开发者_运维技巧ike to know whether I can get source code a method on the fly, and whether I can get which file is this method in.

I would l开发者_运维技巧ike to know whether I can get source code a method on the fly, and whether I can get which file is this method in.

like

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


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.


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:

  • https://rubygems.org/gems/method_source
  • https://github.com/banister/method_source
  • http://banisterfiend.wordpress.com/


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

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


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


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).

GL. -r


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


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)...

0

精彩评论

暂无评论...
验证码 换一张
取 消