benjamins.to_enum

The Enumerable module in Ruby: Part I

Mehdi Farsi
Jul 26, 2018 · 4 min read

In this article we’re going to explore the following topics:

  • The Enumerable module
  • The Enumerator class

Introduction

This logic is encapsulated in the Enumerable module.

The Enumerable module

  • Traversal methods
  • Searching methods
  • Sorting methods

are added to the including class.

This module is widely used among the most popular Ruby gems and projects— for example Ruby on Rails, devise, etc..

Also few important Ruby classes include this module, such as theArray, Hash, Range classes.

Let’s have an overview of the provided API for this module.

Let’s have a look to a traversal, a sorting and a searching method

Firstly, we use the well-known map method.

Then we use the sort method, which sorts by ascendent alphabet by default.

Finally, we use the searching method first to fetch the first item of the array.

Including the Enumerable module

In the above example, a NoMethodError is raised due to the fact that, internally, the Enumerable#map method makes a call to the each method — which is not defined by the Users class.

So, let’s define it

Here we define a Users#each method that loops through the @users array — which is the data source of the Users class — then we yield each value of the @users array.

As the Users#each method is defined, then the Enumerable#map method can use it and get each user as argument of its block.

Feel free to read The yield keyword article if you are unfamiliar with the yield keyword.

Now, that we are more familiar with the Enumerable module, let’s dig into the Enumerator class.

The Enumerator class

How to use an Enumerator?

If no argument is passed to map (and to almost all of the methods of the Enumerable module) then an instance of the Enumerator class is returned.

This enumerator is linked to the [1, 2, 3] array (data source) and the map method (data consumer)

In the above example, the map data consumer is executed in the context of the each method.

As you can you see, the enumerator executes only once the content of the block — as there is only 3 calls to the puts method.

This use case is not very efficient as we would prefer to call the map method as following [1, 2, 3].map { |n| puts n; n + 2 }.

Ok.. but here is a better one

As the map_with_index is not yet part of the Enumerable module, then a cool way to copy the behaviour of this method is by using the Enumerator returned by the map call without argument and then calling the Enumerator#with_index method.

So, the map data consumer is executed in the context of the with_index method.

Chaining Enumerators

Here, the map data consumer is executed in the context of the with_index data consumer which is linked to the context of the each method.

External iteration

For example, The Array#each method.

In contrary, an iteration is called external when the iteration logic is defined outside of a method.

Let’s see how the Enumerator module handles the external iteration

The Kernel#to_enum returns an instance of the Enumerator class with self (the [1,2,3] array in this case) as data source and the each method as default data consumer.

Moving the internal cursor

The Enumerator class provides a bunch of methods to manipulate an internal cursor that keeps the state of the external iteration.

The Enumerator#peek method returns the value contained at the cursor position.

The Enumerable#next method moves the cursor to the next position.

The Enumerator#next method (as well as Enumerator#peek) raises a StopIteration error when it is called and the cursor is at the last position of the data source.

The Enumerable#rewind method moves the cursor to the first position.

Voilà !

Feel free to have a look to the Part II.

May I have your attention please 🎤🎤

Feel free to subscribe here: www.rubycademy.com


Thank you for taking the time to read this post :-)

Feel free to 👏 and share this article if it has been useful for you. 🚀

Here is a link to my previous article: Numbers and Class Hierarchy in Ruby.

rubycademy

E-Learning platform for Ruby and Ruby on Rails

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store