Returning Enumerator unless block_given?
27.03.2015 - Pascal BetzThe Enumerable module gives you methods to search, iterate, traverse and sort elements of a collection. All you need to to is to implement each
and include the mixin.
class NameList
include Enumerable
def initialize(*names)
@names = names
end
def each
@names.each { |name| yield(name) }
end
end
list = NameList.new('Kaiser Chiefs', 'Muse', 'Beck')
list.each_with_index do |name, index|
puts "#{index + 1}: #{name}"
end
# => 1: Kaiser Chiefs
# => 2: Muse
# => 3: Beck
So by defining each and including Enumerable we got an impressive list of methods that we can call on our NameList instance. But having all those methods added does not feel right. Usually you will use one or two of them. Why clutter the interface by adding 50+ methods that you’ll never use? There is an easy solution for this:
class NameList
def initialize(*names)
@names = names
end
def each
return @names.to_enum(:each) unless block_given?
@names.each { |name| yield(name) }
end
end
list = NameList.new('Kaiser Chiefs', 'Muse', 'Beck')
list.each do |name|
puts name
end
# => Kaiser Chiefs
# => Muse
# => Beck
list.each.each_with_index do |name, index|
puts "#{index + 1}: #{name}"
end
# => 1: Kaiser Chiefs
# => 2: Muse
# => 3: Beck
Note that each
now returns the Enumerator on which you can call each_with_index
(or any of the other methods) unless a block is given. So you can even call it like this:
puts list.each.to_a.size # => 3
By returning an Enumerator when no block is given one can chain enumerator methods. Ever wanted to do a each_with_index
on a hash? There you go:
points = {mushroom: 10, coin: 12, flower: 4}
points.each.each_with_index do |key_value_pair, index|
puts "#{index + 1}: #{key_value_pair}"
end
# => 1: [:mushroom, 10]
# => 2: [:coin, 12]
# => 3: [:flower, 4]