The previous post in this series covered Object#enum_for’s ability to make a collection out of any method that yields values to a block. We saw that #enum_for opened up a lot of opportunities for elegant code and simple tests, but we also saw that we were limited in what we could do with the enumerator if we wanted to stay lazy.
In this post, we’ll explore an alternative solution, using Enumerator.new, which will allow us to filter the scores and still delay the evaluation of our collection until the client absolutely needs it.
Given:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class RedWingsFan def initialize(hockey_client) @hockey_client = hockey_client end def red_wings_goal_scorers @hockey_client.enum_for(:listen_for_hockey_scores).select do |goal| goal.team == "Red Wings" end.map do |goal| goal.scorer end end end |
Problem:
We want our method to return immediately, and give us an object we can enumerate on-demand.
Solution:
Use Enumerator.new to lazily produce elements, so we don’t have to evaluate everything at once.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
class RedWingsFan def initialize(hockey_client) @hockey_client = hockey_client end def red_wings_goal_scorers Enumerator.new do |yielder| @hockey_client.listen_for_hockey_scores do |goal| yielder << goal.scorer if goal.team == "Red Wings" end end end end red_wings_fan = RedWingsFan.new(HockeyClient.new) puts "Red Wings scorers:" red_wings_fan.red_wings_goal_scorers.each do |scorer| puts scorer end |
Now our collection is lazy—it does not wait for the game to complete before it returns something we can enumerate, and every iteration of our #each block runs as soon as the goal is made.
Another interesting thing that you can do with Enumerator.new is build an infinite collection. Here’s one for the fibonacci sequence:
1 2 3 4 5 6 7 8 9 10 |
def fibonacci Enumerator.new do |yielder| previous = [1, 1] while current = previous.inject(:+) yielder << previous.shift previous << current end end end |
Warning: Do not call #to_a on an infinite enumerator such as this unless you want to use a lot of memory really quickly.
The full source for this post is available here.
My next post will revisit our previous solution to see if there’s a more elegant way to solve the problem.


One Trackback
[...] Enumerator.new [...]