[LRUG] Enumerator shenanigans
Andrew Stewart
boss at airbladesoftware.com
Thu Nov 14 05:24:25 PST 2013
Hello El Rug,
I have written a method that fetches items in turn from a paged collection. When called with a block it yields each item to the block. When called without a block, it returns an Enumerator that spits out each item in turn as lazily as possible.
Let's say the collection has 1,000 items and we can fetch pages of 50 at a time. This is what I want:
# yields as it goes, retrieving pages as needed behind the scenes
Item.find_each do |item|
puts item.name
end
# only retrieves the first two pages
# Ruby 1.9 doesn't do lazy map or reduce, I think?
Item.find_each.take(75).map(&:price).reduce(:+)
# only retrieves the first two pages
# Ruby 2 allows this?
Item.find_each.map(&:price).reduce(:+).take(75)
I'd like the code to work on Ruby 1.9 and 2.
So I have a working implementation but it looks, er, suboptimal. What's the proper way to do this?
class Item
def self.find_each(params = {})
if block_given?
page = nil
while page.nil? || page.current_page < page.total_pages
if page.nil?
page = all params
else
page = all params.merge(page: page.current_page + 1)
end
page.each do |item|
yield item
end
end
else
Enumerator.new do |yielder|
page = nil
while page.nil? || page.current_page < page.total_pages
if page.nil?
page = all params
else
page = all params.merge(page: page.current_page + 1)
end
page.each do |item|
yielder << item
end
end
end
end
end
def self.all(params = {})
# snip
end
end
Thanks in advance,
Andy Stewart
More information about the Chat
mailing list