tap() method explained
tap() method yields self to the block and returns self. One of the typical use cases of tap() is the method chaining over an object, operating over it inside a block, and returning the object itself.
In ruby, the implementation looks like the below, where self is passed to yield and self is returned.
def tap
Primitive.attr! :inline_block
yield(self)
self
end
Examples
If we want to operate over an object, it makes it more readable to do it as a method chain and perform an operation over the object.
Here block is updating the product object, and the return for the tap is the new product itself.
new_product = Product.new.tap do |product|
product.name = "iphone11"
product.description = "iphone11 description"
end
new_product.name
=> "iphone11"
As we can see below, we can easily chain over the list of products and tap over a narrowed product, update this description, and return the object.
Hence it makes it useful to method chain with tap and modify the object.
products = Product.all
products
.detect { |p| p.created_at < 7.days.ago }
.tap do |p|
p.description = "old product"
end
=> #<Product:0x000000011497eb08 id: 1, name: "iphone1", description: "old product", created_at: Sat, 20 May 2023 00:49:44.987968000 UTC +00:00, updated_at: Sun, 04 Feb 2024 15:20:47.842524000 UTC +00:00>
One point to note down is that tap returns self which means it will return the input object on which the operation is performed.
So if the operation that is performed inside the method chain is immutable and we depend on it as a return value, tap is not the right method.
For example, if we need a description to be returned instead of a whole object, we are out of luck with a tap.
That's where yield_self() comes into the picture.
yield_self() method explained
yield_self() method passes self to the block and returns the result of it.
Here is what the actual internal code looks like for yield_self().
def yield_self
Primitive.attr! :inline_block
unless block_given?
return Primitive.cexpr! 'SIZED_ENUMERATOR(self, 0, 0, rb_obj_size)'
end
yield(self)
end
As we can see below, yield_self() returns the last value which is the description.
new_product = Product.new.yield_self do |product|
product.name = "iphone11"
product.description = "iphone11 description"
end
=> "iphone11 description"
So considering our array of products example, if we want to return the description of the detected product that was created 7 days ago, we can use yield_self() instead of tap.
products
.detect { |p| p.created_at < 7.days.ago }
.yield_self do |p|
p.description = "old product"
end
=> "old product"
In summary
# tap()
[1,2,3].tap do |arr|
arr.map { |e| e*2 }
end
=> [1, 2, 3]
# yield_self()
[1,2,3].yield_self do |arr|
arr.map { |e| e*2 }
end
=> [2, 4, 6]
tap() and yield_self() methods are handy methods to chain the operation but their return types differ. while tap() returns the object itself, yield_self() returns the last value or custom value instead of an object.
Before You Leave
Checkout below resources for becoming a better dev:
Upgrade SQL skills by practicing it.(Use discount code: ABNEW20OFF for 20% off )
Great article!