为什么 UnboundMethod 没有实例方法 source ,因为它有 source location ?我该如何破解呢?

Why doesn't UnboundMethod have the instance method `source` since it has `source location`? And how can I hack this?

为什么 UnboundMethod 没有实例方法 source 因为它有 source location?我该如何破解呢? Does ruby's metaprogramming don't good enough to do this?(Does ruby's metaprogramming suck?) 如果是这样,你能为我推荐一个更好的语言来完成这个吗?

这是一个例子来解释我的问题:

# test.rb

class TestClass
  def test_m_a
    puts "test_m_a path: #{path}"
  end
  def test_m_b
    puts "test_m_b path: #{path}"
  end

  instance_methods(false).each do |name|
    puts "source_location: #{instance_method(name).source_location}"
    method_source_code_ar = instance_method(name).source.split("\n")
    method_source_code_ar.insert(1, 'path = __method__.to_s.split("_").join("/")')
    method_source_code_ar.insert(-2, 'puts "#{__method__}  end"')

    method_source_code = method_source_code_ar.join("\n")
    eval(method_source_code)
  end
end

运行在撬:

➜  ~  
➜  ~  pry
[1] pry(main)> require './test.rb' 
source_location: ["/home/yanying/test.rb", 5]
source_location: ["/home/yanying/test.rb", 8]                                                                                                                                                                                                                                     
=> true
[2] pry(main)> TestClass.new.test_m_a                                                                                                                                                                                                                                   
test_m_a path: test/m/a
test_m_a end
=> nil
[3] pry(main)> TestClass.new.test_m_b                                                                                                                                                                                                                                   
test_m_b path: test/m/b
test_m_b end
=> nil
[4] pry(main)>  

运行 在 irb:

  ~  irb
2.1.5 :001 > require './test.rb'
source_location: ["/home/yanying/test.rb", 5]
NoMethodError: undefined method `source' for #<UnboundMethod: TestClass#test_m_a>
    from /home/yanying/test.rb:13:in `block in <class:TestClass>'
    from /home/yanying/test.rb:12:in `each'
    from /home/yanying/test.rb:12:in `<class:TestClass>'
    from /home/yanying/test.rb:4:in `<top (required)>'
    from /home/yanying/.rvm/rubies/ruby-2.1.5/lib/ruby/site_ruby/2.1.0/rubygems/core_ext/kernel_require.rb:54:in `require'
    from /home/yanying/.rvm/rubies/ruby-2.1.5/lib/ruby/site_ruby/2.1.0/rubygems/core_ext/kernel_require.rb:54:in `require'
    from (irb):1
    from /home/yanying/.rvm/rubies/ruby-2.1.5/bin/irb:11:in `<main>'
2.1.5 :002 > 

这是参考的原始程序:

出于调试目的记住源位置,以便生成包含有用信息的堆栈跟踪。

不记住来源(因为会浪费大量内存);只有编译后的代码是。你可以看到 that with RubyVM::InstructionSequence.disasm(instance_method(name)).

在 Pry 中为你工作的 source 是由 Pry 实现的(好吧,实际上是由 Pry 所依赖的 method_source gem):它读取位于 [=14 的文件=],然后在 source_location 行找到表达式。

编辑:你怎么破解这个?

$ gem install method_source
$ irb -r method_source
> puts Method.instance_method(:source).source
# => def source
#      MethodSource.source_helper(source_location, defined?(name) ? name : inspect)
#    end

Does ruby's metaprogramming suck?

不,它实际上是最好的。但是您通常不需要获取源代码。如果您描述了您正在尝试解决的实际问题,那么可能有更好的方法来完成您正在尝试做的事情。