Day13です!
もうそろそろチュートリアルも終盤。
今日もがんばっていきましょー!
読んでいただいている方、いつもありがとうございます!
初心者なもので、たまに間違った内容もあるかと思います。
その際にはコメントやTwitterでご指摘いただけると幸いです!
[Day13]第十一章11.2〜11.5作業ログ
実際にメールを送る機能の実装の準備
今回は実際にユーザーの有効化のメールを送る機能を作っていきます。
メイラーを下のコマンドで作成。
rails generate mailer UserMailer account_activation password_reset
実行すると、いろんなファイルができます。
- application_mailer.rb
- user_mailer.rb
- account_activation.text.erb
- account_activation.html.erb
下2つは、メールのテキストバージョンと、HTMLバージョンそれぞれのviewファイル。
また、今回必要なaccount_activationメソッドと、パスワードのリセットに必要なpassword_resetメソッド(第十二章でやる)が生成される。
このファイルたちを編集していきます。
application_mailer.rbにはメールの送り主を設定。
返信されても困るので、noreply@example.comにしておきましょう。よくありますよね。
class ApplicationMailer < ActionMailer::Base
default from: "noreply@example.com"
layout 'mailer'
end
user_mailer.rbでは、送信先と件名(Subject)を設定。
class UserMailer < ApplicationMailer
def account_activation(user)
@user = user
mail to: user.email, subject: "Account activation"
end
def password_reset
@greeting = "Hi"
mail to: "to@example.org"
end
end
ユーザー有効化のためのURLの設定
メールの本文に書きたい、ユーザー有効化のためのURLの設定を行います。
名前付きルートは下の表のようになっていました。
edit_account_activation_url(@user.activation_token, ...)
これに対応する、アカウント有効化のURLはこうなります。
http://www.example.com/account_activations/q5lt38hQDc_959PVoo6b7A/edit
ここで、アカウント有効化は、こんな流れで行うのでした。
- ユーザーの初期状態は「有効化されていない」(unactivated) にしておく。
- ユーザー登録が行われたときに、有効化トークンと、それに対応する有効化ダイジェストを生成する。
- 有効化ダイジェストはデータベースに保存しておき、有効化トークンはメールアドレスと一緒に、ユーザーに送信する有効化用メールのリンクに仕込んでおく3。
- ユーザーがメールのリンクをクリックしたら、アプリケーションはメールアドレスをキーにしてユーザーを探し、データベース内に保存しておいた有効化ダイジェストと比較することでトークンを認証する。
- ユーザーを認証できたら、ユーザーのステータスを「有効化されていない」から「有効化済み」(activated) に変更する。
メールで送ったリンクで有効化が完了するには、トークンだけでなくユーザーのメールアドレスを含める必要があります。
これは、クエリパラメータというものを使って設定します。
クエリパラメータとは、URLの末尾で疑問符「?」に続けてキーと値のペアを記述したものです
つまり、有効化のURLの後半はこんな感じになればOK。
account_activations/q5lt38hQDc_959PVoo6b7A/edit?email=foo%40example.com
ここで”%40″は@のこと。
これはエスケープという技術で、通常URLに含められない記号を変換してURLに使っている。
こんなクエリパラメータ付きのURLを作成するには、アクションに2つの引数を渡せばOKらしいです。
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) %>
チュートリアル内では、送信メールのプレビューを簡単にチェックできるツールが紹介されていました。
ここではカットします。
メールを自動送信するように設定
新規登録した時に、アカウント有効化メールを自動送信するようにしましょう。
これは実はものすごく簡単です。
アカウントを作成して、saveする時に、下のようなコードをかませてあげるだけ。
class UsersController < ApplicationController
...
def create
@user = User.new(user_params)
if @user.save
UserMailer.account_activation(@user).deliver_now
flash[:info] = "Please check your email to activate your account."
redirect_to root_url
else
render 'new'
end
end
...
end
ついでにフラッシュメッセージとリダイレクト先も変えたことに注意。
アカウントを有効化するメソッドの作成
これでアカウントを有効化するURLを送信できました。
あとはそのURLからアカウント有効化をしてくれるメソッドの作成をします。
ですが、これは記憶トークンを記憶トークンダイジェストと認証したauthenticated?メソッドとほとんど同じになります。
そこでちょっと寄り道して、共通でauthenticated?メソッドが使えるようにします。
sendメソッドでアクションを抽象化
sendメソッドを使って、authenticated?メソッドを抽象化します。
抽象化するというのは、『メタプログラミング』という技術を使って、プログラムでプログラムを作成できるようにするということです。
よくわからないので、ちょっと詳しく見ます。
例えば、配列の長さをはかるlengthメソッドとsendメソッドをチェインしてみます。
$ rails console
>> a = [1, 2, 3]
>> a.length
=> 3
>> a.send(:length)
=> 3
>> a.send("length")
=> 3
ここで、下2つについてもきちんとlengthメソッドが働いていることに注目。
このsendメソッドの引数として渡した、シンボル:lengthや文字列”length”を勝手に認識して、オブジェクトにlengthメソッドを渡してくれています。
これを応用すると、authenticated?メソッドの使用範囲をぐっと広げられます。
もともとのauthenticated?メソッドはこうでした。
def authenticated?(remember_token)
digest = self.send("remember_digest")
return false if digest.nil?
BCrypt::Password.new(digest).is_password?(remember_token)
end
sendメソッドで改良すると次のようになります。
def authenticated?(attribute, token)
digest = self.send("#{attribute}_digest")
return false if digest.nil?
BCrypt::Password.new(digest).is_password?(token)
end
引数も改名されています。
このauthenticated?メソッドでは、2つの引数をとることができ、第一引数の中身を生かして、メソッドを呼び出すことが可能。
よって、remember_digestとactivation_digestを使い分ける事ができるようになりました。
引数の数が変わったので関係あるテストコードはしっかり変えておきましょう。
editアクションで認証機能を実装
準備が整ったので、editアクションで認証機能を実装してあげます。
class AccountActivationsController < ApplicationController
def edit
user = User.find_by(email: params[:email])
if user && !user.activated? && user.authenticated?(:activation, params[:id])
user.update_attribute(:activated, 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
end
ここで、!user.activated?
はアクティベートされていないユーザーであることを確認しています。
これがないと、認証メールを盗まれたとときに、いつでもログインされてしまう。
これがあれば、認証がおわれば、その認証URLからはログインできなくなります。
ログイン方法を設定してアカウントの有効化を有効化
今のままだと、ログイン方法を変えていないので、認証がまだでも普通にログインできてしまいます。
user.activated?がtrueの場合にのみログインを許可し、そうでない場合はルートURLにリダイレクトしてwarningで警告するようにしましょう。
class SessionsController < ApplicationController
...
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
if user.activated?
log_in user
params[:session][:remember_me] == '1' ? remember(user) : forget(user)
redirect_back_or user
else
message = "Account not activated. "
message += "Check your email for the activation link."
flash[:warning] = message
redirect_to root_url
end
else
flash.now[:danger] = 'Invalid email/password combination'
render 'new'
end
end
...
end
このあと、テストやコードのリファクタリング等ありましたが、割愛。
本番環境でのメールの送信設定
やっと最後!
本番環境つまりHerokuからアカウント認証メールを遅れるようにしましょう。
これはHerokuが用意してくれている『starter tier』という機能を使います。
heroku addons:create sendgrid:starter
このコードで使えるようになります。
あとは、production環境のSMTPに情報を設定。
Rails.application.configure do
...
config.action_mailer.raise_delivery_errors = true
config.action_mailer.delivery_method = :smtp
host = '<your heroku app>.herokuapp.com'
config.action_mailer.default_url_options = { host: host }
ActionMailer::Base.smtp_settings = {
:address => 'smtp.sendgrid.net',
:port => '587',
:authentication => :plain,
:user_name => ENV['SENDGRID_USERNAME'],
:password => ENV['SENDGRID_PASSWORD'],
:domain => 'heroku.com',
:enable_starttls_auto => true
}
...
end
ここで太字にしたように、ユーザー情報には必ず環境変数を用いて、そのままuser_nameとpasswordを書かないようにしましょう。
正直この環境変数あたりがぼくめちゃくちゃ弱い。
今回はHerokuのSendGridアドオンが自動的に設定してくれるそう。
以前インターンでLINEbotを作った時に、API情報をべた書きされて注意されたなぁ。有料のAPIだったら大金請求される可能性あるって教えてもらった思い出。
さいごはadd、commit、pushして終了!
heroku run rails db:migrate
でHerokuでデータベースを回すのも忘れずに。
本番環境からしっかり認証メールが送られてくるようになりました!
感動の極み!!!!!
おつかれさまでした!
[Day13]まとめ
- 学習範囲:11.2〜11.5
- 学習時間:4時間20分
- 総学習時間:55時間20分
- 反省点:演習の沼にまたハマってた。潔くあきらめて先人の答えを見るのもアリ。時間を大切に。
- 備考:細かいところはちょびちょび飛ばすことにした。
読んでいただいている方、いつもありがとうございます!
初心者なもので、たまに間違った内容もあるかと思います。
その際にはコメントやTwitterでご指摘いただけると幸いです!
コメントを残す