チワワかわいいブログ

RUNTEQでrails勉強する日々の記録

2020/12/17 railsチュートリアル11章

accountActivationsリソース

resources :account_activations, only: [:edit]

onlyで作るルーティングを指定できる

 GET    /account_activations/:id/edit

の:idのところはIDだけしか入れられないわけじゃなくて、トークンを入れるようにしてもいい
:idがURLに含まれるなら他のアクションでもいいが、CRUDの基本に基づきeditを使うのが適切
usersモデルにactivation_digestを作成(アカウント有効化のためのトークンをハッシュ化させた時に保存させる場所)

アカウント有効化メールの送信

メール認証の仕組み

トークンを作る

ハッシュ化する

DBに保存

メール文面でトークンを保存

メールを送ったらトークンは忘れる

受信メールのなかでトークンとメールアドレスを認証
認証のためには、トークンとIDが必要。rememberの時はcookiesに保存していた。今回はメールアドレスを使える。

actionメイラー

メールはviewを表示しているのと一緒、今までブラウザに表示させてたようなことと一緒
メール文に含めること
トークンを含んだURLをメールに送る(editアクションが反応するためのURL)
・URLクリックでGETリクエストがとび、editアクションが反応
・URLparamsの中にメールアドレス、トークン情報が入っている
・editアクションの中で、paramsのデータを使って認証する!
※activation_tokenを忘れるタイミング→認証完了を知らせるメールを送ったあと!

メイラーの作り方
rails generate mailer UserMailer account_activation password_reset

モデルと同様にgenerate、アクション(メソッド)、viewも一緒に作る
メールはテキスト用とhtml用で2つテンプレートができる
applicationmailer でデフォルトの送信元を設定できる
メイラーでのメソッドはアクションとは異なり、デフォルトでテンプレートを表示するなどの挙動はない。
メソッドに引数を渡せる、またその結果mail object(text/html)がリターンされる。

edit_account_activation_url(@user.activation_token, email: @user.email)

edit_urlの引数に@user.activation_tokenを渡す
以前 redirect_to user_url(@user.id)の引数部分は、@userに省略できると学んだけど、ドット以下に他の属性を渡すことで他の情報を渡すこともできるということ、ちょっと繋がった感
第2引数はオプションで「URLの末尾で疑問符「?」に続けてキーと値のペアを記述」できるクエリパラメータというらしい

プレビュー
host = 'localhost:3000'  
config.action_mailer.default_url_options = { host: host, protocol: 'http' }
def account_activation
    UserMailer.account_activation
    user = User.first
    user.activation_token = User.new_token
    UserMailer.account_activation(user)
end

アカウントの有効化

アカウント有効化の流れ

1.ユーザー登録時はUnactivate #カラム作成時にdefault入れたので最初はfalse
2.singupじに有効かトークンを作成(createアクション?) #before_action
3.ハッシュ値をDBに保存、有効化トークンはメール送信 #before_action&account_activationメソッド
4.リンクリックで、email=>DB検索、ハッシュ値と比較して認証(authenticate)
5.認証が通れば有効化、通らなければhome表示

Userscontrollerのcreateアクションを編集
def create
    @user = User.new(user_params)
    if @user.save #ここでbefore create→tokenとdigestへの保存がここで完了
      UserMailer.account_activation(@user).deliver_now #モデルメソッド、上で作成したtokenを使ってURLを含むmailオブジェクトができる、deliver_nowでメール送る
      flash[:info] = "Please check your email to activate your account."
      redirect_to root_url
    else
      render 'new'
    end
  end
authenticated?の抽象化

メソッドを動的に決める→sendメソッド
user.send "activation_digest"のように(シンボルでも文字列でも)引数をメソッド名で渡せる

def authenticated?(attribute, token) #引数を2つ
    digest = send("#{attribute}_digest") #メソッドを動的に
    return false if digest.nil?
    BCrypt::Password.new(digest).is_password?(token)
end

authenticated?メソッドを使っているところを書き換える

if user && user.authenticated?(:remember, cookies[:remember_token])

文字列にシンボルを渡すと、文字列に変換される
シンボルで書くことで、メソッド(またはメソッド用)というニュアンスになるとのこと

account_activate_controllerのeditアクションを編集
  def edit
    user = User.find_by(email: params[:email]) #paramsからemail持ってきてユーザーを検索
    if user && !user.activated? && user.authenticated?(:activation, params[:id])  #nilガード && ユーザーはactivateされてませんよね? && tokenはparams[:id]に入ってる
      user.update_attribute(:activated,    true) #acitvatedカラムをtrueに
      user.update_attribute(:activated_at, Time.zone.now)
      log_in user
      flash[:success] = "Account activated!"
      redirect_to user
    else
      flash[:danger] = "Invalid activation link"
      redirect_to root_url
    end
  end

本番環境でのメール

assignsメソッド
user = assigns(:user)

テストの中でコントローラーの中のインスタンス変数にアクセスするためのメソッド(テストコード時点でのオブジェクトの中身を持ってくる)