Another Ruby Enumerator trick

I was working with the official Ruby gem for the Zendesk API. It’s a quirky little thing but on the whole quite nice to work with. I did run into one little gotcha: in order to retrieve all the items in a paginated collection, you have to use the #all! or #all methods (the bang version raises an error), and you must pass it a block.

For example, in order to build a collection of all the suspended tickets, I have to do something like this:

suspended_tickets = []
zendesk.suspended_tickets.all! do |suspended_ticket, _page|
    suspended_tickets << suspended_ticket
end

Now I can use the collection as I see fit.

Recalling my earlier adventures with Enumerators in Ruby, I thought I could surely improve upon this solution by monkeypatching and alias-arounding the #all! method in the zendesk_api gem. Alternatively, I could create a new Enumerator and use it to yield (in the generator sense) values to build the collection. But wait: can’t I ask Ruby to do that for me, without monkeypatching or writing boilerplate code?

suspended_tickets = zendesk.suspended_tickets
    .enum_for(:all!)

Nice! Now I can chain method calls to my heart’s content, without the clumsy intermediate array. Here’s what my final solution ended up looking like:

zendesk.suspended_tickets
    .enum_for(:all!)
    .reject { |suspended_ticket, | INVALID_CAUSES.include?(suspended_ticket.cause) }
    .map { |suspended_ticket, | suspended_ticket.id }
    .each_slice(BATCH_SIZE) { |ids| zendesk.suspended_tickets.recover_many(verb: :put, ids: ids).fetch }

If you’re ever working with a fussy method that returns a collection but requires a block, I hope this trick comes in handy! Let me know in the comments if you have any questions about or improvements to this pattern.

Leave a Reply

Your email address will not be published. Required fields are marked *