Do not return when memoizing in Ruby

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

Do not return when memoizing in Ruby
In Ruby, methods with? Do not always return true/false.
Be careful when omitting return in Ruby
Correspondence when Ruby version does not switch in rbenv
What to do when Method not found in f: ajax
Encoding when getting in Windows + Ruby
[Java] Do not use "+" in append!
Disable display when not logged in
What to do when the changes in the Servlet are not reflected
Do not use instance variables in partial
must not return in the for statement
Do not accept System.in in gradle run
Do not declare variables in List in Java
What to do when Blocked Host: "host name" appears in Ruby on Rails
Escape processing when creating a URL in Ruby
Behavior when wild card (**) is specified in ruby
What to do when rails db: seed does not reflect in the database
Class in Ruby
Final confirmation when ruby version does not switch
Heavy in Ruby! ??
What to do when debugging "Source not found"
What to do when IllegalStateException occurs in PlayFramework
rbenv versions and ruby -v versions do not match
When the project is not displayed in eclipse
"Do not show again" check does not work in the warning dialog when starting applet
Do something like a JS immediate function in Ruby
What to do when JSF tags do not become HTML
Do not write 〇〇 logic in Rails View (* added later)
About eval in Ruby
One of the causes and solutions when whales do not appear in Docker Quickstart Terminal
What to do when a could not find driver appears when connecting to a DB in a Docker environment
Output triangle in Ruby
Variable type in ruby
Ruby Learning # 16 Return Statement
What to do when Rails on Docker does not reflect controller changes in the browser
Fast popcount in Ruby
[Development log ⑬] Do not let 0 be entered in the form !!