Skip to content

Instantly share code, notes, and snippets.

@batizhevsky
Created July 4, 2014 16:30
Show Gist options
  • Save batizhevsky/000e0406e22dc81884b6 to your computer and use it in GitHub Desktop.
Save batizhevsky/000e0406e22dc81884b6 to your computer and use it in GitHub Desktop.
Co-routines are severely under-utilized in Ruby.
There's a truly magical line you can add to the top of a function that yields a bunch of values,
`return to_enum(__callee__) unless block_given?`
which makes the function return an Enumerator enumerating its values if you don't explicitly pass it a block.
As a simple example, consider this,
```ruby
def numbers
return to_enum(__callee__) unless block_given?
yield 1
yield 2
yield 3
end
```
You will now have `numbers.to_a == [1, 2, 3]`.
This allows you to do some really sexy stuff, though it may not be obvious at first. Imagine you're interfacing with an external API (maybe for fetching Reddit posts). The API allows you to check whether there's another post (has_next_post?) and if so, to fetch it (next_post).
Normally, you might end up with various methods for fetching various numbers of posts according to different criteria. You might need to fetch the first 10 posts within the last week, etc. But using this trick you only need one basic method:
```ruby
def all_posts
return to_enum(__callee__) unless block_given?
while api.has_next_post?
yield api.next_post
end
end
```
For fetching the first 10 posts within the last week you can now do,
`all_posts.lazy.take_while{ |p| p.date <= 1.week.ago }.take(10)`
The magic is that you can verify that this will only make the number of API calls that are actually required. The execution of the method pauses and ends when the final take gets everything it needs.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment