Ruby覆盖Enumerable方法

时间:2012-12-30 00:35:09

标签: c ruby monkeypatching

我正在尝试覆盖Enumerable模块上的方法,如下所示:

module Enumerable
  def collect(&block)
    puts 'collect'
    super
  end
end

(注意这是一个简单的例子)。

理论上,当我调用collectmap时,Ruby应该使用我的重写版本,对吧?但事实并非如此。它总是使用内置的Enumerable方法。是因为collect实际上是enum_collect并且符合来源吗?

[1,2,3].map(&:to_s) # never prints anything

是的,我知道Monkey-Patching很糟糕,等等,我知道有其他选择,包括子类化等,但我想知道是否有可能覆盖内置的C函数红宝石。

Enumerable.class_eval do
  def collect(&block)
    puts 'collect was class_eval'
    super
  end
end

eigen = class << Enumerable; self; end
eigen.class_eval do
  def collect(&block)
    puts 'collect was eigen'
    super
  end
end

module Enumerable
  def collect(&block)
    puts 'collect was opened up'
    super
  end
end

Array.send(:include, Enumerable)

以及它的几乎所有组合。

PS。这是Ruby 1.9.3,但理想情况下我正在寻找适用于所有版本的解决方案。

2 个答案:

答案 0 :(得分:5)

我认为你的问题是Array定义了自己的collect method而不是使用Enumerable:

  

收集{| item |阻止}→new_ary
   map {| item |阻止}→new_ary
  收集→an_enumerator
   map→an_enumerator

     

self的每个元素调用一次。创建一个包含块返回的值的新数组。另请参阅Enumerable#collect

所以你可以随心所欲地追踪Enumerable#collect补丁,但Array不关心,因为它不使用Enumerable#collect。如果你追逐补丁Array#collect,你会有更好的运气:

class Array
  def collect
    #...
  end
end

您也需要修补Array#map或仅修补map并让别名处理collect

请注意,Array#map在C中实现,因此C部分与您的问题无关。

答案 1 :(得分:0)

您可以使用相同的方法集在Ruby中迭代许多不同类型的对象。例如,您可以使用包含?迭代Arrays和Hashes以查看它们是否包含特定对象的方法,并类似地使用map方法对它们进行转换。您可能认为每种方法本身都实现了这些方法,但您错了。

在Ruby中,这些类型的方法在Enumerable模块中实现。如果您的类实现了每个方法并包含Enumerable模块,那么您的类将响应涉及集合的大量消息,包括迭代,映射,搜索等。