Enumerable. It’s one of the most important modules in Ruby. You use it all day, every day, to…

# filter lists according to some criteria
[1, 2, 3, 4, 5].select(&:odd?) # => [1, 3, 5]
[1, 2, 3, 4, 5].reject(&:odd?) # => [2, 4]

# query lists about their contents
[1, 2, 3, 4, 5].any?(&:odd?) # => true
[1, 2, 3, 4, 5].all?(&:odd?) # => false

# divide them into groups
[1, 2, 3, 4, 5].partition(&:odd?) # => [[1, 3, 5], [2, 4]]

people = [
  { name: "Joe", location: "WA" },
  { name: "Sally", location: "OR" },
  { name: "Erin", location: "CO" },
  { name: "Doug", location: "WA" },
]

people.group_by {|person| person.fetch(:location) }
  # => {
  #   "WA"=>[{:name=>"Joe", :location=>"WA"},
  #          {:name=>"Doug", :location=>"WA"}],
  #   "OR"=>[{:name=>"Sally", :location=>"OR"}],
  #   "CO"=>[{:name=>"Erin", :location=>"CO"}]
  # }

# access each item's properties
people.collect {|person| person.fetch(:name) }
  # => ["Joe", "Sally", "Erin", "Doug"]

…and a whole lot more. In fact, Enumerable provides 58 instance methods! All of this behavior comes automatically for any class that implements #each and includes Enumerable.

So how do you learn all that this powerful module has to offer?

The obvious approach is to read the documentation, experiment in irb, and hope you internalize Enumerable behavior. Or at least vaguely remember that it can kind of do something, and pull up the documentation whenever you need to use it.

But what if you’re not satisfied with a hazy knowledge of one of the most important modules in Ruby? What if you want to understand it deeply, so you can apply it effectively in your daily work? In that case, you need a new strategy. You’re ready to…

Build your own Enumerable

If you want to really understand how Enumerable works, the best thing you can do is implement it yourself. You’ll get a clear understanding of what each method does, and learn exactly how and when to use each method – because you wrote them yourself! Here’s how it works:

  1. Create a class that implements a single method: each
  2. Create a new MyEnumerable module and include it in the class
  3. Implement the Enumerable methods in MyEnumerable

Start with the methods you use most often, and take it as far as you want. For bonus points, write tests to describe the current Enumerable behavior, and use them to verify your implementation.

Here’s your starting point (I implemented #select as an example):

# MyEnumerable will contain all of the enumerable behavior.
# Classes get the behavior automatically just by including
# it and implementing #each
#
# I implemented #select to show you how it works.
module MyEnumerable
  def select(&block)
    result = []
    each {|item| result << item if block.call(item) }
    result
  end
end

# This class wraps an enumerable object and delegates #each.
# This way MyCollection only has the behavior provided
# by MyEnumerable, and nothing else.
class MyCollection
  include MyEnumerable

  def initialize(collection)
    @collection = collection
  end

  def each(&block)
    @collection.each(&block)
  end
end

collection = MyCollection.new [1, 2, 3, 4, 5]
collection.select(&:odd?) # => [1, 3, 5]

Exercise time!

I’ve uploaded an exercise to GitHub to help get you started. Clone the repo, follow the instructions, and you’ll be on your way to deeply understanding Enumerable!

Video demonstration

And to show you just how simple it is to get going, here’s a video demonstration of how to put the exercise into action:

If you’re struggling with the exercise…

Don’t worry: I’ll follow this up with another post that works through the examples from beginning to end. If you want to be notified when that hits the internet, enter your contact information in the form below.

And if you found this useful, please share it with someone!