セキュリティ系の勉強、その他開発メモとか雑談. GithubはUnity触っていた頃ものがメイン Twitterフォローもよろしくです

ruby チュートリアル11章 メモ

ActionMaier

Railsに標準で組み込まれている、メールを送信してくれる機能。text形式とhtml形式のテンプレートメールを生成する事ができ、宛先や件名含めコントローラから簡単に生成する事ができる。メイラーの生成は下のコマンド。後ろの二つは追加するメソッド名を指定している。

$ rails generate mailer UserMailer account_activation password_reset

実行すると、app/views/user_mailer/account_activation.text.erbapp/views/user_mailer/account_activation.html.erbの様に2つのテンプレートが生成される(password_reset)も同様。

アカウント有効化リンクの生成

#前回
edit_user_url(user)
#生成されるurl
http://www.example.com/users/1/edit

#今回
edit_account_activation_url(@user.activation_token, ...)
http://www.example.com/account_activations/q5lt38hQDc_959PVoo6b7A/edit

上の生成されたリンク内のトークンへeditアクション内ではparams[:id]でアクセスできる。

url内@エスケープ

上で生成したurlにメールアドレスも含める場合、@エスケープする必要があるが、これはメソッドが自動で行ってくれる。

edit_account_activation_url(@user.activation_token, email: @user.email)
#生成されるurl
account_activations/q5lt38hQDc_959PVoo6b7A/edit?email=foo%40example.com

editアクション内ではparams[:email]でアクセスできる。

RSpecでのmailerのテスト

RSpecを普通に入れいていれば、チュートリアルの説明と同じ形のテストファイルがRSpec内にも生成される。ので、同じ手順に沿って進めて問題ない。

メソッドの抽象化

チュートリアルではauthenticated?メソッドを例に挙げている。今まで実装していた、リメンバートークンを比較するメソッドは下。

# トークンがダイジェストと一致したらtrueを返す
def authenticated?(remember_token)
  return false if remember_digest.nil?
  BCrypt::Password.new(remember_digest).is_password?(remember_token)
end


これをsendメソッドを用いる事で、柔軟に変数へアクセス可能となる。文字列、シンボルを渡す事が可能。

def authenticated?(attribute, token)
  digest = self.send("#{attribute}_digest") #モデル内に定義されているのでselfは省略可能
  return false if digest.nil?
  BCrypt::Password.new(digest).is_password?(token)
end

form_with使用時のエラー表示

下のサイトに書いてありますが、form_withを使用するとデフォルトではvalidation時のエラーが表示されない様です。local:trueにする事で、解決します。

    <%=form_with scope: :session, url:login_path,local: true do |f|%>

Rails 5.1のform_withでViewにvalidationエラー表示 - Qiita

アカウント有効化のフィーチャーテスト

チュートリアルでは、ユーザがサインアップしてから有効化のリンクを踏んでログインできるまでをまとめてテストしている。rspecで再現するなら、capybaraを利用して行うが、capybaraがコントローラ内のインスタンス変数が取れないので、生成されたactivation_tokenを取得できず、有効化のリンクを生成できない。代わりの手法として、生成されたメールから正規表現でリンクを取得してみた。チュートリアル上のテストではメール自体のテストを網羅できていないので、フィーチャーテストとしてはむしろ良くなったようにも見えます。

activation_url = expect(ActionMailer:: Base.deliveries.last.body.encoded).to match("^http.*?#{CGI.escape(user.email)}")


他の案としては、コントローラテストで個別に書くとか(コントローラ内からならインスタンス変数にアクセスできる)