As software projects grow, being able to demonstrate your intention to other developers through clear and reasonable code is paramount. In this series I cover short tips to do just that.
Today we cover the under appreciated #tap method:
What does it do?
#tap’s behavior as “Yields self to the block, and then returns self”. Put simply, it lets you act on an object while ensuring the object is also returned.
1 2 3 4 5 car = Car.create.tap do |c| c.wheels = 4 c.move_to_showroom! end car.wheels #=> 4
Why use it?
On the surface this looks useless. In almost all cases, you could just as easily do the following:
1 2 3 4 5 def make_car car = Car.create car.wheels = 4 car.move_to_showroom! end
Why should we ever use
Tap should be used to indicate that the ‘subject’ of the method is also the return value.
For example, with the above
#make_car method, imagine if the
#move_to_showroom! method returned an instance of the car. This would mean the
#make_car method also returns the newly created car. This is far from explicit in the code, a team member could quite easily add more lines to the
#make_car method, changing the return value and breaking various parts of your application that have come to rely on
This can be avoided, changing the method to
#tap as below shouts This method absolutely needs to return an instance of Car from the proverbial rooftops.
1 2 3 4 5 6 7 def make_car Car.create.tap do |c| c.wheels = 4 c.move_to_showroom! # In future, more lines can be added here without accidentally altering the return value of the method. end end
Protecting your methods from accidental future sabotage can take many forms, but
#tap can be a handy tool for your kit.
Why not just use the explicit
return keyword in this case? I generally think explicit returns should only be used in conditionals, such as:
1 2 return car if moved_to_showroom? false
Further, I think the
tap method works better to indicate that the subject (in this case, the
Car), is the value being returned.