猴子补丁vs class_eval?

时间:2012-02-22 16:58:48

标签: ruby metaprogramming monkeypatching

class String
  def hello
    "world"
  end
end

String.class_eval {
  def world
    "hello"
  end
}

"a".world
=> "hello"
"b".hello
=> "world"

他们似乎做同样的事情 - 向现有类添加方法。那有什么区别?

3 个答案:

答案 0 :(得分:13)

使用class_eval,你可以做更多动态的事情:

>> met = "hello" #=> "hello"
>> String.class_eval "def #{met} ; 'hello' ; end" #=> nil
>> "foo".hello #=> "hello"

答案 1 :(得分:13)

class_eval在概念上进行类重新打开(或猴子修补)。主要是语法差异。如果将字符串传递给class_eval(如Michael的示例中所示),则字符串内部的语法与class String; ... end中的语法大致相同。如果你传递阻止:String.class_eval { ... },则按如下方式进行比较:

  • 在class_eval块中,外部局部变量可见
  • 内部重新打开的类外部局部变量不可见
  • 在class_eval内部,您不能将作用域的常量和类变量分配给类
  • 内部重新开课,你可以

了解其他差异会很有趣

答案 2 :(得分:0)

其他答案都很好。想要添加class_eval可以在您希望引用类不是通过常量或修补特定对象时使用。

e.g。

huh = String
class huh
end
SyntaxError: (eval):2: class/module name must be CONSTANT

huh.class_eval <<-eof
def mamma
puts :papa
end
eof

"asdff".mamma
=> papa

您可以使用class_eval修补特定对象而不影响整个根类。

obj = "asd"
obj.singleton_class.class_eval <<-eof
def asd
puts "gah"
end
undef_method :some_method

以上内容与:

相同
class << obj
  ...
end

instance_eval会因某些用法而略有不同。

我觉得这个问题和答案很有趣: How to monkey patch a ruby class inside a method

还有关于instance_evalclass_eval的问题,但我没有方便的链接。