【问题标题】:Ruby: print the code of an arbitrary method (and exec in context)Ruby:打印任意方法的代码(并在上下文中执行)
【发布时间】:2009-06-12 05:08:46
【问题描述】:

我想做如下的事情:

class String
  def fancy_thing appendix
   # Just a trivial example to ensure self and params work.
   # Pretend this is something complex.
   self.reverse + appendix
  end
end

# print_method on instance or class should spit out a string
#  containing the actual code for that method
ft_code = "cob".print_method :fancy_thing
ft_code = String.print_instance_method :fancy_thing
  # => "{|appendix| self.reverse + appendix }"  *

# ft_code gets passed around a bit...

# exec on an object should run code (w/ parameters) as if that code is 
#  an instance method on that object (or class method if it's a class)
"cob".exec(ft_code, '!') #=> "boc!"

如何编写 print_method 和 foo.exec?最好,它们应该适用于任何任意方法,而无需先验地知道它们可能恰好是从哪里定义或来源的。

  • 是的,我知道方法和块并不完全相同。但这更接近 yield 和 call 通常需要的值;我不知道有更好的解决方案。

【问题讨论】:

  • 您不应该在这两个地方都需要“cob”来打印该输出。您能否修改/澄清有关每种方法的作用的问题...
  • 根据我对 joshng 解决方案的评论,目的是 a) 用于 irb/console 目的(在调试器本身之外)和 b) 只是为了看看它是否可能。查看他们所做的修改。

标签: ruby metaprogramming exec


【解决方案1】:

parse_tree 将为您提供所需的关键步骤:

http://github.com/seattlerb/parsetree/tree/master

我认为这是以最快/最黑客/最不安全的方式做到的:

require 'parse_tree'
require 'parse_tree_extensions'
require 'ruby2ruby'

class Object
  def method_source(name)
    (class << self; self; end).instance_method(name).to_ruby
  end

  def exec(ruby, *args)
    code = eval(ruby, binding)
    code.call(*args)
  end
end

我要补充一点,我很难看出这是一个好主意......但是你有它。 :-)

[编辑]

另请注意,您的示例已失败:您的“fancy_thing”方法需要一个参数(附录)。

[编辑 2]

最重要的是,这是您修复了错误的测试代码(我认为您想要的方式):

class String
  def fancy_thing(appendix)
    reverse << appendix || nil
  end
end

code = "cob".method_source :fancy_thing
# => "proc {|appendix| reverse << appendix }"  *
"cob".exec code, '!'
# => "boc!"

【讨论】:

  • 我的意图是在 irb / rails 控制台中,以便于调试、方法编辑等(不一定在调试器中正确)。只是因为我很好奇它是否可以在 1.8 / 1.9 中完成。 ;-)
  • 修复了 OP 中的方法。其定义的部分要点是使用“self”作为测试,以确保 exec 正确绑定它。但这真的只是一个随机的例子。现在我想知道 parse_tree 和 ruby​​2ruby 是如何工作的那个神奇的 to_ruby 调用。看起来我需要阅读他们的代码。 ;)
  • 您的解决方案非常好,但因两件事而失败。 1)类方法; 2)内置(例如“foo”.print_method :to_s # => UnsupportedNodeError)。我想知道这是否可以解决。
  • 好的,编辑了我的答案以支持类方法。还重命名了“print_method”以更好地反映它在做什么:返回 method_source。不幸的是,无法访问大多数内置方法的源代码,因为它们是用 C 编写的。现在,请您单击我的答案旁边的小复选标记吗? :-)
【解决方案2】:

如果不读取 Ruby 文件并搜索方法代码,这并不容易实现。在 Ruby 1.8 中,您可以使用 ParseTree 和 ruby​​2ruby。在 1.9 中,我不知道任何解决方案。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-12-11
    • 2013-12-18
    • 1970-01-01
    • 1970-01-01
    • 2011-01-27
    • 1970-01-01
    • 2014-02-02
    • 1970-01-01
    相关资源
    最近更新 更多