Using Ruby Enumerators for Resumable Iterators
March 11, 2018
[ruby
]
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.