How not to memoize in Ruby
If you are not familiar with memoization, this is a good post about the topic.
First I will show an example of where I was using memoization correctly and why that was the case. After that I will show the example where I reworked the code and the memoization introduced a bug into my codebase.
def create
{
details: account.profile,
login_url: account.create_url_with_token
}
end
def account
@account ||= Account.new(
params["email"],
params["name"]
).find_or_create
end
In the above example the create
method references account twice and account in this method should never change so it would make sense to memoize the account so the application does not need to make a second call to find_or_create
.
A problem occurred when I needed to create a handful of accounts from one request and process them. In the below example you cannot memoize the account because then the same account would be used for each of the accounts requested. But in my codebase, I had, which introduced a not so fun bug I had to fix and write a script to clean the bad data.
def create
accounts_processed = 0
return if !params["accounts_requested_count"].to_i == accounts_processed
{
details: account(accounts_processed).profile,
login_url: account(accounts_processed).create_url_with_token
}
accounts_processed++
create
end
def account(index)
Account.new(
params[index]["email"],
params[index]["name"]
).find_or_create
end
Lesson learned the hard way, but should have known better.
Similar post(s): How to memoize a conditional using Ruby