The bottom line is that you shouldn't make an early return when memoizing in Ruby (probably not just in Ruby).
def user
@user ||= begin
user = User.find_or_initialize_by(id: 1)
return user if user.persisted? #Do not return while defining instance variables
user.save
user
end
end
def user
@user ||= begin
user = User.find_or_initialize_by(id: 1)
if user.persisted?
user
else
user.save
user
end
end
end
#Or
def user
@user ||= begin
user = User.find_or_initialize_by(id: 1)
user.save if user.new_record?
user
end
end
When using memoization in Ruby (Rails), I think that you may write as follows.
def user
@user ||= User.first
end
If there are multiple lines, enclose them in begin
~ ʻend`.
def user
@user ||= begin
user = user.find_or_initialize_by(id: 1)
if user.persisted?
user
else
user.save
user
end
end
end
Let's use early return, which is a common refactoring technique.
def user
@user ||= begin
user = User.find_or_initialize_by(id: 1)
return user if user.persisted?
user.save
user
end
end
At first glance, it looks good, but when I run this method in the rails console, it looks like this:
[1] pry(main)> def user
[1] pry(main)* @user ||= begin
[1] pry(main)* user = User.find_or_initialize_by(id: 1)
[1] pry(main)* return user if user.persisted?
[1] pry(main)*
[1] pry(main)* user.save
[1] pry(main)* user
[1] pry(main)* end
[1] pry(main)* end
=> :user
[2] pry(main)> user
User Load (3.4ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
=> #<User:0x00007ff0b4d2e338
id: 1,
created_at: Tue, 09 Jun 2020 15:04:04 JST +09:00,
updated_at: Tue, 09 Jun 2020 15:04:04 JST +09:00>
[3] pry(main)> user
User Load (1.8ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
=> #<User:0x00007ff0ad554740
id: 1,
created_at: Tue, 09 Jun 2020 15:04:04 JST +09:00,
updated_at: Tue, 09 Jun 2020 15:04:04 JST +09:00>
No memoization is done and a query is issued every time. If you check the reference again,
Ruby 2.7.0 Reference Manual https://docs.ruby-lang.org/ja/latest/doc/spec=2fcontrol.html#return return Ends the method execution with the value of the expression as the return value.
Because it says, the ʻuser method ends at the stage of
return, and the instance variable of
@ user remains
nil`, so the process is executed every time.
It's natural when you think about it, but be careful because you may get addicted to writing that you often use early returns in your habit.
Recommended Posts