Using Ruby Enumerators for Resumable Iterators

March 11, 2018

Here's a power technique: You can use Ruby Enumerators to create "resumable" iterators when you're extracting data from the application console.

In the past, When I had to some one-off analysis of some production data, I’d do something like this:


big_collection.each do |thingy|
  complex_expensive_computation_on(thingy)
end

Invariably something would ‘asplode

OMG! Exception Raised: ... complex_expensive_computation_on() ...

Which I would dutifully fix, then I’d have to start. all. over. again.

however

I won’t go into the sublime complexities of Enumerables and Enumerators. Others have said it much better than I can. This unwrapping is exactly what happens when you pass a block to the enumerator returned by each. If we unwrap it just a little bit using an enumerator instead of calling each:


enum = big_collection.each
loop do
  thingy = enum.next
  complex_expensive_computation_on(thingy)
end

Then (when) an exception is raised, I can “simply” handle it on the console, or edit the method, or say “lets just skip it, bad data, next!”

Resuming from where we left off is a simple recapitulation of the loop:


loop do
  thingy = enum.next
  complex_expensive_computation_on(thingy)
end

and we can carry on with the rest of the data!

When an enumerator gets to the end of its iterations, it raises the StopIteration exception. That’s how each with a block is implemented. Even moar better: loop do...end has some extra Ruby magic sprinkles in it: It rescues StopIteration for you so that when the enumerator does come to its timely end, there’s no exception raised.

Using Ruby Enumerators for Resumable Iterators - March 11, 2018 - Ken Mayer