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 = \
I would say that there is no advantage to using tap
. The only potential benefit, as @sawa points out is, and I quote: "A reader would not have to read what is inside the block to know that an instance user is created." However, at that point the argument can be made that if you're doing non-simplistic record creation logic, your intent would be better communicated by extracting that logic into its own method.
I hold to the opinion that tap
is an unnecessary burden on the readability of the code, and could be done without, or substituted with a better technique, like Extract Method.
While tap
is a convenience method, it's also personal preference. Give tap
a try. Then write some code without using tap, see if you like one way over another.
The difference in terms of code readability is purely stylistic.
user = User.new.tap do |u|
u.username = "foobar"
u.save!
end
Key points:
u
variable is now used as block parameter?user
variable should now point to a User ( with a username: ‘foobar’, and who is also saved).Here’s an easy to read version of the source code:
class Object
def tap
yield self
self
end
end
For more info, see these links:
https://apidock.com/ruby/Object/tap
http://ruby-doc.org/core-2.2.3/Object.html#method-i-tap
There is a tool called flog that measures how difficult it is to read a method. "The higher the score, the more pain the code is in."
def with_tap
user = User.new.tap do |u|
u.username = "foobar"
u.save!
end
end
def without_tap
user = User.new
user.username = "foobar"
user.save!
end
def using_create
user = User.create! username: "foobar"
end
and according on flog's result the method with tap
is the most difficult to read (and I agree with it)
4.5: main#with_tap temp.rb:1-4
2.4: assignment
1.3: save!
1.3: new
1.1: branch
1.1: tap
3.1: main#without_tap temp.rb:8-11
2.2: assignment
1.1: new
1.1: save!
1.6: main#using_create temp.rb:14-16
1.1: assignment
1.1: create!
You're right: the use of tap
in your example is kind of pointless and probably less clean than your alternatives.
As Rebitzele notes, tap
is just a convenience method, often used to create a shorter reference to the current object.
One good use case for tap
is for debugging: you can modify the object, print the current state, then continue modifying the object in the same block. See here for example: http://moonbase.rydia.net/mental/blog/programming/eavesdropping-on-expressions.
I occasionally like to use tap
inside methods to conditionally return early while returning the current object otherwise.
You can make your codes more modular using tap, and can achieve a better management of local variables. For example, in the following code, you don't need to assign a local variable to the newly created object, in the scope of the method. Note that the block variable, u, is scoped within the block. It is actually one of the beauties of ruby code.
def a_method
...
name = "foobar"
...
return User.new.tap do |u|
u.username = name
u.save!
end
end
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.