Let's tap in!

Posted by kriti-rai on March 1, 2018

Sorry for the cheesy pun but I am sure it has crossed your mind too (if not, o well..). Anyways, I just got done with my Music Library CLI project. After submitting my code, I went onto see how other students had solved the lab. I always try to see what many different ways something can be done. And, after checking out a few of the students’ work, I saw that a couple of them had used tap. I got intrigued as the method seemed to make the code shorter and more elegant. So, I ended up doing a little research.

tap_clipart

What does it do?

The tap method yields the object to a block and returns the object itself. In Ruby, it is coded as follows:

class Object
  def tap
	yield self
	self
	end
end
	 

Why is it useful?

Per Ruby Doc, the primary purpose of the tap method is to “tap into” a method chain, in order to perform operations on intermediate results within the chain. Let’s look at the example Ruby Doc provides:

Say you have a line of code like this:

(1..10).to_a.select {|x| x.even? }.map {|x| x*x }

With methods chained like this, it is easy to have a bug and now you need to debug the code. You can use pry but that is repetitive and tedious (and we are lazy). So, let’s look at how we can use tap to debug it.

(1..10).tap {|x| puts "original: #{x}" }    #=>original: 1..10
  .to_a.tap {|x| puts "array: #{x}" }    #=>array:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  .select {|x| x.even? } .tap {|x| puts "evens:#{x}" }    #=>evens:[2, 4, 6, 8, 10]
  .map {|x| x*x } .tap {|x| puts "squares:#{x}" }    #=>squares:[4, 16, 36, 64, 100]
	
=> [4, 16, 36, 64, 100]   #return

Clearly, with tap we were instantly able to see what each method in the chain does, making it easier for us to debug the line of code.

Besides debugging method chains, tap also cuts out extra, dangling variables, making the code look more elegant and readable. Let’s look at one of the Genre or Artist class methods in the Music Library labs, self.create_by_name(name).

The traditional and the way I did it:

def self.create_by_name(name)
    obj = self.new
    obj.name = name
	obj
 end
end

Using tap:

 def class.create_by_name(name)
  self.new.tap do {|obj| obj.name = name}
 end

Notice how elegant the second, one-liner method is? From now on, I will definitely be keeping an eye out for situations like this where I need to do something with an object and return the object itself and see if I can use tap. This method sure goes into my “sugar” toolkit box.