How would you express the idiom โ€œwith this object, if it exists, do itโ€ in Ruby?

Very often in Ruby (and Rails specifically) you have to check if something exists and then perform an action on it, for example:

if @objects.any? puts "We have these objects:" @objects.each { |o| puts "hello: #{o}" end 

It is as short as everything and everything is fine, but what if you have @objects.some_association.something.hit_database.process instead of @objects ? I would have to repeat it a second time inside the if , and what if I don't know the implementation details and method calls are expensive?

The obvious choice is to create a variable and then test it and then process it, but then you have to come up with the variable name (ugh) and it will also be in memory until the end of the area.

Why not something like this:

 @objects.some_association.something.hit_database.process.with :any? do |objects| puts "We have these objects:" objects.each { ... } end 

How do you do this?

+4
source share
4 answers

How about tap ?

 @objects.some_association.something.hit_database.process.tap do |objects| if objects.any? puts "We have these objects:" objects.each { ... } end end 
+2
source

Note that there is no reason to check that the array has at least one element with any? if you are going to send each , because sending each to an empty array is non-op.

To answer your question, maybe you are looking for https://github.com/raganwald/andand ?

+3
source

Indeed, using a variable pollutes the namespace, but still, I think that if (var = value).predicate is a fairly common idiom, and this is usually normal:

 if (objects = @objects.some_association.hit_database).present? puts "We have these objects: #{objects}" end 

Option 2: if you want to create your own abstractions in a declarative way, this is also possible with a block:

 @objects.some_association.hit_database.as(:if => :present?) do |objects| puts "We have these objects: #{objects}" end 

Writing Object#as(options = {}) pretty tricky.

+3
source

Change If you are using Ruby 1.9, the Object#tap method provides the same functions as the code below.

Looks like you just want to keep the link to the object without polluting the scope, right? How about we open the Object class and add a do method, which simply gives way to a block:

 class Object def do yield self if block_given? return self # allow chaining end end 

Then we can call, for example:

 [1,2,3].do { |a| puts a.length if a.any? } => 3 [].do { |a| puts a.length if a.any? } => nil 
+2
source

All Articles