Ruby: вывести код произвольного метода (и exec в контексте) - PullRequest
1 голос
/ 12 июня 2009

Я хотел бы сделать что-то вроде следующего:

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; Я не знаю лучшего решения.

Ответы [ 2 ]

7 голосов
/ 12 июня 2009

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!"
0 голосов
/ 12 июня 2009

Это не очень легко реализовать без простого чтения файла Ruby и поиска кода метода. В Ruby 1.8 вы можете использовать ParseTree и ruby2ruby. В 1.9 я не знаю ни одного решения.

...