Rails-tutorialのまとめ11.2(アカウント有効化のメール送信 主に演習)

その11から続く

11.2 アカウント有効化のメール送信
11.2.1 送信メールのテンプレート

メイラーは、モデルやコントローラと同様にrails generateで生成できます。

rails generate mailer UserMailer account_activation password_reset

今回必要となるaccount_activationメソッドと、
第12章で必要となるpassword_resetメソッドが生成されました。

生成したメイラーごとに、ビューのテンプレートが2つずつ生成されます。
1つはテキストメール用のテンプレート、
1つはHTMLメール用のテンプレートです。

アカウント有効化メイラーのテキストビュー (自動生成)
app/views/user_mailer/account_activation.text.erb

UserMailer#account_activation
<%= @greeting %>, find me in app/views/user_mailer/account_activation.text.erb

アカウント有効化メイラーのHTMLビュー (自動生成)
app/views/user_mailer/account_activation.html.erb

<h1>UserMailer#account_activation</h1>
  <p>
    <%= @greeting %>, find me in app/views/user_mailer/account_activation.html.erb
  </p>

生成されるHTMLメイラーのレイアウトやテキストメイラーのレイアウトはapp/views/layoutsで確認できます。
生成されたコードにはインスタンス変数@greetingも含まれています。

このインスタンス変数は、ちょうど普通のビューでコントローラのインスタンス変数を利用できるのと同じように、メイラービューで利用できます。

生成されたApplicationメイラーapp/mailers/application_mailer.rb

class ApplicationMailer < ActionMailer::Base
default from: "from@example.com"
layout 'mailer'
end

生成されたUserメイラー
app/mailers/user_mailer.rb

class UserMailer < ApplicationMailer

def account_activation
  @greeting = "Hi"
  mail to: "to@example.org"
end

def password_reset
  @greeting = "Hi"
  mail to: "to@example.org"
 end
end

生成されたテンプレートをカスタマイズして、実際に有効化メールで使えるようにします。
次に、ユーザーを含むインスタンス変数を作成してビューで
使えるようにし、user.emailにメール送信します。
mailにsubjectキーを引数として渡しています。
この値は、メールの件名にあたります。

fromアドレスのデフォルト値を更新したアプリケーションメイラー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

  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

テンプレートビューは、通常のビューと同様ERBで自由にカスタマイズできます。
ここでは挨拶文にユーザー名を含め、カスタムの有効化リンクを追加します。
この後、Railsサーバーでユーザーをメールアドレスで検索して有効化トークンを認証できるようにしたいので、
リンクにはメールアドレスとトークンを両方含めておく必要があります。
AccountActivationsリソースで有効化をモデル化したので、
トークン自体はリスト 11.1で定義した名前付きルートの引数で使われます。

edit_account_activation_url(@user.activation_token, …)

ここで思い出してみましょう。

edit_user_url(user)

上のメソッドは、次の形式のURLを生成します。

http://www.example.com/users/1/edit

これに対応するアカウント有効化リンクのベースURLは次のようになります。

http://www.example.com/account_activations/q5lt38hQDc_959PVoo6b7A/edit

URLで使えるようにBase64でエンコードされています。
/users/1/editの「1」のようなユーザーIDと同じ役割を果たします。
このトークンは、特にAccountActivationsコントローラのeditアクションではparamsハッシュでparams[:id]として参照できます。

クエリパラメータを使って、このURLにメールアドレスもうまく組み込んでみましょう。
クエリパラメータとは、URLの末尾で疑問符「?」に続けてキーと値のペアを記述したものです

acount_activations/q5lt38hQDc_959PVoo6b7A/edit?email=foo%40example.com

このとき、メールアドレスの「@」記号がURLでは「%40」となっている点に注目してください。
これは「エスケープ」と呼ばれる手法で、通常URLでは扱えない文字
を扱えるようにするために変換されています。

Railsでクエリパラメータを設定するには、
名前付きルートに対して次のようなハッシュを追加します。

edit_account_activation_url(@user.activation_token,
email: @user.email)

アカウント有効化のテキストビュー
app/views/user_mailer/account_activation.text.erb

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ビュー
app/views/user_mailer/account_activation.html.erb

<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,mail:@user.email) %>

演習

1:コンソールを開き、CGIモジュールのescapeメソッドでメールアドレスの文字列をエスケープできることを確認してみましょう。
このメソッドで”Don’t panic!”をエスケープすると、どんな結果になりますか?

CGI.escapeを使ってエスケープする

> CGI.escape(“Don’t panic!”)
=> “Don%27t+panic%21”

11.2.2 送信メールのプレビュー

定義したテンプレートの実際の表示を簡単に確認するために、
メールプレビューという裏技を使ってみましょう。
Railsでは、特殊なURLにアクセスするとメールのメッセージをその場でプレビューすることができます。
メールを実際に送信しなくてもよいので大変便利です。
これを利用するには、
アプリケーションのdevelopment環境の設定に手を加える必要があります。

development環境のメール設定config/environments/development.rb
Rails.application.configure do
  .
  .
  .
  config.action_mailer.raise_delivery_errors = true
  config.action_mailer.delivery_method = :test
  host = 'example.com' # ここをコピペすると失敗します。自分の環境に合わせてください。
  config.action_mailer.default_url_options = { host: host, protocol: 'https' }
  .
  .
  .
end
host = 'rails-tutorial-mhartl.c9users.io'     # クラウドIDE
config.action_mailer.default_url_options = { host: host, protocol: 'https' }

一方、もしローカル環境で開発している場合は、次のようになります。

host = 'localhost:3000'                     # ローカル環境
config.action_mailer.default_url_options = { host: host, protocol: 'http' }

developmentサーバーを再起動して設定を読み込んだら、
次は自動生成したUserメイラーのプレビューファイルの更新が必要です

Userメイラープレビュー (自動生成)
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
    UserMailer.account_activation
  end

  # Preview this email at
  # http://localhost:3000/rails/mailers/user_mailer/password_reset
  def password_reset
    UserMailer.password_reset
  end
end

user変数が開発用データベースの最初のユーザーになるように定義して、それをUserMailer.account_activationの引数として渡します。
このとき、ではuser.activation_tokenの値にも代入している点にご注目ください。
テンプレートでは、アカウント有効化のトークンが必要なので、
代入は省略できません。
なお、activation_tokenは仮の属性でしかないので
データベースのユーザーはこの値を実際には持っていません。

アカウント有効化のプレビューメソッド (完成)
test/mailers/previews/user_mailer_preview.rb

# Preview all emails at http://3cfb3f48c6f540c799b69407f8f01afa.vfs.cloud9.ap-northeast-1.amazonaws.com0/rails/mailers/user_mailer
class UserMailerPreview < ActionMailer::Preview

# Preview this email at
# http://3cfb3f48c6f540c799b6941afa.vfs.cloud9.ap-northeast-1.amazonaws.com/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

自分の場合クラウド9なので↓Preview Running appricationのホスト名を足す
http://3cfb3f.vfs.cloud9.ap-northeast-1.amazonaws.com/rails/mailers/user_mailer/password_reset

演習

1:Railsのプレビュー機能を使って、ブラウザから先ほどのメールを表示してみてください。「Date」の欄にはどんな内容が表示されているでしょうか?

Dateはブラウザでメールを表示した時点の日時が表示される。

11.2.3 送信メールのテスト

現在のメールの実装をテストする red
test/mailers/user_mailer_test.rb
require 'test_helper'

class UserMailerTest < ActionMailer::TestCase

  test "account_activation" do
    user = users(:michael)
    user.activation_token = User.new_token
    mail = UserMailer.account_activation(user)
    assert_equal "Account activation", mail.subject
    assert_equal [user.email], mail.to
    assert_equal ["noreply@example.com"], mail.from
    assert_match user.name,               mail.body.encoded
    assert_match user.activation_token,   mail.body.encoded
    assert_match CGI.escape(user.email),  mail.body.encoded
  end
end
green
rails test:mailers

演習

この時点で、テストスイートが greenになっていることを確認してみましょう。

green
rails test:mailers

リスト 11.20で使ったCGI.escapeの部分を削除すると、テストが redに変わることを確認してみましょう。

CGI.escapeを削除してテスト

11.2.4 ユーザーのcreateアクションを更新

ユーザー登録にアカウント有効化を追加する red
app/controllers/users_controller.rb

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

失敗するテストを一時的にコメントアウトする green
test/integration/users_signup_test.rb

require 'test_helper'

class UsersSignupTest < ActionDispatch::IntegrationTest
    #中略
    follow_redirect!
    # assert_template 'users/show'
    # assert is_logged_in?
  end
end

演習

1:新しいユーザーを登録したとき、リダイレクト先が適切なURLに変わったことを確認してみましょう。その後、Railsサーバーのログから送信メールの内容を確認してみてください。有効化トークンの値はどうなっていますか?

https://ap-northeast-1.amazonaws.com/account_activations
/my8omJYTQnPJ41g/edit?email=boku%40example.com

2:コンソールを開き、データベース上にユーザーが作成されたことを確認してみましょう。また、このユーザーはデータベース上にはいますが、
有効化のステータスがfalseのままになっていることを確認してください。

rails cで確認する

その11.3から続く

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です