@current_user ||= User.find_by(id: session[:user_id])って何してるの？

## はじめに
Ruby on RailsでのWEBアプリケーション開発を学習しております。
ログイン中のユーザを取得するメソッドをヘルパーメソッドとして定義した際、下記のように記述をしました。
なぜそのように書くのかがわかりにくかったので、整理していきたいと思います。
誤った解釈等ありましたらご指摘いただけると幸いです。よろしくお願いいたします。


### 問題のコード
```ruby
module SessionsHelper
  def current_user
    @current_user ||= User.find_by(id: session[:user_id])
  end
end
```


## 疑問１　そのコードで何をしているのか
```ruby
    @current_user ||= User.find_by(id: session[:user_id])
```
上記の１行は下記のように書くこともできる。
```ruby
    @current_user || @current_user = User.find_by(id: session[:user_id])
```
[解釈]
@current_userが真の場合はそのままにし、偽の場合は右辺の値 User.find_by(id: session[:user_id]) を代入する処理をしている。


## 疑問2 @currenr_userが真である確認がなぜ必要か
理屈としては　@current_user || 　がなくてもアプリケーションは動くはず。
クエリを何度も発行すると遅くなるのでそれを防いでいるのか？
とういうことで、ログを比較してみた。

#### [元のコードで投稿の詳細画面を訪れた場合]
```ruby
    @current_user ||= User.find_by(id: session[:user_id])
```
current_userメソッドで１度クエリが発行されている。（下記参照）

```
Started GET "/tasks/5" for ::1 at 2023-07-06 11:47:03 +0900
Processing by TasksController#show as HTML
  Parameters: {"id"=>"5"}
  User Load (0.9ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 2], ["LIMIT", 1]]
  ↳ app/helpers/sessions_helper.rb:3:in `current_user'
  Task Load (3.2ms)  SELECT "tasks".* FROM "tasks" WHERE "tasks"."id" = $1 LIMIT $2  [["id", 5], ["LIMIT", 1]]
  ↳ app/controllers/tasks_controller.rb:57:in `show'
  Rendering layout layouts/application.html.erb
  Rendering tasks/show.html.erb within layouts/application
  CACHE User Load (0.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 2], ["LIMIT", 1]]
  ↳ app/views/tasks/show.html.erb:14
  Label Load (2.4ms)  SELECT "labels".* FROM "labels" INNER JOIN "labellings" ON "labels"."id" = "labellings"."label_id" WHERE "labellings"."task_id" = $1  [["task_id", 5]]
  ↳ app/views/tasks/show.html.erb:21
  Rendered tasks/show.html.erb within layouts/application (Duration: 6.8ms | Allocations: 1587)
[Webpacker] Everything's up-to-date. Nothing to do
  Rendered layout layouts/application.html.erb (Duration: 13.8ms | Allocations: 8403)
Completed 200 OK in 25ms (Views: 12.5ms | ActiveRecord: 6.5ms | Allocations: 10280)
```



#### [コードを変更し投稿の詳細画面を訪れた場合]
下記に変更
```ruby
@current_user = User.find_by(id: session[:user_id])
```
ログが長い。
current_userメソッドで4度クエリが発行されている。（下記参照）
```ログ
Started GET "/tasks/1" for ::1 at 2023-07-06 11:54:10 +0900
Processing by TasksController#show as HTML
  Parameters: {"id"=>"1"}
  User Load (1.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
  ↳ app/helpers/sessions_helper.rb:4:in `current_user'
  Task Load (2.0ms)  SELECT "tasks".* FROM "tasks" WHERE "tasks"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
  ↳ app/controllers/tasks_controller.rb:57:in `show'
  Rendering layout layouts/application.html.erb
  Rendering tasks/show.html.erb within layouts/application
  CACHE User Load (0.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
  ↳ app/views/tasks/show.html.erb:14
  Label Load (2.6ms)  SELECT "labels".* FROM "labels" INNER JOIN "labellings" ON "labels"."id" = "labellings"."label_id" WHERE "labellings"."task_id" = $1  [["task_id", 1]]
  ↳ app/views/tasks/show.html.erb:21
  Rendered tasks/show.html.erb within layouts/application (Duration: 8.1ms | Allocations: 1600)
[Webpacker] Everything's up-to-date. Nothing to do
  CACHE User Load (0.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
  ↳ app/helpers/sessions_helper.rb:4:in `current_user'
  CACHE User Load (0.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
  ↳ app/helpers/sessions_helper.rb:4:in `current_user'
  CACHE User Load (0.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
  ↳ app/helpers/sessions_helper.rb:4:in `current_user'
  Rendered layout layouts/application.html.erb (Duration: 24.2ms | Allocations: 9656)
Completed 200 OK in 37ms (Views: 22.2ms | ActiveRecord: 5.7ms | Allocations: 12607)
```
[解釈]
今回のアプリではSessionsHelperをApplicationControllerでincludeし、
至るところでcurrent_userメソッドを呼び出している。
今回ログを確認した投稿詳細ページについても、
current_userを使って処理を指定していた。
<br>
例１）current_userがnilの場合、ログイン画面に遷移させるメソッドで使用
```ruby
def login_required
  redirect_to new_session_path unless current_user
end
```
例2)ユーザーがログインしていればtrue、その他ならfalseを返すメソッドで使用
```ruby
def logged_in?
  current_user.present?
end
```
<br>

このようなcurrent_userメソッドを使用した処理を行う際に、
毎回クエリを呼び出す結果となってしまうため、
データベースが大きくなればなるほど処理速度に差が出てしまいそう。（遅くなりそう）


## 疑問3　キャッシュの有効範囲
ここまでで、一度@current_userを定義していれば
その後もキャッシュが保持されて使い回せるため、
下記の書き方が良さそうだと理解した。
```ruby
    @current_user ||= User.find_by(id: session[:user_id])
```
しかし、@current_userのキャッシュはいつリセットされるのだろう？
別のページに遷移したら消えてしまうのか？という疑問に直面。

[解釈]
有効範囲・期間はそのリクエスト完了まで。
インスタンス変数のスコープはそのインスタンスである。
別のリクエストが送られるとコントローラで新しいインスタンスが作られるため、リセットされる。




## 参考にさせていただいた記事
[【Rails】@current_user ||= User.find_by(id: session[:user_id])という書き方について](https://qiita.com/aiandrox/items/f4215471f40325d69523)

[　rails tutorial のカレントユーザーのキャッシュについて](https://teratail.com/questions/221022)


<br>
最後までお読みいただきありがとうございました。