Rails Tutorial（2週目）-9-

#記憶トークンの作成
Ruby標準ライブラリのSecureRandomモジュールにあるurlsafe_base64メソッド（A–Z、a–z、0–9、"-"、"_"のいずれかの文字 (64種類) からなる長さ22のランダムな文字列を返す）を使用して、記憶トークンを作成する

具体的には、

```app/models/user.rb
class User < ApplicationRecord
  attr_accessor :remember_token　

#（省略）

# ランダムなトークンを返す
  def User.new_token
    SecureRandom.urlsafe_base64
  end

  # 永続セッションのためにユーザーをデータベースに記憶する
  def remember
    self.remember_token = User.new_token
    update_attribute(:remember_digest, User.digest(remember_token))
  end
end
```
としておく。

クラスメソッドのnew_tokenでは記憶トークンを作成。rememberメソッドでは、現在のユーザーのremember_token属性をnew_tokenメソッドを用いて作成する。その後、update_attributeの第一引数に変更を加える属性（:remember_digest）を指定し、第二引数にremember_tokenをdigestメソッドを用いて暗号化したものをセッティングして、値の更新を行っている。

つまり、rememberメソッドでは、記憶トークンを現在のユーザーと紐づけ、その記憶トークンを暗号化して、DBにremember_digest属性として保存している。

##cookiesに保存
```app/helpers/sessions_helper.rb
#(省略）
# ユーザーのセッションを永続的にする
  def remember(user)
    user.remember
    cookies.permanent.signed[:user_id] = user.id
    cookies.permanent[:remember_token] = user.remember_token
  end
```

上のrememberメソッドでは、user.rememberで先程のrememberクラスメソッドを引数で与えられたユーザーに対して実行。
`cookies.permanent.signed[:user_id] = user.id`
では、permanent(期限の設定）とsigned（署名付きにする）をメソッドチェーンしてcookiesに対して行う。またcookiesはsessionと同様にハッシュのように取り扱うことができる。
記憶トークンの方も同様にcookiesに保存している。

##記憶トークンの認証
```app/models/user.rb
 # 渡されたトークンがダイジェストと一致したらtrueを返す
  def authenticated?(remember_token)
    BCrypt::Password.new(remember_digest).is_password?(remember_token)
  end
```
上のコードで、暗号化されたremember_digestと、引数として与えられた値を暗号化したものが同じになるか比較して認証。

##current_userメソッドをcookiesに対応させる
```app/helpers/sessions_helper.rb
# 記憶トークンcookieに対応するユーザーを返す
  def current_user
    if (user_id = session[:user_id])
      @current_user ||= User.find_by(id: user_id)
    elsif (user_id = cookies.signed[:user_id])
      user = User.find_by(id: user_id)
      if user && user.authenticated?(cookies[:remember_token])
        log_in user
        @current_user = user
      end
    end
  end
```

`if (user_id = session[:user_id])`の部分では、変数user_idにsession[:user_id]の値を代入した結果、session[:user_id]が存在すれば、という意味。

#永続的セッションの削除
```app/models/user.rb
  # ユーザーのログイン情報を破棄する
  def forget
    update_attribute(:remember_digest, nil)
  end
```
ユーザーモデルにforgetメソッドを定義して、remember_digest属性をnilにする

```app/helpers/sessions_helper.rb
# 永続的セッションを破棄する
  def forget(user)
    user.forget
    cookies.delete(:user_id)
    cookies.delete(:remember_token)
  end

  # 現在のユーザーをログアウトする
  def log_out
    forget(current_user)
    session.delete(:user_id)
    @current_user = nil
  end
```
forgetで永続的セッションの削除。log_outで永続的セッションの削除と、セッションの削除を行う。

##チェックボックスの作成
```app/views/sessions/new.html.erb
 <%= f.label :remember_me, class: "checkbox inline" do %>
        <%= f.check_box :remember_me %>
        <span>Remember me on this computer</span>
 <% end %>

```
ここで与えられたデータは、params[:session][:remember_me]で取り出せる。

```app/controllers/sessions_controller.rb
def create
    user = User.find_by(email: params[:session][:email].downcase)
    if user && user.authenticate(params[:session][:password])
      log_in user
      params[:session][:remember_me] == '1' ? remember(user) : forget(user)
      redirect_to user
    else
      flash.now[:danger] = 'Invalid email/password combination'
      render 'new'
    end
  end
```

`params[:session][:remember_me] == '1' ? remember(user) : forget(user)`は三項演算子を使っている。
***論理値? ? 何かをする : 別のことをする***
上のコードでは、params[:session][:remember_me]の値が1なら、remember(user)を実行し、それ以外なら、forget(user)を実行するということ。

ログイン時にforget(user)を実行して大丈夫なのだろうかと思ったけれど、cookies関連の情報がnilになるだけなので、ログインはできる。