(steps on soap box)
I think Ruby's Object#tap
method is abused. I see code like this all the time:
obj = SomeClass.new.tap do |o|
o.blah = true
end
What good does tap do here? It saves 0 lines of code. It makes it complex for no reason. And if it's a particularly long block, it just makes it harder to see what actually gets returned. To me, it feels like trying to be clever for the sake of being clever.
Compare it to the "traditional" way:
obj = SomeClass.new
obj.blah = true
obj
Same number of lines of code.
Same return value.
Only 1 local variable (as opposed to needing to come up with 2 different names like above with obj
and o
).
No unnecessary complexity.
I prefer this simpler version.
(steps off soap box)
Here's a better, more appropriate use of tap:
list
.map { |o| o.foo.bar }
.reverse
.more_transform .tap { |things| puts "transformed: #{things.inspect}" }
.even_more_transformations
...as if to "tap into" a method chain (to quote the official Ruby documentation).
This can be useful with debugging a series of ActiveRecord chained scopes, e.g.
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
How is this better that just doing 'puts result' ?