【135日目】【1日20分のRailsチュートリアル】【第10章】パスワード再設定用メイラーメソッドのテストを書く
今日は「10.2.3 PasswordResetsメイラーメソッド」のテストを書くところから。
10.2.3 PasswordResetsメイラーメソッド
アカウント有効化メイラーメソッドのテスト (リスト10.18) の場合と同様、パスワード再設定用メイラーメソッドのテストを書くことにします (リスト10.47)。
アカウント有効化のときと同じようなテストを作成する。
test/mailers/user_mailer_test.rb
test "password_reset" do user = users(:michael) user.reset_token = User.new_token mail = UserMailer.password_reset(user) assert_equal "Password reset", mail.subject assert_equal [user.email], mail.to assert_equal ["noreply@example.com"], mail.from assert_match user.reset_token, mail.body.encoded assert_match CGI::escape(user.email), mail.body.encoded end
テスト実行して問題ないことを確認。
$ bundle exec rake test 44 tests, 192 assertions, 0 failures, 0 errors, 0 skips
リスト10.43、リスト10.44、リスト10.45のコードを使用すると、正しいメールアドレスを送信したときの画面は図10.16のようになります。このメールはサーバーログではリスト10.49のように表示されます。
正しいメールアドレス送信してみよう。
サーバーを起動して
$ rails server -b $IP -p $PORT
パスワード再設定画面から有効なメールアドレスを送信。
リダイレクト後の画面にflashメッセージが表示されることを確認。
パスワード再設定メールもサーバーログに出力されてました。
Sent mail to <メールアドレス> (16.9ms) Date: Wed, 19 Apr 2017 01:58:18 +0000 From: noreply@example.com To: <メールアドレス> Message-ID: <58f6c43a2f077_6c13f9569dca45481827@kt-zoe-rails-tutorial-3478208.mail> Subject: Password reset Mime-Version: 1.0 Content-Type: multipart/alternative; boundary="--==_mimepart_58f6c43a2b791_6c13f9569dca4548172a"; charset=UTF-8 Content-Transfer-Encoding: 7bit ----==_mimepart_58f6c43a2b791_6c13f9569dca4548172a Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 7bit To reset your password click the link below: http://rails-tutorial-kt-zoe.c9users.io//password_resets/DQVXIK-zO7OtQwQfF7VaWQ/edit?email=<メールアドレス> This link will expire in two hours. If you did not request your password to be reset, please ignore this email and your password will stay as it is. ----==_mimepart_58f6c43a2b791_6c13f9569dca4548172a Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: 7bit <html> <body> <h1>Password reset</h1> <p>To reset your password click the link below:</p> <a href="http://rails-tutorial-kt-zoe.c9users.io//password_resets/DQVXIK-zO7OtQwQfF7VaWQ/edit?email=<メールアドレス>">Reset password</a> <p>This link will expire in two hours.</p> <p> If you did not request your password to be reset, please ignore this email and your password will stay as it is. </p> </body> </html> ----==_mimepart_58f6c43a2b791_6c13f9569dca4548172a--
(<メールアドレス>の部分は実際にはフォームに入れたメールアドレスが表示される)
メールに記載されているURLが動作するにはまだ実装が必要らしい。もうちょいかな。
今日の学習時間は【17分】。
次は「10.2.4 パスワードを再設定する」から。
【134日目】【1日20分のRailsチュートリアル】【第10章】パスワードリセットのメールプレビュー機能を実装する
今日は「10.2.3 PasswordResetsメイラーメソッド」から。
10.2.3 PasswordResetsメイラーメソッド
最初にユーザーメイラーにpassword_resetメソッドを作成し (リスト10.43)、続いてテキストメールのビューテンプレート (リスト10.44) と HTMLメールのビューテンプレート (リスト10.45) をそれぞれ定義します。
アカウント有効化のときと同じようにメソッドとビュー追加。
app/mailers/user_mailer.rb
def password_reset(user) @user = user mail to: user.email, subject: "Password reset" end
app/views/user_mailer/password_reset.text.erb
To reset your password click the link below: <%= edit_password_reset_url(@user.reset_token, email: @user.email) %> This link will expire in two hours. If you did not request your password to be reset, please ignore this email and your password will stay as it is.
app/views/user_mailer/password_reset.html.erb
<h1>Password reset</h1> <p>To reset your password click the link below:</p> <%= link_to "Reset password", edit_password_reset_url(@user.reset_token, email: @user.email) %> <p>This link will expire in two hours.</p> <p> If you did not request your password to be reset, please ignore this email and your password will stay as it is. </p>
アカウント有効化メールの場合 (10.1.2) と同様、Railsのメールプレビュー機能でパスワード再設定のメールをプレビューしましょう。
メールプレビュー機能を実装する。
test/mailers/previews/user_mailer_preview.rb
# Preview this email at http://localhost:3000/rails/mailers/user_mailer/password_reset def password_reset user = User.first user.reset_token = User.new_token UserMailer.password_reset(user) end
ここまで実装できたら動作確認。
サーバーを起動して
$ rails server -b $IP -p $PORT
<ローカルアドレス>/rails/mailers/user_mailer/password_reset
にアクセス。
パスワードリセットのメールプレビューが確認できました。
今日の学習時間は【17分】。
次は「10.2.3 PasswordResetsメイラーメソッド」のテストを書くところから。
【133日目】【1日20分のRailsチュートリアル】【第10章】パスワード再設定画面でメールアドレスを送信したときの処理
今日は「10.2.2 PasswordResetsコントローラとフォーム」のフォームからメールアドレスを送信するところから。
10.2.2 PasswordResetsコントローラとフォーム
図10.12のフォームから送信を行なった後、メールアドレスをキーとしてユーザーをデータベースから見つけ、パスワード再設定用トークンと送信時のタイムスタンプでデータベースの属性を更新する必要があります。
(中略) 送信が無効の場合は、ログイン (リスト8.9) と同様にnewページを出力してflash.nowメッセージを表示します。
パスワード再設定フォームからメールアドレスが送信された後の処理をcreateアクションとして追加する。
app/controllers/password_resets_controller.rb
def create @user = User.find_by(email: params[:password_reset][:email].downcase) if @user @user.create_reset_digest @user.send_password_reset_email flash[:info] = "Email sent with password reset instructions" redirect_to root_url else flash.now[:danger] = "Email address not found" render 'new' end end
Userモデル内のコードは、before_createコールバック (リスト10.3) 内で使用されるcreate_activation_digestメソッドと似ています(リスト10.42)。
上で追加したcreate_reset_digest
メソッドとかあったっけ、と思ってたらここで追加するのか。
アカウント有効化の処理と似てますね。
app/models/user.rb
attr_accessor :remember_token, :activation_token, :reset_token : # パスワード再設定の属性を設定する def create_reset_digest self.reset_token = User.new_token update_attribute(:reset_digest, User.digest(reset_token)) update_attribute(:reset_sent_at, Time.zone.now) end # パスワード再設定のメールを送信する def send_password_reset_email UserMailer.password_reset(self).deliver_now end :
図10.13に示すように、この時点でのアプリケーションは、無効なメールアドレスを入力した場合に正常に動作します。
サーバーを起動して
$ rails server -b $IP -p $PORT
パスワード再設定画面で適当なメールアドレス(test01@example.com)を入れて送信してみる。
エラーのfalshメッセージ表示されました。
正しいメールアドレス送信が正常に動作するにはメイラーメソッドの定義が必要らしい。まだか。
今日の学習時間は【26分】。
次は「10.2.3 PasswordResetsメイラーメソッド」から。
【132日目】【1日20分のRailsチュートリアル】【第10章】パスワード再設定フォームを追加する
今日は「10.2.2 PasswordResetsコントローラとフォーム」から。
10.2.2 PasswordResetsコントローラとフォーム
ログインフォームを参考に、パスワード再設定フォームのビューを実装する。
app/views/password_resets/new.html.erb
<% provide(:title, "Forgot password") %> <h1>Forgot password</h1> <div class="row"> <div class="col-md-6 col-md-offset-3"> <%= form_for(:password_reset, url: password_resets_path) do |f| %> <%= f.label :email %> <%= f.email_field :email, class: 'form-control' %> <%= f.submit "Submit", class: "btn btn-primary" %> <% end %> </div> </div>
サーバーを起動して
$ rails server -b $IP -p $PORT
ログインフォームの「forgot password」から
<ローカルアドレス>/password_resets/new
にアクセス。
パスワード再設定フォームが表示されました。
今日はかなり短いけどここまで。。。
今日の学習時間は【7分】。
次は「10.2.2 PasswordResetsコントローラとフォーム」のフォームからメールアドレスを送信するところから。
【131日目】【1日20分のRailsチュートリアル】【第10章】ログイン画面にパスワード再設定画面へのリンクを追加する
今日は「10.2 パスワードの再設定」から。
10.2 パスワードの再設定
パスワード再設定の仕組みは、アカウント有効化と似ている部分が多く、10.1で学んだ手法の多くをここでも適用できます。
1passwordを導入してからパスワード忘れること減ったけど、それより前にサービス登録したやつなんか結構パスワード再発行(再設定)したりする。。。
アカウント有効化の際と似ていて、PasswordResetsリソースを作成して、再設定用のトークンとそれに対応するダイジェストを保存するのが今回の目的となります。
ふーむ、パスワード再設定フォーム表示するまでの処理は似てる、ってことかな。
10.2.1 PasswordResetsリソース
アカウント有効化 (10.1.1) の場合と同様、最初に新しいリソースで使用するコントローラを生成します。
下記コマンドでコントローラを生成。
$ rails generate controller PasswordResets new edit --no-test-framework create app/controllers/password_resets_controller.rb route get 'password_resets/edit' route get 'password_resets/new' invoke erb create app/views/password_resets create app/views/password_resets/new.html.erb create app/views/password_resets/edit.html.erb invoke helper create app/helpers/password_resets_helper.rb invoke assets invoke coffee create app/assets/javascripts/password_resets.coffee invoke scss create app/assets/stylesheets/password_resets.scss
新しいパスワードを再設定するためのフォーム (図10.8) と、Userモデル内のパスワードを変更するためのフォーム (図10.9) が両方必要になるので、今回はnew、create、edit、updateのルーティングも必要になります。
deleteは要らないからnew、create、edit、updateを指定、ってことなのかな…。
config/routes.rb
resources :password_resets, only: [:new, :create, :edit, :update]
ログイン画面のパスワード入力フォームの上にパスワード再設定画面へのリンクを追加。
app/views/sessions/new.html.erb
<%= link_to "(forgot password)", new_password_reset_path %>
ちゃんとリンク追加できたか見た目確認してみよう。
サーバーを起動して
$ rails server -b $IP -p $PORT
<ローカルアドレス>/login
にアクセス。
テキストリンク追加されてます。
パスワード再設定画面はまだViewいじってないのでデフォルトのまま。
セキュリティ上の注意点をもうひとつ。再設定用のリンクはなるべく短時間 (数時間以内) で期限切れになるようにしなければなりません。そのために、再設定メールの送信時刻も記録する必要があります。
パスワード再設定は期限切れ設定するのか。まぁそうだよね…!
パスワード再設定処理向けにUserモデルにreset_digest属性とreset_sent_at属性を追加する。
$ rails generate migration add_reset_to_users reset_digest:string reset_sent_at:datetime invoke active_record create db/migrate/20170410023324_add_reset_to_users.rb $ bundle exec rake db:migrate -- add_column(:users, :reset_digest, :string) -> 0.0011s -- add_column(:users, :reset_sent_at, :datetime) -> 0.0004s
追加できたー。
今日の学習時間は【27分】。
次は「10.2.2 PasswordResetsコントローラとフォーム」から。
【130日目】【1日20分のRailsチュートリアル】【第10章】アカウント有効化処理のリファクタリング
今日は「10.1.4 有効化のテストとリファクタリング」のリファクタリングするところから。
10.1.4 有効化のテストとリファクタリング
今日はリファクタリング。
activateメソッドを作成してユーザーの有効化属性を更新し、send_activation_emailメソッドを作成して有効化メールを送信します。
activateメソッド欲しいなー、と思ってました。ソースの可読性上がるよね。
Userモデルにメソッド2つ追加して
app/models/user.rb
# アカウントを有効にする def activate update_attribute(:activated, true) update_attribute(:activated_at, Time.zone.now) end # 有効化用のメールを送信する def send_activation_email UserMailer.account_activation(self).deliver_now end private :
Userコントローラーのメール送信処理をsend_activation_emailメソッドに置き換えて
app/controllers/users_controller.rb
def create @user = User.new(user_params) if @user.save @user.send_activation_email :
アカウント有効化コントローラーのアカウント有効化処理をactivateメソッドに置き換えた。
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 :
リスト10.33ではuser.という記法を使用していないことにご注目ください。Userモデルにはそのような変数はないので、これがあるとエラーになります。
置き換え前はuser.という記法使ってたけど、Userモデルに移動させたことでuser→selfになるからいらなくなるよ、ってことか…。
処理コピペすると変更するの忘れそう。。。
リファクタリングできたのでテスト実行。問題なし。
$ bundle exec rake test 43 tests, 185 assertions, 0 failures, 0 errors, 0 skips
ついにアカウントの有効化を実装できました。きりのよい所でGitにコミットしておきましょう。
コミットしました。
$ git add -A
$ git commit -m "Add account activations"
今日の学習時間は【18分】。
次は「10.2 パスワードの再設定」から。
【129日目】【1日20分のRailsチュートリアル】【第10章】アカウント有効化の統合テストを追加する
今日は「10.1.4 有効化のテストとリファクタリング」から。
10.1.4 有効化のテストとリファクタリング
この節では、アカウント有効化の統合テストを追加します。
正しい情報でユーザー登録を行った場合のテスト (リスト7.26) は既にあるので、7.4.4で開発したテストに若干手を加えることにします。
setupメソッドを追加して、
“invalid signup information” はそのまま?(でも警告表示のテストが抜けてたので追加した)
“valid signup information with account activation"は後半新規追加。
test/integration/users_signup_test.rb
class UsersSignupTest < ActionDispatch::IntegrationTest def setup ActionMailer::Base.deliveries.clear end : test "valid signup information with account activation" do : post users_path, user: { name: "Example User", : assert_equal 1, ActionMailer::Base.deliveries.size user = assigns(:user) assert_not user.activated? # 有効化していない状態でログインしてみる log_in_as(user) assert_not is_logged_in? # 有効化トークンが不正な場合 get edit_account_activation_path("invalid token") assert_not is_logged_in? # トークンは正しいがメールアドレスが無効な場合 get edit_account_activation_path(user.activation_token, email: 'wrong') assert_not is_logged_in? # 有効化トークンが正しい場合 get edit_account_activation_path(user.activation_token, email: user.email) assert user.reload.activated? follow_redirect! assert_template 'users/show' assert is_logged_in? end
有効化トークンに関するテスト、この順番が重要なんだろうな。正しい場合からテストすると期待してる動作にはならないもんね。たぶん
リスト10.31のassignsメソッドは本チュートリアル初登場です。
第8章の演習 (8.6) で説明したように、assignsメソッドを使用すると、対応するアクション内にあるインスタンス変数にアクセスできるようになります。
Usersコントローラのcreateアクションでは@userというインスタンス変数が定義されていて、そのインスタンス変数にアクセスできる、ってことらしい。
うーん、難しいな…。
テストは通ることを確認。
$ bundle exec rake test 43 tests, 185 assertions, 0 failures, 0 errors, 0 skips
今日の学習時間は【23分】。