Memoize Expensive Method Calls In Ruby To Avoid Repeated Computation
When a method performs an expensive operation — a database query, an API call, a complex calculation — calling it repeatedly wastes time and resources. Memoization caches the result on first call and returns it instantly on subsequent calls.
Description
The idiomatic Ruby memoization pattern uses the ||= operator to assign the result of an expression only if the variable is currently nil or false. For methods that take arguments, instance_variable_get and instance_variable_set let you cache results keyed by the arguments.
This technique is foundational in Rails model code where methods like current_user or computed aggregates would otherwise re-query the database on each call.
Note: ||= has a subtle edge case — if the method legitimately returns false or nil, it will recompute every time. Use explicit defined? checking when the result can be falsy.
Sample input:
class ReportBuilder
def expensive_summary
# Simulates a slow database aggregation
sleep(2)
"Summary result"
end
end
builder = ReportBuilder.new
builder.expensive_summary # slow
builder.expensive_summary # slow again — no memoization
Sample Output (after memoization):
builder.expensive_summary # slow on first call
builder.expensive_summary # instant — returns cached value
Answer
class ReportBuilder
# Simple memoization — works when result is never nil/false
def expensive_summary
@expensive_summary ||= begin
sleep(2)
"Summary result"
end
end
# Safe memoization when result can be nil or false
def nullable_result
return @nullable_result if defined?(@nullable_result)
@nullable_result = compute_something_that_might_be_nil
end
# Memoization with arguments
def score_for(user_id)
@scores ||= {}
@scores[user_id] ||= UserScoreCalculator.run(user_id)
end
end
Check viewARU - Brand Newsletter!
Newsletter to DEVs by DEVs - boost your Personal Brand & career! 🚀