Obviously ||= won’t work
def x?
@x_query ||= expensive_way_to_calculate_x
end
because if it turns out to be false or nil, then expensive_way_to_calculate_x will get run over and over.
Currently the best way I know is to put the value into an Array:
def x?
return @x_query.first if @x_query.is_a?(Array)
@x_query = [expensive_way_to_calculate_x]
@x_query.first
end
Is there a more conventional or efficient way of doing this?
UPDATE I realized that I wanted to memoize nil in addition to false – this goes all the way back to https://rails.lighthouseapp.com/projects/8994/tickets/1830-railscachefetch-does-not-work-with-false-boolean-as-cached-value – my apologies to Andrew Marshall who gave an otherwise completely correct answer.
Explicitly check if the value of
@x_queryisnilinstead:Note that if this wasn’t an instance variable, you would have to check if it was defined also/instead, since all instance variables default to
nil.Given your update that
@x_query‘s memoized value can benil, you can usedefined?instead to get around the fact that all instance variables default tonil:Note that doing something like
a = 42 unless defined?(a)won’t work as expected since once the parser hitsa =,ais defined before it reaches the conditional. However, this isn’t true with instance variables since they default tonilthe parser doesn’t define them when it hits=. Regardless, I think it’s a good idiom to useororunless‘s long block form instead of a one-lineunlesswithdefined?to keep it consistent.