Here’s a common technique:
def foo @foo ||= compute_foo end
For those that don’t know, this is an example of memoization; we only compute_foo if @foo hasn’t been assigned a value.
Well, that’s not strictly true. Let’s say that compute_foo returns nil or false, completely acceptable values for @foo. Let’s also say that compute_foo
take 2.3 minutes, chugs 150MB of RAM while running, and hits the network like a room full of Rails programmers at a conference (just for kicks— I have no idea what compute_foo does, but it’s resource intensive, obviously).
Using this traditional memoization technique, if compute_foo returns nil, we’ll be calling compute_foo unnecessarily every successive time we call foo. Oops.
Ruby’s pretty dynamic, so let’s make use of it:
def foo
@foo = compute_foo unless instance_variables.include?('@foo')
@foo
end
Let’s add Rails-style “expirable” memoization, too:
def foo(force_reload=false)
unless force_reload || !instance_variables.include?('@foo')
@foo = compute_foo
end
@foo
end
And there you have it.
Keep in mind what I’m talking about is definitely an edge case; in most situations, compute_foo won’t return nil or false, so this won’t come up. Also, if you find expirable memoization to be tedious to do by hand (like any sane person _does_), take a look at Marcel’s memoize in aws/s3 for a nifty approach.