Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby "return unless nil" idiom

Tags:

idioms

ruby

I've got a smelly method like:

def search_record(*args)    
  record = expensive_operation_1(foo)
  return record unless record.nil?

  record = expensive_operation_2(foo, bar)
  return record unless record.nil?

  record = expensive_operation_3(baz)
  return record unless record.nil?

  record = expensive_operation_4(foo, baz)
  return record unless record.nil?
end

Is there a good ruby idiom for "return result of call unless nil"?

Or should I just write a return_unless_nil(&blk) method?

(Note that args are different for each call, so I can't simply just iterate over them)

like image 688
pithyless Avatar asked Jun 15 '11 06:06

pithyless


People also ask

Does Ruby return nil by default?

By default methods in Ruby are empty, and an empty method returns, by default, nil.

How do you check if a variable is nil in Ruby?

That's the easy part. In Ruby, you can check if an object is nil, just by calling the nil? on the object... even if the object is nil. That's quite logical if you think about it :) Side note : in Ruby, by convention, every method that ends with a question mark is designed to return a boolean (true or false).


2 Answers

Do you care about the difference between nil and false here? If you only care whether the return value of each method is "falsy," then this is a pretty Rubyish way of doing it:

def search_record(*args)    
  expensive_operation_1(foo)      ||
  expensive_operation_2(foo, bar) ||
  expensive_operation_3(baz)      ||
  expensive_operation_4(foo, baz)
end

If you're unfamiliar with this idiom, it can be explained thusly: Ruby, like most languages, "short circuits" OR comparisons, meaning that if the first operand evaluates to "truey" it won't bother to evaluate the second operand (i.e. if expensive_operation_1 returns something other than nil or false, it won't ever call expensive_operation_2), because it already knows that the result of the boolean operation is true.

Another useful thing that Ruby does is, instead of returning true or false from boolean operations it just returns the last operand it evaluates. So in this case if expensive_operation_1 returns nil, it will then call expensive_operation_2, and if that returns a value (that isn't falsy), the whole expression will just evaluate to that value.

Finally, we can chain these booleans so, in effect, it will return the result of the first operand that isn't falsy and never evaluate the subsequent operands. If all of the operands evaluate to falsy, it will return the final operand (which we know is falsy and, in your case, probably nil).

like image 127
Jordan Running Avatar answered Oct 07 '22 20:10

Jordan Running


Complementing Jordan's answer: let's imagine these functions could return a boolean (unlikely for a search function, but anyway) and || does not work. We could then use the abstraction discussed here:

expensive_operation_1(foo).or_if(:nil?) do
  expensive_operation_2(foo).or_if(:nil?) do
    expensive_operation_3(foo).or_if(:nil?) do
      expensive_operation_4(foo)
    end
  end
end
like image 36
tokland Avatar answered Oct 07 '22 19:10

tokland