らくだ🐫にもできるRailsチュートリアル|11.3

11.3 アカウントを有効化する

メールが生成できるようになったのでAccountActivationsコントローラのeditアクションを書いていく。
テストも書いて、しっかりテストできていたらUserモデルにコードを移していく (リファクタリング)

authenticated?メソッドの抽象化

有効化トークンとメールはそれぞれparams[:id]とparams[:email]で参照できるので
次のようなコードでユーザーを検索して承認する

(この後、上の式に論理値を1つ追加します。何が追加されるか考えてみましょう。)

(→予想:アカウントが有効化されていないことを確認する論理値 #とは 🤔)

上記のコードは、アカウント有効化のダイジェストと渡されたトークンが一致するかどうかをチェックするものだけど
authenticated?メソッドは記憶トークン用(↓参照)なので現在は正常に作動しない!

メタプログラミングとは何ぞ

メタプログラミングを一言で言うと「プログラムでプログラムを作成する」ことです。メタプログラミングはRubyが有するきわめて強力な機能であり、Railsの一見魔法のような機能 (「黒魔術」とも呼ばれます) の多くは、Rubyのメタプログラミングによって実現されています。

ちょっと何言ってるか(略
実例を見ていく

remember_digestはUserモデルの属性なので、モデル内では次のように書き換えることができます

さらに、このコードのrememberの部分を変数として扱えるようにしたい
→下記のように状況に応じて呼び出すメソッドを切り替えたい!

authenticated?メソッドにこの手法を組み込んでいく

強力なsendメソッド

sendこのメソッドは、渡されたオブジェクトに「メッセージを送る」事で呼び出すメソッドを動的に決めることができる
🤔

例を見てみましょう。Railsコンソールを開き、Rubyのオブジェクトに対してsendメソッドを実行し、配列の長さを得るとします。

sendを通して渡した「シンボル:length」や「文字列”length”」は、どちらもlengthメソッドを渡している事になる

変数attributeにシンボル:activationを代入し、文字列の式展開を使った引数をsendに渡している
→よって、activation_digestとなり同じ結果を返している

文字列やシンボルをメソッドで呼び出せる
→変数展開した文字列をメソッドとして呼びだすこともできる
→動的!スゴイ!!
という事かな?

sendメソッドを使ってauthenticated?メソッドを書き換える

これにより下記のコードでauthenticated?の従来の振舞いを再現できる

Usersモデルに実際に抽象化したauthenticated?メソッドを適用

テストの修正

この時点ではテストがRED

current_userメソッド (リスト 9.9) とnilダイジェストのテスト (リスト 9.17) の両方で、authenticated?が古いままになっており、引数も2つではなくまだ1つのままだからです。

両者を更新

これでテストがGREEN!!

演習

  1. コンソール内で新しいユーザーを作成してみてください。新しいユーザーの記憶トークンと有効化トークンはどのような値になっているでしょうか? また、各トークンに対応するダイジェストの値はどうなっているでしょうか?
  2. リスト 11.26で抽象化したauthenticated?メソッドを使って、先ほどの各トークン/ダイジェストの組み合わせで認証が成功することを確認してみましょう。

1.
有効化トークン・ダイジェストは before_create で作成・代入されているため値が入っている
記憶トークン・ダイジェストは有効化された後に代入されるためnil

2.
認証が成功することを確認

11.3.2 editアクションで有効化

authenticated?メソッドが有効化トークン・ダイジェストにも対応できるようになったので
editアクションを書くいていく → paramsハッシュで渡されたメールアドレスに対応するユーザーを認証するアクション

!user.activated?という記述にご注目ください。先ほど「1つ論理値を追加します」と言ったのはここで利用したいからです。

上のコードはすでに有効なユーザーを再度有効化させないために必要。
!user.activated? → ユーザーがactivated で は な い

また、上の論理値に基づいてユーザー認証するには、ユーザー認証後activated_atタイムスタンプを更新する必要がある。

これらを含めてeditアクションを定義していく

有効化トークンが無効だった場合の処理も行われている点にご注目ください。トークンが無効になるようなことは実際にはめったにありませんが、もしそうなった場合はルートURLにリダイレクトされる仕組みです。

リスト 11.31のコードを使うと、リスト 11.25にあるURLを貼り付けてユーザーを有効化できます。

ただし🐫の環境では日本語化による文字化けが起こっているので確認のために英語に戻しておく必要がある

これでサンプルページからサインイン→サーバーログのURLをクリックでアカウントの有効化が成功する(実際は送られてきたメールに記載されたURLにアクセス)
view系のテストが一部REDになるなどがあるけどキニシナイ!!

もちろん、この時点ではユーザーのログイン方法を変更していないので、ユーザーの有効化にはまだ何の意味もありません。ユーザーの有効化が役に立つためには、ユーザーが有効である場合にのみログインできるようにログイン方法を変更する必要があります

user.activated?がtrueの場合にのみログインを許可
falseの場合はルートURLにリダイレクトしてwarningのFlashメッセージを表示
(現状テストはRED)

本文だと「user」になっている部分を「@user」にしないとエラーになったりするので上記参照で

演習

  1. コンソールから、11.2.4で生成したメールに含まれているURLを調べてみてください。URL内のどこに有効化トークンが含まれているでしょうか?
  2. 先ほど見つけたURLをブラウザに貼り付けて、そのユーザーの認証に成功し、有効化できることを確認してみましょう。また、有効化ステータスがtrueになっていることをコンソールから確認してみてください。

1.

14行目の「7G1RkBM1H2VFV2at2cub2A」の部分が有効化トークン

2.

コンソールで生成したメールに含まれているURLを調べる

めちゃくちゃ嵌りました🐫💧
新規ユーザーを作ってどうすればいいんじゃろ。と。
まずはメールを作るのか。となって

して、
メールが作られたっぽいログが出るけどどうやって本文を読みだせばいいのだ??と
上のコードに何かつなげれば?何をつなげれば?????
自分なりに手あたり次第やってみたけどダメで、コミュで質問しました
えぇ、コミュはこちらです→にゅ〜ぶる会
「mail.bodyとかでメール本文出してなかった?」
あああああああああ!テストの時にさんざん悩んだアレねええええええええええええ!!!!!!!

いつもお世話になっております。本当にありがとうございます。

11.3.3 有効化のテストとリファクタリング

アカウント有効化の統合テストを追加する

正しい情報でユーザー登録を行った場合のテスト (7.4.4) は既にあるので、リスト 7.33で書いたテストに若干手を加えることにします。

ActionMailer::Base.deliveries.clearとはなんぞ

/sample_app/config/environments/test.rbでテスト中のメール送信モードをtestに設定している
これにより送信されたメールはdeliveriesという変数に配列として追加されていく
この追加されている内容をクリアするためclearメソッドを繋いでいる
(setupメソッドでdeliveries(変数を初期化しておかないと、並列する他のテストでエラーが発生する)

リファクタリング

テストがGREENになったのでリファクタリングを行う!
ユーザー操作の一部をコントローラからモデルに移行する

activateメソッドを作成してユーザーの有効化属性を更新し、send_activation_emailメソッドを作成して有効化メールを送信します。

消えたuser.

Userモデルには「user」という変数はないので、コントローラのコードをそのまま移すとエラーになる

(userをselfに切り替えるという手もあるのですが、selfはモデル内では必須ではないと6.2.5で解説したことを思い出しましょう。) Userメイラー内の呼び出しでは、@userがselfに変更されている点にもご注目ください。

どんなに簡単なリファクタリングであっても、この手の変更はつい忘れてしまうものです。テストをきちんと書いておけば、この種の見落としを検出できます。以上でテストスイートは greenになるはずです。

演習

  1. リスト 11.35にあるactivateメソッドはupdate_attributeを2回呼び出していますが、これは各行で1回ずつデータベースへ問い合わせしていることになります。リスト 11.39に記したテンプレートを使って、update_attributeの呼び出しを1回のupdate_columns呼び出しにまとめてみましょう (これでデータベースへの問い合わせが1回で済むようになります)。また、変更後にテストを実行し、 greenになることも確認してください。
  2. 現在は、/usersのユーザーindexページを開くとすべてのユーザーが表示され、/users/:idのようにIDを指定すると個別のユーザーを表示できます。しかし考えてみれば、有効でないユーザーは表示する意味がありません。そこで、リスト 11.40のテンプレートを使って、この動作を変更してみましょう9。なお、ここで使っているActive Recordのwhereメソッドについては、13.3.3でもう少し詳しく説明します。
  3. ここまでの演習課題で変更したコードをテストするために、/users と /users/:id の両方に対する統合テストを作成してみましょう。
    訳注: updateメソッドは、コールバックとバリデーションを実行せずにスキップしますので、コールバックやバリデーションをかける必要がある場合は注意が必要です。

1.

update_columns→データベースに直接アクセスしてカラム(属性)の値を更新する
バリデーションやコールバックはスキップされる
Railsガイド|update_columns
2.

Railsドキュメントのrenderメソッドの注意点に

同じアクション内でrenderメソッドを複数呼び出すと、エラーになるので、and returnを付ける

とある。
複数レイアウトを描画するメソッドがあると条件によって2重にレイアウトが描画されエラーになる。という事っぽい。
それを防ぐ為にand returnで明示的に処理を終了させる
redirect_toメソッドでも同様なんだけど、上記では複数呼び出していないのでand returnはなくてもエラーにならないけど何であるのかは謎
3.
準備:有効化していないテストユーザーを用意

最初に考えた手順

  1. 有効化していないユーザーでログイン
  2. ログインユーザー未認証ユーザーは有効化されていないことを確認(必要?)
  3. /usersを表示
  4. 有効化されていないユーザーは表示されていないことを確認
  5. 有効化されていないログインユーザーの/users/:idへgetのリクエスト
  6. root_urlへリダイレクト

ログインして確認するからログインテストに書く?(?
書いてる途中で/sample_app/test/integration/users_index_test.rbのindex including paginationテストと書き出しがかぶっているので
そこに機能追加していくことにした!
言われてみればページネーションを含むインデックスページのテスト。ふむ。

まとめとか感想

今回は今のところ一番大変だった!
頑張ったし沢山助けてもらいました✨ありがたや✨
特にテストに関してはほぼノーヒントじゃん?
でも表記を変えてみて希望の挙動が返ってきてたらとりあえずは安心していいのかなって。
(このブログは添削してもらっているのでとても安心だけども)

らくだ🐫にもできるRailsチュートリアルとは

「ド」が付く素人のらくだ🐫が勉強するRailsチュートリアルの学習記録です。
自分用に記録していますが、お役に立つことがあれば幸いです。

調べたとはいえらくだ🐫なりの解釈や説明が含まれます。間違っている部分もあるかと思います。そんな所は教えて頂けますと幸いなのですが、このブログにはコメント機能がありません💧お手数おかけしますがTwitterなどでご連絡いただければ幸いです