14

I am reviewing a piece of code from a Rails project and I came across the tap method. What does it do?

Also, it would be great if someone could help me understand what the rest of the code does:

def self.properties_container_to_object properties_container
  {}.tap do |obj|
  obj['vid'] = properties_container['vid'] if properties_container['vid']
  obj['canonical-vid'] = properties_container['canonical-vid'] if   properties_container['canonical-vid']
  properties_container['properties'].each_pair do |name, property_hash|
  obj[name] = property_hash['value']
  end
 end
end

Thanks!

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Manmeet
  • 271
  • 3
  • 9
  • Please refer to the docs first in cases like this :-) [Object#tap](http://ruby-doc.org/core-2.1.2/Object.html#method-i-tap) – Daniël Knippers Aug 07 '14 at 20:43
  • 5
    The method, which is unnecessarily convoluted, is building a hash. See [here](http://seejohncode.com/2012/01/02/ruby-tap-that/) for a clear explanation on `tap`. – Damien Roche Aug 07 '14 at 20:45
  • While I agree with @DamienRoche it is probably not the most idiomatic way to go about this it is actually flattening a `hash`. It is taking in a hash `properties_container` containing a `hash` of properties and then creating a new `hash` where the `properties` are on the top-level. Not sure why but that is what is happening. – engineersmnky Aug 07 '14 at 20:49
  • 2
    See also http://stackoverflow.com/questions/17493080/advantage-of-tap-method-in-ruby – Mark Thomas Aug 07 '14 at 20:56
  • 1
    `tap` is just the K combinator, nothing special. – Jörg W Mittag Aug 08 '14 at 03:06
  • Excuse me but what's the reason for down voting this question? You see @manmeet is a new user and you don't even provide a feedback for him – Filip Bartuzi Jul 20 '15 at 16:07
  • See also [what ruby tap method does on a `{}`](https://stackoverflow.com/questions/25522708/what-ruby-tap-method-does-on-a) – ggorlen Aug 20 '23 at 19:27

2 Answers2

18

.tap is here to "perform operations on intermediate results within a chain of methods" (quoting ruby-doc).

In other words, object.tap allows you to manipulate object and to return it after the block:

{}.tap{ |hash| hash[:video] = 'Batmaaaaan' }
# => return the hash itself with the key/value video equal to 'Batmaaaaan'

So you can do stuff like this with .tap:

{}.tap{ |h| h[:video] = 'Batmaaan' }[:video]
# => returns "Batmaaan"

Which is equivalent to:

h = {}
h[:video] = 'Batmaaan'
return h[:video]

An even better example:

user = User.new.tap{ |u| u.generate_dependent_stuff }
# user is equal to the User's instance, not equal to the result of `u.generate_dependent_stuff`

Your code:

def self.properties_container_to_object(properties_container)
  {}.tap do |obj|
    obj['vid'] = properties_container['vid'] if properties_container['vid']
    obj['canonical-vid'] = properties_container['canonical-vid'] if   properties_container['canonical-vid']
    properties_container['properties'].each_pair do |name, property_hash|
      obj[name] = property_hash['value']
    end
  end
end

Is returning a Hash beeing filled in the .tap block

The long-version of your code would be:

def self.properties_container_to_object(properties_container)
  hash = {}

  hash['vid'] = properties_container['vid'] if properties_container['vid']
  hash['canonical-vid'] = properties_container['canonical-vid'] if   properties_container['canonical-vid']
  properties_container['properties'].each_pair do |name, property_hash|
    hash[name] = property_hash['value']
  end

  hash
end
epsilones
  • 11,279
  • 21
  • 61
  • 85
MrYoshiji
  • 54,334
  • 13
  • 124
  • 117
0

Tap is a Ruby method from the Object class.

This method yields x to the block and then returns x. This method is used to "tap into" a method chain, to perform operations on intermediate results within the chain.

Darkmouse
  • 1,941
  • 4
  • 28
  • 52