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)
By default methods in Ruby are empty, and an empty method returns, by default, nil.
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).
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
).
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With