Ruby 1.9 added a new method tap
that is useful for cleaning up code. Here’s the Ruby doc:
tap{|x|…} → obj:
Yields x to the block, and then returns x. The primary purpose of this method is to “tap into” a method chain, in order to perform operations on intermediate results within the chain.
Effectively tap
could be coded in pure Ruby like this:
class Object def tap(value) yield(value) value end end
Knowing what tap does, here are some clever ways to use it.
As a With Statement
Jamis Buck demonstrated the Rails version of tap – returning
– back in 2006. Since the two methods are equivalent, his example still applies:
def create_book book = Book.new book.title = "Trafalgar: The Nelson Touch" book.author = "David Howarth" @db.add(book) book end
This can be simplified to the following:
def create_book Book.new.tap do |book| book.title = "Trafalgar: The Nelson Touch" book.author = "David Howarth" @db.add(book) end end
As an Accumulator
I like to use tap when building up collections of data, so instead of writing this:
data = [] (1..3).each do |value| data << value end data
I can write the much clearer tap version:
[].tap do |data| (1..3).each do |value| data << value end end
As an Indentor
gsinclair showed a way to use tap for code structuring:
def test_isosceles_given_base_and_height triangle(:ABC, :isosceles, :base => 5, :height :=> 9).tap do |t| assert_point_equal p(2.5,9), t.apex assert_point_equal p(0,0), @register[:A] assert_point_equal p(5,0), @register[:B] assert_point_equal p(2.5,9), @register[:C] assert_equal t, @register.retrieve(:triangle, :ABC) end end
While I don’t agree that the unit test comes out looking cleaner than its counterpart, I do see the power of this type of nesting as a way to clean up code.
If you’ve discovered any other clever usages of tap leave it in the comments.