Railsチュートリアル 11章を終えました。
11章ではサインアップ時やその他メールの定型文を送る方法を見ていきます
メールアドレス有効化の概要
ユーザ新規作成時の有効化の手順はRailsチュートリアル10章までは、サインアップ画面にユーザ情報を入力することで有効化できましたが、入力されたメールアドレスが有効かどうかまでは調べれませんでした。
ここではメールアドレスが有効かどうかを調べるために、実際にメールを送り、そのメールに記載しているユーザ毎発行の有効化リンクをクリックすることで、有効化させます。
具体的には
1.Userモデルに有効化カラム(boolean)を作成し、このカラムが有効化(true)じゃないとユーザを無効化する
2.ユーザー登録が行われたときに、有効化トークンと、それに対応する有効化ダイジェストを生成する。
3.有効化ダイジェストはデータベースに保存しておき、有効化トークンはメールアドレスと一緒に、ユーザーに送信する有効化用メールのリンクに仕込んでおく
4.ユーザーがメールのリンクをクリックしたら、アプリケーションはメールアドレスをキーにしてユーザーを探し、データベース内に保存しておいた有効化ダイジェストと比較することでトークンを認証する
5.ユーザーを認証できたら、ユーザーのステータスを「有効化されていない」から「有効化済み」(activated) に変更する。
この過程はユーザ登録時のパスワード認証とよく似た仕組みで実装できます
AccountActivationsリソース
有効化のメールにはeditアクションへのURLを発行することになりますので、そのルーティングが必要になります。
route.rb
resources :account_activations, only: [:edit]
データモデル
次に、モデルに[有効化カラム]と共に、ユーザ登録時同様に、仮想属性として[有効化トークン]と[有効化ダイジェスト]を用意して認証を行うことで有効化させます
$ rails generate migration add_activation_to_users \ > activation_digest:string activated:boolean activated_at:datetime
これを実行することでUserモデルは下記のようになります
有効化コールバック
[有効化トークン]とそこから生成する[有効化ダイジェスト]の作成はUserモデルで行います。
app/model/user.rb
# 有効化トークンとダイジェストを作成および代入する def create_activation_digest self.activation_token = User.new_token self.activation_digest = User.digest(activation_token) end
これの実行はユーザーを作成する時点で実行すれば良いです。
具体的にはモデルに対して、ユーザを作成するときに動作するbefore_create
コールバックを設定することで実行できます
以前に、mailアドレスを小文字化するdowncase.email
をbefore_save
コールバックとして定義しましたが
これはユーザを登録するときに動作するものになります
before_action
はコントローラにて、とあるアクションの前に実施されるメソッドの設定になります
メイラー作成
続いてメイラーを作成していきます。具体的には$ rails g
で作成します。
$ rails generate mailer UserMailer account_activation password_reset
テンプレート作成
メイラーによって、text用のテンプレートとHTML用のテンプレートの2つ自動作成されます
後述する@userをuser_mailerに定義して、これをテンプレートに使用します。
textテンプレート
Hi <%= @user.name %>, Welcome to the Sample App! Click on the link below to activate your account: <%= edit_account_activation_url(@user.activation_token, email: @user.email) %>
htmlテンプレート
<h1>Sample App</h1> <p>Hi <%= @user.name %>,</p> <p> Welcome to the Sample App! Click on the link below to activate your account: </p> <%= link_to "Activate", edit_account_activation_url(@user.activation_token, email: @user.email) %>
クエリパラメータ付与
edit_account_activation_url(@user.activation_token,email: @user.email)
では
editルーティングにトークンとメールアドレスを与えることで
account_activations/q5lt38hQDc_959PVoo6b7A/edit?email=foo%40example.com
というトークンとURLの末尾で疑問符「?」に続けてキーと値のペアを記述したクエリパラメータ付きのURLが作成されます
このURLをメールからクリックすることで、activated属性を更新できます
メイラー作成(mailer.rb)
また、$rails g
を行うことでApplicationメイラーと、それを継承するUserメイラーが生成されます
どちらもこの課題用に少しいじります
app/mailers/application_mailer.rb
class ApplicationMailer < ActionMailer::Base default from: "noreply@example.com" layout 'mailer' end
app/mailers/user_mailer.rb
class UserMailer < ApplicationMailer # Subject can be set in your I18n file at config/locales/en.yml # with the following lookup: # # en.user_mailer.account_activation.subject # def account_activation(user) @user = user mail to: user.email, subject: "Account activation" end # Subject can be set in your I18n file at config/locales/en.yml # with the following lookup: # # en.user_mailer.password_reset.subject # def password_reset @greeting = "Hi" mail to: "to@example.org" end end
コントローラ編集
最後にサインアップ時にメールを送るアクションをコントローラに追加します
app/controller/users_controller.rb
def create @user = User.new(user_params) if @user.save @user.send_activation_email flash[:info] = "Please check your email to activate your account." redirect_to root_url else render 'new' end end
app/model/user.rb
# 有効化用のメールを送信する def send_activation_email UserMailer.account_activation(self).deliver_now end
プレビュー
ここまでくればサインアップでメールは送れますが、メールをプレビューしたいだけだと、わざわざサインアップするのは面倒です
Railsでは、特殊なURLにアクセスするとメールのメッセージをその場でプレビューすることができます。
そのためにはローカル環境設定を少しいじる必要があります
config/environments/development.rb
config.action_mailer.raise_delivery_errors = true config.action_mailer.delivery_method = :test host = 'localhost:3000' # ローカル環境 config.action_mailer.default_url_options = { host: host, protocol: 'https' }
もしクラウド環境で開発している場合host = 'rails-tutorial-mhartl.c9users.io' # クラウドIDE
とすれば良いです
次に自動生成されたuser_mailer_preview
を少しいじります。
この課題の場合、メールにユーザ情報やトークンを入れているのでプレビューにも入れます
test/mailers/previews/user_mailer_preview.rb
# Preview all emails at http://localhost:3000/rails/mailers/user_mailer class UserMailerPreview < ActionMailer::Preview # Preview this email at # http://localhost:3000/rails/mailers/user_mailer/account_activation def account_activation user = User.first user.activation_token = User.new_token UserMailer.account_activation(user) end # Preview this email at # http://localhost:3000/rails/mailers/user_mailer/password_reset def password_reset UserMailer.password_reset end end
user_mailer_preview.rb
のコメント中にあるhttp://localhost:3000/rails/mailers/user_mailer/account_activation
にアクセスすることで、メールのプレビューを見ることができます
ユーザ有効化
メール中の有効かリンクをクリックすることで、ユーザの有効化する処理を書いていきます
メールのリンクをクリックした際に送られてくる有効化トークンはparams[:id]で取り出せるので、それをダイジェストと一致するかを見ます
app/controllers/account_activations_controller.rb
def edit user = User.find_by(email: params[:email]) if user && !user.activated? && user.authenticated?(:activation, params[:id]) user.activate log_in user flash[:success] = "Account activated!" redirect_to user else flash[:danger] = "Invalid activation link" redirect_to root_url end end
app/model/user.rb
# 渡されたトークンがダイジェストと一致したらtrueを返す # トークンがダイジェストと一致したらtrueを返す def authenticated?(attribute, token) digest = send("#{attribute}_digest") return false if digest.nil? BCrypt::Password.new(digest).is_password?(token) end # アカウントを有効にする def activate update_attribute(:activated, true) update_attribute(:activated_at, Time.zone.now) end
sendメソッド
userモデル中のdigest = send("#{attribute}_digest")
について、
sendメソッドを"#{attribute}_digest"とすることでattributeに値を入れると別々のメソッドにアクセスできます
例えばattributeの値が"remember"であれば記憶ダイジェストを、
"activation"であれば有効化ダイジェストを呼び出します
最後に
メールによる認証はここまでやっていると、だんだんと理解することができそうです
あと3章で終わりですね
参考
いつもここの方にはまとめ方がうまいので感謝です