Question

I'm trying to overwrite a method on the Enumerable module like this:

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

(Note this is a trivial example).

Theoretically, when I call collect or map, Ruby should use my overridden version, right? But it's not. It always uses the built-in Enumerable method. Is it because collect is actually enum_collect and complied with the source?

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

Yes, I'm aware that Monkey-Patching is bad, etc, etc, and I'm aware there are alternatives including subclassing, etc, but I want to know if it's possible to overwrite a built-in C function with Ruby.

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)

and pretty much every combination thereof.

PS. This is Ruby 1.9.3, but ideally I'm looking for a solution that would work on all versions.

Was it helpful?

Solution

I think your problem is that Array defines its own collect method rather than using Enumerable's:

collect {|item| block } → new_ary
map {|item| block } → new_ary
collect → an_enumerator
map → an_enumerator

Invokes block once for each element of self. Creates a new array containing the values returned by the block. See also Enumerable#collect.

So you can monkey patch Enumerable#collect all you want but Array won't care because it doesn't use Enumerable#collect. You'll have better luck if you monkey patch Array#collect:

class Array
  def collect
    #...
  end
end

You'll want to patch Array#map as well or just patch map and let the alias take care of collect.

Note that Array#map is implemented in C so the C part had nothing to do with your problem.

OTHER TIPS

You can iterate over many different types of objects in Ruby using the same set of methods. For example, you can use the includes? method to iterate over Arrays and Hashes to see if they include a specific object, and similarly use the map method to transform them. You might think each method implements these methods themselves, but you'd be wrong.

In Ruby, these types of methods are implemented in the Enumerable module. If your class implements the each method and includes the Enumerable module, your class will respond to a whole host of messages involving collections, including iterating, mapping, searching, etc.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top