advantage of tap method in ruby

I was just reading a blog article and noticed that the author used tap in a snippet something like:

user = User.new.tap do |u|
  u.username = "foobar"
  u.save!
end

My question is what exactly is the benefit or advantage of using tap? Couldn't I just do:

user = User.new
user.username = "foobar"
user.save!

or better yet:

user = User.create! username: "foobar"

Solution 1:

When readers encounter:

user = User.new
user.username = "foobar"
user.save!

they would have to follow all the three lines and then recognize that it is just creating an instance named user.

If it were:

user = User.new.tap do |u|
  u.username = "foobar"
  u.save!
end

then that would be immediately clear. A reader would not have to read what is inside the block to know that an instance user is created.

Solution 2:

Another case to use tap is to make manipulation on object before returning it.

So instead of this:

def some_method
  ...
  some_object.serialize
  some_object
end

we can save extra line:

def some_method
  ...
  some_object.tap{ |o| o.serialize }
end

In some situation this technique can save more then one line and make code more compact.

Solution 3:

This can be useful with debugging a series of ActiveRecord chained scopes.

User
  .active                      .tap { |users| puts "Users so far: #{users.size}" } 
  .non_admin                   .tap { |users| puts "Users so far: #{users.size}" }
  .at_least_years_old(25)      .tap { |users| puts "Users so far: #{users.size}" }
  .residing_in('USA')

This makes it super easy to debug at any point in the chain without having to store anything in in a local variable nor requiring much altering of the original code.


And lastly, use it as a quick and unobtrusive way to debug without disrupting normal code execution:

def rockwell_retro_encabulate
  provide_inverse_reactive_current
  synchronize_cardinal_graham_meters
  @result.tap(&method(:puts))
  # Will debug `@result` just before returning it.
end

Solution 4:

Using tap, as the blogger did, is simply a convenience method. It may have been overkill in your example, but in cases where you'd want to do a bunch of things with the user, tap can arguably provide a cleaner looking interface. So, perhaps it may be better in an example as follows:

user = User.new.tap do |u|
  u.build_profile
  u.process_credit_card
  u.ship_out_item
  u.send_email_confirmation
  u.blahblahyougetmypoint
end

Using the above makes it easy to quickly see that all those methods are grouped together in that they all refer to the same object (the user in this example). The alternative would be:

user = User.new
user.build_profile
user.process_credit_card
user.ship_out_item
user.send_email_confirmation
user.blahblahyougetmypoint

Again, this is debatable - but the case can be made that the second version looks a little messier, and takes a little more human parsing to see that all the methods are being called on the same object.