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