Understanding tap in Ruby
.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
what ruby tap method does on a {}
#tap
method simply passes an object it was called on to a block. At the end of the block it returns the same object again. This way you can chain operations or restrict variable scope.
{}.tap { |h| h[:a] = 1 }.size # => 1
You were able to chain a next method to this block. And also avoided creating a h
variable in your scope.
advantage of tap method in ruby
When readers encounter:
user = User.new
user.username = "foobar"
user.save!
they would have to follow all the three lines and then recognize that it is just creating an instance named user
.
If it were:
user = User.new.tap do |u|
u.username = "foobar"
u.save!
end
then that would be immediately clear. A reader would not have to read what is inside the block to know that an instance user
is created.
tap method in Rails 3 - Have I understood the API Docs correctly?
The tap
method has been in Ruby since 1.8.7:
tap{|x|...} => obj
Yields
x
to the block, and then returnsx
. The primary purpose of this method is to “tap into” a method chain, in order to perform operations on intermediate results within the chain.
Note that 1.8.6 did not have Object#tap
. Presumably, tap
was in older versions of Rails (as a monkey patch on Object
) but was added to Ruby itself in 1.8.7. Since 1.8.6 is rather ancient now, the Rails version was deprecated and, in more recent Rails releases, removed entirely.
Object#tap
is still around so tap
itself has not been deprecated, just the Rails monkey patched version has been removed.
Ruby: tap writes on a read?
Object#tap
couldn't be simpler:
VALUE
rb_obj_tap(VALUE obj)
{
rb_yield(obj);
return obj;
}
(from the documentation). It just yields and then returns the receiver. A quick check in IRB shows that yield
yields the object itself rather than a new object.
def foo
x = {}
yield x
x
end
foo { |y| y['key'] = :new_value }
# => {"key" => :new_value }
So the behavior of tap
is consistent with yield
, as we would hope.
Building a Rails scope using `tap`
tap
will not work for this.
all
is anActiveRecord::Relation
, a query waiting to happen.all.where(...)
returns a newActiveRecord::Relation
the new query.However checking the documentation for tap, you see that it returns the object that it was called on (in this case
all
) as opposed to the return value of the block.i.e. it is defined like this:
def tap
yield self # return from block **discarded**
self
endWhen what you wanted was just:
def apply
yield self # return from block **returned**
endOr something similar to that.
This is why you keep getting all the objects returned, as opposed to the objects resulting from the query. My suggestion is that you build up the hash you send to where
as opposed to chaining where
calls. Like so:
query = {}
query[:first_name] = options[:query] if options[:query]
query[:graduated] = options[:graduated] if options[:graduated]
# ... etc.
all.where(query)
Or a possibly nicer implementation:
all.where({
first_name: options[:query],
graduated: options[:graduated],
}.delete_if { |_, v| v.empty? })
(If intermediate variables are not to your taste.)
What does brew tap mean?
The tap command allows Homebrew to tap into another repository of formulae. Once you've done this you've expanded your options of installable software.
These additional Git repos (inside /usr/local/Homebrew/Library/Taps
) describe sets of package formulae that are available for installation.
E.g.
brew tap # list tapped repositories
brew tap <tapname> # add tap
brew untap <tapname> # remove a tap
Related Topics
How to Get the Number of Elements Having Same Attribute in HTML in Watir
Rails How to Change Attribute Name When Rendering JSON
Ruby: Differencebetween the Comparatives: "||" and "Or"
Problem with Rspec Test, Undefined Method 'Post'
Error Installing Ruby 2.6.7 on MAC Os - How to Resolve
You May Have Encountered a Bug in the Ruby Interpreter or Extension Libraries
Change String in a Date Format to Another Format
Sort a List of Objects by Using Their Attributes in Ruby
Custom Filtering of Parameters in Rails 3 Using Config.Filter_Parameters
Creating Spectral Heat Maps or Intensity Maps from Cdip Data Using Ruby
How Get Best Performance Rails Requests Parallel Sidekiq Worker
Generate Array of Numbers That Fit to a Probability Distribution in Ruby
Grabbing Snapshots from Webcams in Ruby
Ransack, Postgres - Sort on Column from Associated Table with Distinct: True