Ruby猴子修补陷阱

时间:2010-08-09 22:32:28

标签: ruby monkeypatching

我正在寻找为什么在ruby中扩展基类不是一个好主意的例子。我需要向一些人展示为什么它是一种谨慎使用的武器。

你能分享哪些恐怖故事?

3 个答案:

答案 0 :(得分:5)

大约2。5年前在鲁宾尼斯有一个非常着名的monkey-patching going horribly wrong例子。

这个案例的有趣之处在于,违规代码和受害者都非常明显且极不寻常。通常,罪犯是由一些PHP脚本小伙子写的一段代码,他们在他的1337元编程h4X0r技能上喝醉了。失败模式是一个简单的ArgumentError异常,因为原始方法和monkeypatch具有不同的arity。

然而,在这种情况下,罪犯是stdlib(mathn)中的一个库,失败模式是Rubinius VM完全爆炸。

那么,发生了什么?好吧,mathn monkeypat the Fixnum类并更改Fixnum算术的工作方式。特别是,它更改了几个核心方法的结果类型。 E.g:

r = 4/3  # => 1
r.class  # => Fixnum

require 'mathn'

r = 4/3  # => (4/3)
r.class  # => Rational

问题当然是在Rubinius中,整个Ruby编译器,整个Ruby内核,Ruby核心库的大部分内容,Rubinius VM的某些部分以及Rubinius基础架构的其他部分都是用Ruby编写的。当然,所有这些都在整个地方使用Fixnum算术。

Hash类是用Ruby编写的,它使用Fixnum算法来计算散列桶的大小,计算散列函数等等。 Array是用Ruby编写的,需要计算元素大小和数组长度。 FFI库是用Ruby编写的,需要计算内存地址(!)和结构大小。 Rubinius的许多部分假设他们可以执行一些Fixnum算术,然后将结果作为指针或int传递给某个C函数。

由于Ruby不支持任何类型的选择器命名空间或类拳击或类似(虽然计划用于Ruby 2.0),只要一些随机用户代码需要mathn库,所有那些碎片爆炸性地爆炸,因为突然之间,Fixnum操作的结果不再是Fixnum(它基本上与机器int相同,并且可以作为这样),但是Rational(这是一个成熟的Ruby对象)。

基本上,会发生什么,是某些代码会require 'mathn'(或者你会将其输入到IRb中),并且VM会立即死亡。

在这种情况下,解决方案是编译器的安全数学插件:当编译器检测到它正在编译内核或Rubinius的其他核心部分时,它会自动重写对{{{ 1}}方法调用这些方法的私有不可变副本。 [注意:我认为在当前版本的Rubinius中,问题以不同的方式解决。]

答案 1 :(得分:3)

The Trifecta of FAIL; or, how to patch Rails 2.0 for Ruby 1.8.7有一个Rails的例子(这是一个经过严格审查的大型项目)会导致问题,因为他们通过monkeypatched String添加方法chars

答案 2 :(得分:1)

一个明显的缺陷是名称冲突 - 如果两个或多个包为行为不同的方法选择相同的名称。