LoginSignup
0
0

More than 3 years have passed since last update.

Railsチュートリアル 12章まとめ

Last updated at Posted at 2021-03-02

Railsチュートリアル 12章まとめ

この章でやること

  • よくあるパスワードを忘れた際にパスワードの再設定をできるようにする
  • パスワードリセットコントローラーを作成する
  • メールを送信できるようにする
  • パスワードを再設定できるようにする

12.1 PasswordResetsリソース

PasswordResetsリソースのモデリングから
必要なデータ(再設定用のダイジェストなど)をUserモデルに追加していく

PasswordResetsもリソースとして扱っていきたいので、まずは標準的なRESTfulなURLを用意。

12.1.1 PasswordResetsコントローラ

コントローラーを生成 newとeditのメソッドも追加

$ rails generate controller PasswordResets new edit --no-test-framework

no-test-framework はテストを生成しないオプション
今回は単体テストをせず統合テストでカバーするため

リスト 12.1: パスワード再設定用リソースを追加する

config/routes.rb
    Rails.application.routes.draw do
    root   'static_pages#home'
    get    '/help',    to: 'static_pages#help'
    get    '/about',   to: 'static_pages#about'
    get    '/contact', to: 'static_pages#contact'
    get    '/signup',  to: 'users#new'
    get    '/login',   to: 'sessions#new'
    post   '/login',   to: 'sessions#create'
    delete '/logout',  to: 'sessions#destroy'
    resources :users
    resources :account_activations, only: [:edit]
    resources :password_resets,     only: [:new, :create, :edit, :update] #対応するビューを作るため
    end

RESTfulのルーティング↓

HTTPリクエスト URL Action 名前付きルート
GET password_resets/new new new_password_reset_path
POST password_resets create password_resets_path
GET password_resets/トークン/edit edit edit_password_reset_url(token)
PATCH password_resets/トークン update password_reset_url(token)

パスワード再設定画面へのリンクを追加する

app/views/sessions/new.html.erb
    <% provide(:title, "Log in") %>
    <h1>Log in</h1>

    <div class="row">
    <div class="col-md-6 col-md-offset-3">
        <%= form_with(url: login_path, scope: :session, local: true) do |f| %>

        <%= f.label :email %>
        <%= f.email_field :email, class: 'form-control' %>

        <%= f.label :password %>
        <%= link_to "(forgot password)", new_password_reset_path %>  #パスワード忘れた時のリンク作成→createアクションへ
        <%= f.password_field :password, class: 'form-control' %>

        <%= f.label :remember_me, class: "checkbox inline" do %>
            <%= f.check_box :remember_me %>
            <span>Remember me on this computer</span>
        <% end %>

        <%= f.submit "Log in", class: "btn btn-primary" %>
        <% end %>

        <p>New user? <%= link_to "Sign up now!", signup_path %></p>
  </div>
</div>

12.1.2 新しいパスワードの設定

  • Userモデルに2つのカラムを追加していく

    ・reset_digest属性   ハッシュ化した認証トークンを保存
    ・reset_sent_at属性   再設定のリンクに有効期限をつけるためにリンク送信時間を記憶

migrationファイルを作成

$ rails generate migration add_reset_to_users reset_digest:string \ reset_sent_at:datetime
$ rails db:migrate

パスワード再設定用のメールアドレスを入力するビューpassword_resets/new.html.erb
sessions/new.html.erbを参考に作成

新しいパスワード再設定画面ビュー

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_with(url: password_resets_path, scope: :password_reset,  #createアクションへpath
                        local: true) do |f| %>    
        <%= f.label :email %>
        <%= f.email_field :email, class: 'form-control' %>

        <%= f.submit "Submit", class: "btn btn-primary" %>
        <% end %>
    </div>
    </div>

12.1.3 createアクションでパスワード再設定

以下機能を持ったcreateアクションを定義する

  • フォームから送信を行った後、メールアドレスをキーとしてユーザーをデータベースから見つける
  • パスワード再設定用トークンと送信時のタイムスタンプでデータベースの属性を更新する
  • ルートURLにリダイレクトし、フラッシュメッセージをユーザーに表示
  • 送信が無効の場合は、ログイン同様にnewページを出力してflash.nowメッセージを表示

パスワード再設定用のcreateアクション

app/controllers/password_resets_controller.rb
    class PasswordResetsController < ApplicationController

    def create
        @user = User.find_by(email: params[:password_reset][:email].downcase) #formで送信された小文字化したemailから@userを取得
        if @user           #もしuserが存在すれば
        @user.create_reset_digest       #@userのcreate_reset..メソッドを発動→パスワード再設定の属性を設定
        @user.send_password_reset_email     #パスワード再設定のメールを送信する
        flash[:info] = "Email sent with password reset instructions"
        redirect_to root_url             #root_urlに飛ばす
        else
        flash.now[:danger] = "Email address not found"   #もしuserがなければ、メール見つからないとメッセージ表示
        render 'new'
        end
    end

Userモデルにパスワード再設定用メソッドを追加する

app/models/user.rb
    class User < ApplicationRecord
    attr_accessor :remember_token, :activation_token, :reset_token      #reset_token属性追加
    before_save   :downcase_email
    before_create :create_activation_digest
    .
    .
    .
    # アカウントを有効にする
    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

    # パスワード再設定の属性を設定する
    def create_reset_digest
        self.reset_token = User.new_token                #トークンを作成しreset_token属性に代入
        update_attribute(:reset_digest,  User.digest(reset_token))  #Userモデルのreset_digestにresetトークンを入れてupdateする
        update_attribute(:reset_sent_at, Time.zone.now)  #Userモデルのreset_sent_atに現在時刻を入れてupdate
    end

    # パスワード再設定のメールを送信する
    def send_password_reset_email
        UserMailer.password_reset(self).deliver_now     #今すぐにメールを送信する
    end

    private

        # メールアドレスをすべて小文字にする
        def downcase_email
        self.email = email.downcase
        end

        # 有効化トークンとダイジェストを作成および代入する
        def create_activation_digest
        self.activation_token  = User.new_token
        self.activation_digest = User.digest(activation_token)
        end
    end

12.2 パスワード再設定のメール送信

・メールを送信できるようにする

12.2.1 パスワード再設定のメールとテンプレート

UserMailer.password_reset(self).deliver_now

このコードを実装するためにUserメイラーにpassword_resetメソッドを作成し、テキストメールのテンプレートを変えていく

パスワード再設定のリンクをメール送信する

app/mailers/user_mailer.rb
    class UserMailer < ApplicationMailer


    def password_reset(user)
        @user = user
        mail to: user.email, subject: "Password reset"
    end
    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.

パスワード再設定のテンプレート(HTML)

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>

パスワード再設定のプレビューメソッド(完成)

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    #password_resetメソッド定義
        user = User.first
        user.reset_token = User.new_token
        UserMailer.password_reset(user)
    end
    end

これでhttp://localhost:3000/rails/mailers/user_mailer/password_resetのURL
(localhost:3000の部分はAWSではサーバー名に書き換える.....com/rails/mailers/...)

12.2.2 送信メールのテスト

メイラーメソッドのテストを書いていく

パスワード再設定用メイラーメソッドのテストを追加する

test/mailers/user_mailer_test.rb
    require 'test_helper'

    test "password_reset" do
        user = users(:michael)
        user.reset_token = User.new_token       #userにreset_tokenを代入する
        mail = UserMailer.password_reset(user)      #mail変数にメールを送る
        assert_equal "Password reset", mail.subject  #メールのタイトルは"Password reset"になっているか
        assert_equal [user.email], mail.to          #メールの宛先はuser.emailになっているか
        assert_equal ["noreply@example.com"], mail.from     #差出人はexample.comになっているか
        assert_match user.reset_token,        mail.body.encoded   #reset_tokenが入っているか
        assert_match CGI.escape(user.email),  mail.body.encoded   #userの.emailはescapeされているか@が%になっているか
    end
    end

12.3 パスワードを再設定する

次はPasswordResetsコントローラのeditアクションの実装

12.3.1 editアクションで再設定

先ほど定義したパスワード再設定の送信メールには、次のようなリンクが含まれている
https://example.com/password_resets/3BdBrXeQZSWqFIDRN8cxHA/edit?email=fu%40bar.com

このリンクを機能させるには、リンク先のパスワード再設定フォームを表示するビューを作る

注意点
メアドをキーにuserを検索するにはeditアクションとupdateアクションの両方でメアドが必要になる
フォームを一度送信してしまうと、メアドの情報は消えてしまう。
今回はこのメールアドレスを保持するため、隠しフィールドとしてページ内に保存する手法をとる
これにより、フォームから送信したときに、他の情報と一緒にメールアドレスが送信されるようになる

パスワード再設定のフォーム

app/views/password_resets/edit.html.erb
    <% provide(:title, 'Reset password') %>
    <h1>Reset password</h1>

    <div class="row">
    <div class="col-md-6 col-md-offset-3">
        <%= form_with(model: @user, url: password_reset_path(params[:id]),
                    local: true) do |f| %>
        <%= render 'shared/error_messages' %>

        <%= hidden_field_tag :email, @user.email %>   #hidden_field_tagを追加しメールも送るようにする

        <%= f.label :password %>                   #password入力フォーム
        <%= f.password_field :password, class: 'form-control' %>

        <%= f.label :password_confirmation, "Confirmation" %>       #確認用password入力フォーム
        <%= f.password_field :password_confirmation, class: 'form-control' %>

        <%= f.submit "Update password", class: "btn btn-primary" %>
        <% end %>
    </div>
    </div>

hidden_field_tag :email, @user.email はメールアドレスがparams[:email]に保存される
f.hidden_field :email, @user.email はparams[:user][:email]に保存される

次はeditアクションにparams[:email]が入った@userインスタンス変数を定義
authenticated?メソッドを使って認証する

パスワード再設定のeditアクション

app/controllers/password_resets_controller.rb
    class PasswordResetsController < ApplicationController
    before_action :get_user,   only: [:edit, :update]
    before_action :valid_user, only: [:edit, :update]#正しいユーザーだけ
    .
    private

        def get_user
        @user = User.find_by(email: params[:email])     #@userインスタンス変数=リンクで受け取ったメアドでuserを探す
        end

        # 正しいユーザーかどうか確認する
        def valid_user
        unless (@user && @user.activated? &&          #userが存在し、activated?がtrueかつ
                @user.authenticated?(:reset, params[:id]))      #reset_digest(テーブルのデータ)がリンクのURLと一致しているか
            redirect_to root_url
        end
        end
    end

抽象化したauthenticated?はsample_appで以下のように使われた

    authenticated?(:reset, params[:id])
    authenticated?(:remember, cookies[:remember_token])
    authenticated?(:activation, params[:id])

12.3.2 パスワードを更新する

AccountActivationsコントローラのeditアクションでは、ユーザーの有効化ステータスをfalseからtrueに変更したが、
今回の場合はフォームから新しいパスワードを送信するようになる。
したがって、フォームからの送信に対応するupdateアクションが必要
このupdateアクションでは、次の4つのケースを考慮する必要がある。

  1. パスワード再設定の有効期限が切れていないか
  2. 無効なパスワードであれば失敗させる(失敗した理由も表示する)
  3. 新しいパスワードが空文字列になっていないか(ユーザー情報の編集ではOKだった)
  4. 新しいパスワードが正しければ、更新する

(1)と(2)と(4)はこれまでの知識で対応できそうだが、(3)はどのように対応すれば良いのかあまり明確ではない

(1)「パスワード再設定の有効期限が切れていないか」については、editとupdateアクションに次のようなメソッドとbeforeフィルターを用意することで対応できそう

before_action :check_expiration, only: [:edit, :update]    # (1)への対応案

このcheck_expirationメソッドは、有効期限をチェックするPrivateメソッドとして定義

# 期限切れかどうかを確認する
def check_expiration
  if @user.password_reset_expired?#有効期限が切れていたら
    flash[:danger] = "Password reset has expired."
    redirect_to new_password_reset_url
  end
end

期限切れかどうかを確認するインスタンスメソッド「password_reset_expired?」を使っている。

beforeフィルターで保護したupdateアクションを使うことで、(2)「無効なパスワードであれば失敗させる(失敗した理由も表示する)」と(4)「新しいパスワードが正しければ、更新する」のケースに対応することができそう
例えば(2)については、更新が失敗したときにeditのビューが再描画され、パーシャルにエラーメッセージが表示されるようにすれば解決できる。
(4)については、更新が成功したときにパスワードを再設定し、あとはログインに成功したときと同様の処理を進めていけば問題なさそう。

ちょっと厄介なのが、パスワードが空文字だった場合の処理。
以前Userモデルを作っていたときに、パスワードが空でも良いallow_nilを実装をしたから
したがって、このケースについては明示的にキャッチするコードを追加する必要がある
これが考慮すべき点の(3)「 新しいパスワードが空文字列になっていないか(ユーザー情報の編集ではOKだった)」に当たる。
解決する方法として、今回は@userオブジェクトにエラーメッセージを追加する方法をとってみる

次のようにerrors.addを使ってエラーメッセージを追加する。

@user.errors.add(:password, :blank)

このように書くと、パスワードが空だった時に空の文字列に対するデフォルトのメッセージを表示してくれるようになる

以上の結果をまとめると、(1)のpassword_reset_expired?の実装を除き、すべてのケースに対応したupdateアクションが完成

app/controllers/password_resets_controller.rb
class PasswordResetsController < ApplicationController
  before_action :get_user,         only: [:edit, :update]
  before_action :valid_user,       only: [:edit, :update]
  before_action :check_expiration, only: [:edit, :update]    # (1)への対応

  def new
  end

  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

  def edit
  end

  def update
    if params[:user][:password].empty?                  # (3)への対応
      @user.errors.add(:password, :blank)
      render 'edit'
    elsif @user.update(user_params)                     # (4)への対応
      log_in @user
      flash[:success] = "Password has been reset."
      redirect_to @user
    else
      render 'edit'                                     # (2)への対応
    end
  end

  private

    def user_params
      params.require(:user).permit(:password, :password_confirmation)
    end

    # beforeフィルタ

    def get_user
      @user = User.find_by(email: params[:email])
    end

    # 有効なユーザーかどうか確認する
    def valid_user
      unless (@user && @user.activated? &&
              @user.authenticated?(:reset, params[:id]))
        redirect_to root_url
      end
    end

    # トークンが期限切れかどうか確認する
    def check_expiration
      if @user.password_reset_expired?
        flash[:danger] = "Password reset has expired."
        redirect_to new_password_reset_url
      end
    end
end

あとは、残しておいたpassword_reset_expired?の実装だけ

@user.password_reset_expired?

上のコードを動作させるために、password_reset_expired?メソッドをUserモデルで定義していく
このメソッドではパスワード再設定の期限を設定して、2時間以上パスワードが再設定されなかった場合は期限切れとする処理を行う

reset_sent_at < 2.hours.ago

上の<記号を「〜より少ない」と読んでしまうと、「パスワード再設定メール送信時から経過した時間が、2時間より少ない場合」となってしまうので、「少ない」ではなく「〜より早い時刻」と読む。
こうすると「パスワード再設定メールの送信時刻が、現在時刻より2時間以上前(早い)の場合」となり、 期待どおりの条件となる

app/models/user.rb
class User < ApplicationRecord

  # パスワード再設定の期限が切れている場合はtrueを返す
  def password_reset_expired?
    reset_sent_at < 2.hours.ago
  end

これでpassword_resets_controller.rbのcreateアクションが動く

12.3.3 パスワードの再設定をテストする

送信に成功した場合と失敗した場合の統合テストを作成する

まずは統合テストを作成

$ rails generate integration_test password_resets
      invoke  test_unit
      create    test/integration/password_resets_test.rb

パスワード再設定をテストする手順は、

  1. 最初に「forgot password」フォームを表示して無効なメールアドレスを送信
  2. 次はそのフォームで有効なメールアドレスを送信
  3. パスワード再設定用トークンが作成され、再設定用メールが送信される。
  4. メールのリンクを開いて無効な情報を送信
  5. そのリンクから有効な情報を送信して、それぞれが期待どおりに動作することを確認
test/integration/password_resets_test.rb
require 'test_helper'

class PasswordResetsTest < ActionDispatch::IntegrationTest

  def setup
    ActionMailer::Base.deliveries.clear
    @user = users(:michael)
  end

 test "password resets" do
    get new_password_reset_path#forgot passのリンク取得
    assert_template 'password_resets/new'#passリセット画面出るか
    assert_select 'input[name=?]', 'password_reset[email]'#input[name]がpassword_resetにあるか
    # メールアドレスが無効
    post password_resets_path, params: { password_reset: { email: "" } }
    assert_not flash.empty?#flashが空でないか
    assert_template 'password_resets/new'#newの画面を再描写するか
    # メールアドレスが有効
    post password_resets_path,
         params: { password_reset: { email: @user.email } }
    assert_not_equal @user.reset_digest, @user.reload.reset_digest#reset_digestカラムがreload後変化ないか
    assert_equal 1, ActionMailer::Base.deliveries.size #mailは1通だけ送ったか
    assert_not flash.empty?#flashが空でないか
    assert_redirected_to root_url#rootへリダイレクトしたか
    # パスワード再設定フォームのテスト
    user = assigns(:user)
    # メールアドレスが無効
    get edit_password_reset_path(user.reset_token, email: "")
    assert_redirected_to root_url
    # 無効なユーザー
    user.toggle!(:activated)#activatedカラムをtrueかfalseに反転(この場合false)
    get edit_password_reset_path(user.reset_token, email: user.email)
    assert_redirected_to root_url#失敗することを確認
    user.toggle!(:activated)#trueに戻す
    # メールアドレスが有効で、トークンが無効
    get edit_password_reset_path('wrong token', email: user.email)
    assert_redirected_to root_url
    # メールアドレスもトークンも有効
    get edit_password_reset_path(user.reset_token, email: user.email)
    assert_template 'password_resets/edit'#editページが描写されるか
    assert_select "input[name=email][type=hidden][value=?]", user.email#inputタグに正しい名前、type="hidden"、メールアドレスがあるかどうかを確認
    # 無効なパスワードとパスワード確認
    patch password_reset_path(user.reset_token),#createアクションへ送信
          params: { email: user.email,
                    user: { password:              "foobaz",
                            password_confirmation: "barquux" } }
    assert_select 'div#error_explanation'#errorのdivタグ出るか確認
    # パスワードが空
    patch password_reset_path(user.reset_token),
          params: { email: user.email,
                    user: { password:              "",
                            password_confirmation: "" } }
    assert_select 'div#error_explanation'#errorのdivタグ出るか確認
    # 有効なパスワードとパスワード確認
    patch password_reset_path(user.reset_token),
          params: { email: user.email,
                    user: { password:              "foobaz",
                            password_confirmation: "foobaz" } }
    assert is_logged_in?#ログインされたか
    assert_not flash.empty?#flashは空でないか
    assert_redirected_to user#@userページへ飛ぶか
  end
end

今回の新しい要素はinputタグぐらい

assert_select "input[name=email][type=hidden][value=?]", user.email

上のコードは、inputタグに正しい名前、type="hidden"、メールアドレスがあるかどうかを確認している

<input id="email" name="email" type="hidden" value="michael@example.com" />

テストコードは green になるはず

演習

1リスト 12.6にあるcreate_reset_digestメソッドはupdate_attributeを2回呼び出していますが、これは各行で1回ずつデータベースへ問い合わせしていることになります。リスト 12.20に記したテンプレートを使って、update_attributeの呼び出しを1回のupdate_columns呼び出しにまとめてみましょう(これでデータベースへの問い合わせが1回で済むようになります)。また、変更後にテストを実行し、 green になることも確認してください。ちなみにリスト 12.20にあるコードには、前章の演習(リスト 11.39)の解答も含まれています。

user.rb
  # パスワード再設定の属性を設定する
    def create_reset_digest
        self.reset_token = User.new_token#ランダムな文字列をいれる
        update_columns(reset_digest: User.digest(reset_token), reset_sent_at: Time.zone.now )
      end

2リスト 12.21のテンプレートを埋めて、期限切れのパスワード再設定で発生する分岐(リスト 12.16)を統合テストで網羅してみましょう(12.21 のコードにあるresponse.bodyは、そのページのHTML本文をすべて返すメソッドです)。期限切れをテストする方法はいくつかありますが、リスト 12.21でオススメした手法を使えば、レスポンスの本文に「expired」という語があるかどうかでチェックできます(なお、大文字と小文字は区別されません)。

password_reset_test.rb
test "expired token" do
    get new_password_reset_path#reset_pathを取得
    post password_resets_path,#mailを送る
         params: { password_reset: { email: @user.email } }

    @user = assigns(:user)
    @user.update_attribute(:reset_sent_at, 3.hours.ago)#メールを送った時間を3時間前にセット
    patch password_reset_path(@user.reset_token),#passwordの再設定をする
          params: { email: @user.email,
                    user: { password:              "foobar",
                            password_confirmation: "foobar" } }
    assert_response :redirect
    follow_redirect!
    assert_match /expired/i, response.body#Html bodyタグにexpiredがあるか確認
  end

3 2時間経ったらパスワードを再設定できなくする方針は、セキュリティ的に好ましいやり方でしょう。しかし、もっと良くする方法はまだあります。例えば、公共の(または共有された)コンピューターでパスワード再設定が行われた場合を考えてみてください。仮にログアウトして離席したとしても、2時間以内であれば、そのコンピューターの履歴からパスワード再設定フォームを表示させ、パスワードを更新してしまうことができてしまいます(しかもそのままログイン機構まで突破されてしまいます!)。この問題を解決するために、リスト 12.22のコードを追加し、パスワードの再設定に成功したらダイジェストをnilになるように変更してみましょう 。

app/controllers/password_resets_controller.rb

  def update
    if params[:user][:password].empty?
      @user.errors.add(:password, :blank)
      render 'edit'
    elsif @user.update(user_params)
      log_in @user
      @user.update_attribute(:reset_digest, nil)#追加するだけ
      flash[:success] = "Password has been reset."
      redirect_to @user
    else
      render 'edit'
    end
  end

4 リスト 12.18に1行追加し、1つ前の演習課題に対するテストを書いてみましょう。ヒント: リスト 9.25のassert_nilメソッドとリスト 11.33のuser.reloadメソッドを組み合わせて、reset_digest属性を直接テストしてみましょう。

test "password resets" do
....
patch password_reset_path(user.reset_token),
          params: { email: user.email,
                    user: { password:              "foobaz",
                            password_confirmation: "foobaz" } }
    assert_nil user.reload.reset_digest
    assert is_logged_in?#ログインされたか
    assert_not flash.empty?#flashは空でないか
    assert_redirected_to user#@userページへ飛ぶか

一番最後に追加するとエラーになるので、注意

12.4 本番環境でのメール送信(再掲)

これでパスワード再設定の実装も終わった。
あとはproduction環境でも動くようにするだけ

前章でやっているので、細かな設定はスキップ

以下スキップOK

本番環境からメール送信するために、「Mailgun」というHerokuアドオンを利用してアカウントを検証します
本チュートリアルでは、「starter tier」というサービスを使う

アプリケーションでMailgunアドオンを使うには、production環境のSMTPに情報を記入する
本番Webサイトのアドレスをhost変数に定義する必要がある。を自分のHerokuのURLに設定してください。その他の設定はこのまま使える

config/environments/production.rb
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 = {
    :port           => ENV['MAILGUN_SMTP_PORT'],
    :address        => ENV['MAILGUN_SMTP_SERVER'],
    :user_name      => ENV['MAILGUN_SMTP_LOGIN'],
    :password       => ENV['MAILGUN_SMTP_PASSWORD'],
    :domain         => host,
    :authentication => :plain,
  }
  .
  .
  .
end

この時点で、Gitのトピックブランチをmasterにマージしておく


$ rails test
$ git add -A
$ git commit -m "Add password reset"
$ git checkout master
$ git merge password-reset
`

続いてリモートリポジトリにプッシュし、Herokuにデプロイします。

$ rails test
$ git push && git push heroku
$ heroku run rails db:migrate

MailgunのHerokuアドオンをまだ追加していなければ、次のコマンドを実行

$ heroku addons:create mailgun:starter

注: herokuコマンドのバージョンが古いとここで失敗するかも。その場合はHeroku Toolbeltを使って最新版に更新するか、次の古い文法のコマンドを試す

$ heroku addons:add mailgun:starter

メール設定にはMailgunアカウントのuser_nameとpassword設定を記入する行もありますが、そこには記入せず、必ず環境変数「ENV」に設定するよう注意。
本番運用するアプリケーションでは、暗号化されていないIDやパスワードのような重要なセキュリティ情報は「絶対に」ソースコードに直接書き込まない。
そのような情報は環境変数に記述し、そこからアプリケーションに読み込む。
今回の場合、そうした変数はMailgunアドオンが自動的に設定してくれる

最後に、受信メールの認証を行います。以下のコマンドを打つと、Mailgun ダッシュボードのURLが表示されるのでブラウザで開きます。

$ heroku addons:open mailgun

MailGun公式ドキュメントに従い、受信するメールアドレスを認証。
画面左側の「Sending」→「Domains」のリストにある「sandbox」で始まるサンドボックスドメインを選択。
画面右側の「Authorized Recipients」から受信メールアドレスを認証し、本番環境でのメール送信準備は完了。

Herokuへのデプロイが完了したら、ログインページの[forgot password]リンクをクリックして、production環境でパスワードの再設定を行ってみる。
フォームから送信すると、メールが送信されてくるはず。
記載されているリンクをクリックし、無効なパスワードと有効なパスワードをそれぞれ試してみましょう。

演習

production環境でユーザー登録を試してみましょう。ユーザー登録時に入力したメールアドレスにメールは届きましたか?
メールを受信できたら、実際にメールをクリックしてアカウントを有効化してみましょう。また、Heroku上のログを調べてみて、有効化に関するログがどうなっているのか調べてみてください。ヒント: ターミナルからheroku logsコマンドを実行してみましょう。
アカウントを有効化できたら、今度はパスワードの再設定を試してみましょう。正しくパスワードの再設定ができたでしょうか?

herokuエラー中なのでスキップ

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0