Reputation: 5936
How do I add my custom methods onto the existing Enumerable module in Ruby? I'm running Ruby 2.0.
Upvotes: 2
Views: 1518
Reputation: 110675
All methods defined in the Enumerable
module require receivers to be either enumerators or have a method each
. Let's suppose we wish to create an enumerable method my_to_a
similar to Enumerable#to_a except it returns just the first n
of the receiver's elements (or, if it's an enumerator, the first n
elements it generates), n
being my_to_a
's argument.
module Enumerable
def my_to_a(n)
enum = is_a?(Enumerator) ? self : each
n.times.with_object([]) { |o,a| a << enum.next }
end
end
[0,1,2,3,4].my_to_a(3)
#=> [0, 1, 2]
(0..4).my_to_a(3)
#=> [0, 1, 2]
{ a:1, b:2, c:3, d:4, e:5 }.my_to_a(3)
#=> [[:a, 1], [:b, 2], [:c, 3]]
'0ab_1ab_2ab_3ab_4ab'.gsub(/.(?=ab)/).my_to_a(3)
#=> ["0", "1", "2"]
require 'prime'; Prime.my_to_a(4)
#=> [2, 3, 5, 7]
[0,1,2,3,4].my_to_a(6)
#=> StopIteration (iteration reached an end)
'0ab_1ab_2ab_3ab_4ab'.gsub(/.(?=ab)/).my_to_a(6)
#=> StopIteration (iteration reached an end)
The line
enum = is_a?(Enumerator) ? self : each
sets enum
to self
if self
is an enumerator and to the enumerator self.each
otherwise. The latter case assumes self
has a method each
, meaning that the class self.class
has an instance method each
(or a method each
has been defined on self
's singleton class). each
must return an enumerator. In the example, is_a?(Enumerator)
(equivalent to self.is_a?(Enumerator)
) is true
only for the two examples that employ String#gsub with one argument and no block.1 In the other examples self.class.included_modules.includes?(Enumerable) #=> true
and self.class.instance_methods.include?(:each) #=> true
. Those classes are Array
, Range
, Hash
and Prime
. each
is an instance method for the first three, a class method for Prime
.
Once we have the enumerator enum
the line
n.times.with_object([]) { |o,a| a << enum.next }
is straightforward. See Integer#times, Enumerator#with_object and Enumerator#next.
1 Note that Enumerator.included_modules #=> [Enumerable, Kernel]
, so any method that returns an enumerator (instance of Enumerator
) responds to Enumerable
methods. That is, there is no need for the method's class (such as String) to include Enumerable
or have an method each
.
Upvotes: 1
Reputation:
In Ruby, you can use monkey-patching
, which incorporates the concept of open classes
. This means that classes in Ruby can be modified at any time. For example, you could create a double method in the number class.
class Integer < Numeric
def self.double
self * 2
end
end
Now you could call a double method on a number
4.double
=> 8
Hope this helps
Upvotes: 2
Reputation: 369458
The same way you add methods to every other module.
If you want to add method bar
with parameters baz
and quux
to module Foo
you write
module Foo
def bar(baz, quux)
# code code code
end
end
So, if you want to add method histogram
to module Enumerable
you write
module Enumerable
# Counts the number of occurrences of each unique object in `self`.
#
# @return [Hash<E, Integer>] a `Hash` of objects mapped to number of occurrences in `self`.
def histogram
group_by(&:itself).map {|k, v| [k, v.size] }.to_hash
end
end
Upvotes: 3