これから行っていくもの
- AccountActivationsコントローラーの
edit
アクションの実装 - 当該アクションに対するテストの実装
- AccountActivationsコントローラーに関係するリファクタリング
- AccountActivationsコントローラーからUserモデルにコードを移していく
authenticated?
メソッドの抽象化
前提
アカウント有効化リンクにおけるparams
ハッシュ
http://www.example.com/account_activations/C477rjyLBojP4v5nfKcoqQ/edit?email=foo%40example.com
例えば上述のようなURLでAccountActivations#edit
アクションを呼び出した場合、アカウントの有効化に必要な属性は、params
ハッシュを使って以下のように参照できます。
- 有効化トークンは、
params[:id]
として参照できる - メールアドレスは、
params[:email]
として参照できる
既存のauthenticated?
メソッド
今回製作中のサンプルアプリケーションには、すでに「与えられたトークンとRDBに記録されたダイジェストの両者を照合し、合致すれば認証する」という実装は存在します。User#authenticated?
というメソッドがそれですね。
def authenticated?(remember_token)
return false if remember_digest.nil?
BCrypt::Password.new(remember_digest).is_password?(remember_token)
end
なお、User#authenticated?
の実装については、大枠はそのままながら引数のとり方を変更していきます。
アカウント有効化リンクからユーザーを検索・認証する仕組み
上記前提を踏まえ、「アカウント有効化リンクからユーザーを検索・認証する仕組み」は以下のように実装していくことになります。
user = User.find_by(email: params[:email])
if user && user.authenticated?(:activation, params[:id])
User#authenticated?
の実装の変更
remember_digest
はUserモデルの属性です。Userモデル内においては、以下のように使うことができます。
self.remember_digest
では、上記remember_digest
のremenber
という部分を可変にし、「状況に応じて呼び出すメソッドを変える」という実装を実現するためにはどうすればいいか。そこがUser#authenticated?
の実装の変更ポイントとなります。すなわち、以下のような呼び出しができるようにしたい、ということです。
self.FOOBAR_digest
メタプログラミング
「プログラムそのものをプログラムによって作成する」というプログラミング手法のことです。「Rubyの言語体系に含まれるメタプログラミング手法は、Smalltalkの影響1が強いものである」とはよく言及されますね。とりわけRailsにおいては、メタプログラミング手法の多用により、「素のRubyとは別物」と言われるほどの言語体系が構築されるに至っています。
Railsチュートリアルの第11章においては、method_missing
2までは言及されておらず、send
についてのみ言及されています。
send
メソッドの動作
# rails console --sandbox
>> a = [1, 2, 3]
>> a.length
=> 3
>> a.send(:length)
=> 3
>> a.send("length")
=> 3
上記Railsコンソールの例では、「send
メソッドにシンボル:length
や文字列"length"
を渡した結果は、length
メソッドを直接実行した結果と同じになる」という点が重要です。
# rails console --sandbox
>> user = User.first
>> user.send(:activation_digest)
=> "$2a$10$Q.bVywQrrgEJC6Mg0IhdXONY5M/0jQYm4/ZEBwhJfxcag7VBBx6S6"
>> user.send("activation_digest")
=> "$2a$10$Q.bVywQrrgEJC6Mg0IhdXONY5M/0jQYm4/ZEBwhJfxcag7VBBx6S6"
>> attribute = :activation
=> :activation
>> user.send("#{attribute}_digest")
=> "$2a$10$Q.bVywQrrgEJC6Mg0IhdXONY5M/0jQYm4/ZEBwhJfxcag7VBBx6S6"
続いては、「send
メソッドを用いて、RDB上に存在する最初のユーザーのactivation_digest
属性にアクセスする」という例です。activation_digest
メソッドも、length
メソッドと同様、「メソッド自身の名前を、send
の引数にシンボルや文字列として渡すことにより実行する」ことが可能です。
特に重要なのは、最後の「式展開により、呼び出すメソッドの名前の一部を変数としている」という実行例です。
「式展開により、呼び出すメソッドの名前の一部を変数とする」その動作の解説
まず、:activation
というシンボル3を値とする変数attribute
を定義します。
attribute = :activation
その上で、文字列の展開により引数を組み立てて…
"#{attribute}_digest"
send
メソッドに渡します。
send("#{attribute}_digest")
余談 - send
に類似するメソッドとか概念とか
そういえば、Excelのワークシート関数であるINDIRECT
も、Rubyのsend
と似たような使い方をしますよね。
send
に類似するメソッドとして、eval
というメソッドも存在します。send
が「メソッド名」を渡して用いるのに対し、eval
は「Rubyコードそのもの」を渡して用いるメソッドです。
実際にUser#authenticated?
の実装を変更する
ここまでのポイントを踏まえた上で、実際にUser#authenticated?
の実装内容に手を加えていきましょう。
def authenticated?(attribute, token)
digest = send("#{attribute}_token")
return false if digest.nil?
BCrypt::Password.new(digest).is_password?(token)
end
class User < ApplicationRecord
...略
- def authenticated?(remember_token)
+ def authenticated?(attribute, token)
+ digest = send("#{attribute}_digest")
- return false if remember_digest.nil?
+ return false if digest.nil?
- BCrypt::Password.new(remember_digest).is_password?(remember_token)
+ BCrypt::Password.new(digest).is_password?(token)
end
...略
end
既存のauthenticated?
メソッド使用箇所の実装を変更する
現状ではテストが成功しない
現状では、アプリケーション全体に対するテストは成功しません。
# rails test
Running via Spring preloader in process 13778
Started with run options --seed 51138
ERROR["test_current_user_returns_nil_when_remember_digest_is_wrong", SessionsHelperTest, 3.314027799991891]
test_current_user_returns_nil_when_remember_digest_is_wrong#SessionsHelperTest (3.31s)
ArgumentError: ArgumentError: wrong number of arguments (given 1, expected 2)
app/models/user.rb:31:in `authenticated?'
app/helpers/sessions_helper.rb:26:in `current_user'
test/helpers/sessions_helper_test.rb:17:in `block in <class:SessionsHelperTest>'
ERROR["test_current_user_returns_right_user_when_session_is_nil", SessionsHelperTest, 3.3577567999600433]
test_current_user_returns_right_user_when_session_is_nil#SessionsHelperTest (3.36s)
ArgumentError: ArgumentError: wrong number of arguments (given 1, expected 2)
app/models/user.rb:31:in `authenticated?'
app/helpers/sessions_helper.rb:26:in `current_user'
test/helpers/sessions_helper_test.rb:11:in `block in <class:SessionsHelperTest>'
ERROR["test_authenticated?_should_return_false_for_a_new_user_with_nil_digest", UserTest, 5.586914699990302]
test_authenticated?_should_return_false_for_a_new_user_with_nil_digest#UserTest (5.59s)
ArgumentError: ArgumentError: wrong number of arguments (given 1, expected 2)
app/models/user.rb:31:in `authenticated?'
test/models/user_test.rb:75:in `block in <class:UserTest>'
44/44: [=================================] 100% Time: 00:00:06, Time: 00:00:06
Finished in 6.20436s
44 tests, 185 assertions, 0 failures, 3 errors, 0 skips
「ArgumentError: wrong number of arguments (given 1, expected 2)」というメッセージが見受けられますね。これは、「authenticated?
メソッドに渡す引数の数が不足している」という趣旨のメッセージです。
まずは、3つのエラー共通で報告されているapp/models/user.rb:31
を修正していきましょう。
修正箇所1 - SessionsHelper#current_user
メソッド
まずはapp/helpers/sessions_helper.rb
の31行目を修正していきましょう。私の環境では、SessionsHelper#current_user
に問題の行があります。
def current_user
if (user_id = session[:user_id])
@current_user ||= User.find_by(id: user_id)
elsif (user_id = cookies.signed[:user_id])
user = User.find_by(id: user_id)
- if user && user.authenticated?(cookies[:remember_token])
+ if user && user.authenticated?(:remember , cookies[:remember_token])
log_in user
@current_user = user
end
end
end
まだテストは成功しない
# rails test
Running via Spring preloader in process 13791
Started with run options --seed 46675
ERROR["test_authenticated?_should_return_false_for_a_new_user_with_nil_digest", UserTest, 3.5224237999645993]
test_authenticated?_should_return_false_for_a_new_user_with_nil_digest#UserTest (3.53s)
ArgumentError: ArgumentError: wrong number of arguments (given 1, expected 2)
app/models/user.rb:31:in `authenticated?'
test/models/user_test.rb:75:in `block in <class:UserTest>'
FAIL["test_current_user_returns_right_user_when_session_is_nil", SessionsHelperTest, 4.250324499967974]
test_current_user_returns_right_user_when_session_is_nil#SessionsHelperTest (4.25s)
--- expected
+++ actual
@@ -1 +1 @@
-#<User id: 959740715, name: "Reimu Hakurei", email: "rhakurei@example.com", created_at: "2019-12-06 09:01:01", updated_at: "2019-12-06 09:01:05", password_digest: "$2a$04$HVRWhbBA8amg3yoSSEKV9.vf6jg/T01/QCKfSI.w6ML...", remember_digest: "$2a$04$W7PA7NZAg5QbFQDgfqZQo.bmSUdr6OBKJ01JZKkI0gj...", admin: true, activation_digest: nil, activated: true, activated_at: nil>
+nil
test/helpers/sessions_helper_test.rb:11:in `block in <class:SessionsHelperTest>'
44/44: [=================================] 100% Time: 00:00:04, Time: 00:00:04
Finished in 4.53206s
44 tests, 187 assertions, 1 failures, 1 errors, 0 skips
「ArgumentError: wrong number of arguments (given 1, expected 2)」というメッセージがまだ見受けられます。今度はtest/models/user_test.rb:75
とあります。修正していきましょう。
修正箇所2 - UserTest
の「authenticated? should return false for a new user with nil digest」テスト
test "authenticated? should return false for a new user with nil digest" do
- assert_not @user.authenticated?('')
+ assert_not @user.authenticated?(:remember, '')
end
まだ何かがおかしい
これで改めてテストを実行してみます。しかし、まだ成功しません。
# rails test
Running via Spring preloader in process 13807
Started with run options --seed 22372
FAIL["test_current_user_returns_right_user_when_session_is_nil", SessionsHelperTest, 2.9952282999875024]
test_current_user_returns_right_user_when_session_is_nil#SessionsHelperTest (3.00s)
--- expected
+++ actual
@@ -1 +1 @@
-#<User id: 959740715, name: "Reimu Hakurei", email: "rhakurei@example.com", created_at: "2019-12-06 09:11:02", updated_at: "2019-12-06 09:11:05", password_digest: "$2a$04$lRWR5q0KpF3UiW2r.hyD4e19FtV4Na6gh7iwkvdiely...", remember_digest: "$2a$04$26R0Y3nwF/Wh68N94ShIze7j6dLAT03Hva5oDt4PhP5...", admin: true, activation_digest: nil, activated: true, activated_at: nil>
+nil
test/helpers/sessions_helper_test.rb:11:in `block in <class:SessionsHelperTest>'
44/44: [=================================] 100% Time: 00:00:05, Time: 00:00:05
Finished in 5.43956s
44 tests, 188 assertions, 1 failures, 0 errors, 0 skips
Railsチュートリアル本文に言及されていない何かがおかしい、ということですね。
修正箇所3 - authenticated?
に関する想定外のテスト失敗と、その解決
長くなりましたので、別記事で解説します。
これでようやくテストが成功
# rails test
Running via Spring preloader in process 13904
Started with run options --seed 21760
44/44: [=================================] 100% Time: 00:00:06, Time: 00:00:06
Finished in 6.01140s
44 tests, 189 assertions, 0 failures, 0 errors, 0 skips
ようやくテストが成功しました。正直ここまで長かったです。
演習 - authenticated?
メソッドの抽象化
長くなりましたので、別記事で解説します。
edit
アクションで有効化
ユーザーが有効化対象であることの確認
以下のif
文が核となります。
if user && !user.activated? && user.authenticated?(:activation, params[:id])
!user.activated?
なる文の必要性
「既に有効化済みのユーザーに対し、再度有効化処理を実行することはしない」という意味の文です。このような文が必要となる理由は以下です。
- 「有効化が行われたら、その時点で当該ユーザーはログイン状態となる」という実装を行う
- ユーザー体験を考えると、ユーザーの有効化とログインはセットで行われるのが好ましいため
- 上記のような実装の場合、何も対策をしていないと、「有効化リンクが盗み出されたら、そのリンクを使って自由にログインできてしまう」という脆弱性が発生する
- 以上の脆弱性への対策として、「既に有効化済みのユーザーに対し、再度有効化処理を実行することはしない」という実装を行う
その他の部分
以下の事柄について確認を行っています。
-
user
が存在するユーザーであること - 与えられたトークンが正しい有効化トークンであること
今回定義するアカウント有効化リンクにおいては、「有効化トークンは、params[:id]
として参照できる」のでしたよね。
アカウント有効化と、付随する処理の実体
コードは以下のようになります。
user.update_attribute(:activated, true)
user.update_attribute(:activated_at, Time.zone.now)
log_in user
flash[:success] = "Account activated!"
redirect_to user
処理の流れは以下のようになります。
- 有効化されるユーザーの
:activated
属性の値にtrue
を格納する - 有効化されるユーザーの
:actiated_at
属性の値に現在時刻を格納する - 有効化されたユーザーとしてログイン処理を行う
- 正常に処理が行われた旨のフラッシュメッセージを定義する
- 有効化されたユーザーのプロフィールページにリダイレクトする
ポイントは以下です。
- RDBへの変更の反映は、
update_attribute
メソッドによって行っている- バリデーションをバイパスする必要があるため
- 有効化されたユーザーとしてログインする処理を行っている
- 有効化されたユーザーのプロフィールページにリダイレクトされている
有効化URLによりユーザーを有効化する
ここまでの実装が完了すれば、メールで送信されたURLを使ってユーザーを有効化することができるようになります。
Hi Foo Bar Baz,
Welcome to the Sample App! Click on the link below to activate your account:
https://localhost:3000/account_activations/8I_nvDPPSA3TIqvNT4n9qA/edit?email=foobar%2Bfoobar%40example.org
ポート番号の変更
私の環境では、Docker仮想環境側のTCP3000番ポートをローカル側のTCP8080番ポートと紐付けているため、localhost:3000
という記述をlocalhost:8080
に変更する必要があります。
スキームをhttps://
からhttp://
に変更する
また、スキームがhttps://
だと以下のエラーで有効化URLにアクセスできませんでした。
HTTP parse error, malformed request (): #<Puma::HttpParserError: Invalid HTTP format, parsing fails.>
スキームがhttp://
だとアクセスできます。
最終的な有効化URL
最終的には、ローカル側でアクセスする有効化URLは以下のようになります。
- https://localhost:3000/account_activations/8I_nvDPPSA3TIqvNT4n9qA/edit?email=foobar%2Bfoobar%40example.org
+ http://localhost:8080/account_activations/8I_nvDPPSA3TIqvNT4n9qA/edit?email=foobar%2Bfoobar%40example.org
有効化が成功する
有効化が成功すると、Webブラウザで以下のような画面が返ってきます。
上述ユーザーのIDが102であることを踏まえて、Railsコンソールでactivated?
とactivated_at
の状態を確認してみます。
# rails console --sandbox
>> user = User.find(102)
>> user.activated?
=> true
>> user.activated_at
=> Sun, 08 Dec 2019 05:20:45 UTC +00:00
正常に有効化処理が行われたようですね。
同じ有効化URLを再度使うことはできない
同じ有効化URLで再度アクセスを試みると、以下のような画面が返ってきます。
「Invalid activation link」のフラッシュメッセージと、/ へのリダイレクト。こちらも想定どおりの挙動ですね。
「ユーザーが有効化されていなければログインできない」という動作の実装
class SessionsController < ApplicationController
...略
def create
@user = User.find_by(email: params[:session][:email].downcase)
if @user && @user.authenticate(params[:session][:password])
- log_in @user
- params[:session][:remember_me] == '1' ? remember(@user) : forget(@user)
- redirect_back_or @user
+ if user.activated?
+ log_in @user
+ params[:session][:remember_me] == '1' ? remember(@user) : forget(@user)
+ redirect_back_or @user
+ else
+ message = "Account not activated."
+ message += "Check your email for activation link."
+ flash[:warning] = message
+ redirect_to root_url
+ end
else
...略
end
end
...略
end
ログインしようとしたユーザーが有効化されていない場合は、「送付されたメールの確認を促すフラッシュメッセージを表示するようにした上で、/ にリダイレクト」という処理が行われます。
演習 - edit
アクションで有効化
1. コンソールから、11.2.4で生成したメールに含まれているURLを調べてみてください。URL内のどこに有効化トークンが含まれているでしょうか?
https://localhost:3000/account_activations/8I_nvDPPSA3TIqvNT4n9qA/edit?email=foobar%2Bfoobar%40example.org
上記URLを前提とすると、 /edit の前にある以下の文字列が有効化トークンとなります。
8I_nvDPPSA3TIqvNT4n9qA
Railsアプリケーション側では、params[:id]
として参照できます。
2. 先ほど見つけたURLをブラウザに貼り付けて、そのユーザーの認証に成功し、有効化できることを確認してみましょう。また、有効化ステータスがtrueになっていることをコンソールから確認してみてください。
上述「有効化URLによりユーザーを有効化する」の通りです。
- ポート番号が違うという問題に対する解決
- Docker環境において、
https://
スキームで有効化URLを扱えるようにする方法
以上の課題は今後に持ち越しでしょうか。
有効化のテストとリファクタリング
有効化のテスト
テストコードの全体像
test/integration/users_signup_test.rb
の全体像は以下のようになります。
require 'test_helper'
class UsersSignupTest < ActionDispatch::IntegrationTest
def setup
ActionMailer::Base.deliveries.clear
end
test "invalid signup information" do
get signup_path
assert_select 'form[action="/signup"]'
assert_no_difference 'User.count' do
post signup_path, params: { user: { name: "",
email: "user@invalid",
password: "foo",
password_confirmation: "bar" } }
end
assert_template 'users/new'
assert_select 'div#error_explanation'
assert_select 'div.field_with_errors'
end
test "valid signup information with account activation" do
get signup_path
assert_difference 'User.count', 1 do
post users_path, params: { user: { name: "Example User",
email: "user@example.com",
password: "password",
password_confirmation: "password"} }
end
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", email: user.email)
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_not flash.empty?
assert is_logged_in?
end
end
配信されたメッセージがきっかり1通であることを確認する
以下のコードにより、「メイラーによって配信されたメッセージが1通であること」を確認できます。
assert_equal 1, ActionMailer::Base.deliveries.size
但し、ActionMailer::Base.delivaries
は変数であり、メイラーを使う他のテストで上書きされる可能性があることに注意しなければなりません。このテストが期待通りに行われるためには、setup
メソッドでdelivaries
を初期化しておく必要があります。
def setup
ActionMailer::Base.deriveries.clear
end
テストが成功することの確認
コードにtypo等がなければ、この時点でテストは成功するはずです。
# rails test
Running via Spring preloader in process 14065
Started with run options --seed 9265
44/44: [=================================] 100% Time: 00:00:10, Time: 00:00:10
Finished in 10.72402s
44 tests, 195 assertions, 0 failures, 0 errors, 0 skips
私がやってしまったtypo
私は多数のtypoをやってしまったため、テストが成功するまでに数回エラーを出してしまいました。私がやってしまったtypoの例は以下です。
- if user.activated?
+ if @user.activated?
- deriveries
+ deliveries
- delivaries
+ deliveries
- user = assigns(:user)
+ user = assigns(@user)
- assert is logged_in?
+ assert is_logged_in?
皆さんもtypoには気をつけましょう。
リファクタリング - Userモデルにユーザー有効化メソッドを追加する
ここまでのテストが実装できれば、「ユーザー操作の一部をコントローラーからモデルに移動する」というリファクタリングが可能になります。具体的には、以下の処理をコントローラーからモデルに移していくことになります。
- ユーザーの有効化属性の更新
- 有効化メールの送信
ユーザーの有効化属性の更新
ユーザーの有効化属性を更新するactivate
メソッドの実装は、以下のようになります。
def activate
update_attribute(:activated, true)
update_attribute(:activated_at, Time.zone.now)
end
「update_attribute
の呼び出しに際し、user.
を頭につけなくなった」というのは大きなポイントです。user.
がなくなるに至る経緯の説明は以下です。
- Userモデルに
user
という変数は存在しない- この点AccountActivationsコントローラーとは異なる
- AccountActivationsコントローラーにおける
user.
に相当するのは、Userモデルにおいてはself.
である - セッターメソッド4でない場合、レシーバーとして用いる
self.
は省略できる-
update_attribute
はセッターメソッドではない
-
有効化メールの送信
有効化メールを送信するsend_activation_email
メソッドの実装は、以下のようになります。
def send_activation_email
UserMailer.account_activation(self).deliver_now
end
こちらも「@user
がself
に変更されている」というのがポイントです。実装箇所がUsersコントローラーからUserモデルに変わったことにより、このような変更が必要となりました。
「実装箇所が変更されたことに伴う変数名の変更」について、Railsチュートリアル本文には、以下のような記述があります。
どんなに簡単なリファクタリングであっても、この手の変更はつい忘れてしまうものです。テストをきちんと書いておけば、この種の見落としを検出できます。
Userモデル全体の変更
app/models/user.rb
に、activate
メソッドとsend_activation_email
メソッドを追加していきます。
class User < ApplicationRecord
...略
+
+ # アカウントを有効にする
+ 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
...略
end
Usersコントローラーのcreate
アクションにおける、有効化メール送信処理の実装変更
有効化メールを送信する処理が行われるのは、Usersコントローラーのcreate
アクション内です。Userモデルにsend_activation_email
メソッドを実装したので、Usersコントローラーのcreate
アクションでも当該メソッドを呼び出すように実装を変更していきます。
def create
@user = User.new(user_params)
if @user.save
- UserMailer.account_activation(@user).deliver_now
+ @user.send_activation_email
flash[:info] = "Please check your email to activate your account."
redirect_to root_url
else
render 'new'
end
end
AccoutActivationsコントローラーのedit
アクションにおける、ユーザーの有効化処理の実装変更
ユーザーの有効化処理が行われるのは、AccountActivationsコントローラーののedit
アクション内です。Userモデルにactivate
メソッドを実装したので、AccoutActivationsコントローラーのedit
アクションでも当該メソッドを呼び出すように実装を変更していきます。
def edit
user = User.find_by(email: params[:email])
if user && !user.activated? && user.authenticated?(:activation, params[:id])
- user.update_attribute(:activated, true)
- user.update_attribute(:activated_at, Time.zone.now)
+ user.activate
log_in user
flash[:success] = "Account activated!"
redirect_to user
else
flash[:danger] = "Invalid activation link"
redirect_to root_url
end
end
テストが成功することを確認する
テストの成功を確認するところまでがリファクタリングです。実装の変更が完了したならば、すぐにテストに取り掛かりましょう。
# rails test
Running via Spring preloader in process 14117
Started with run options --seed 49116
44/44: [=================================] 100% Time: 00:00:09, Time: 00:00:09
Finished in 9.66480s
44 tests, 195 assertions, 0 failures, 0 errors, 0 skips
テストは無事成功しました。
演習 - 有効化のテストとリファクタリング
1.1. リスト 11.39に記したテンプレートを使って、update_attribute
の呼び出しを1回のupdate_columns
呼び出しにまとめてみましょう (これでデータベースへの問い合わせが1回で済むようになります)。
リスト 11.35にある
activate
メソッドはupdate_attributeを2回呼び出していますが、これは各行で1回ずつデータベースへ問い合わせしていることになります。
def activate
- update_attribute(:activated, true)
- update_attribute(:activated_at, Time.zone.now)
+ update_columns(activated: true, activated_at: Time.zone.now)
end
update_attribute
と同様、update_columns
においてもバリデーションは行われません。この点はsave
やupdate_attributes
とは異なります。
ActiveRecordにおけるattributeの更新方法に関しては、@tyamagu2さんによる以下の記事が参考になります(ただし2016年時点の記事であることに留意)。
1.2. また、変更後にテストを実行し、green
になることも確認してください。
# rails test
Running via Spring preloader in process 14130
Started with run options --seed 41078
44/44: [=================================] 100% Time: 00:00:07, Time: 00:00:07
Finished in 7.69770s
44 tests, 195 assertions, 0 failures, 0 errors, 0 skips
無事テストは成功しました。
2. リスト 11.40のテンプレートを使って、後述引用の動作を変更してみましょう。なお、ここで使っているActive Recordのwhere
メソッドについては、13.3.3でもう少し詳しく説明します。
現在は、/usersのユーザーindexページを開くとすべてのユーザーが表示され、/users/:idのようにIDを指定すると個別のユーザーを表示できます。しかし考えてみれば、有効でないユーザーは表示する意味がありません。
3. ここまでの演習課題で変更したコードをテストするために、/users と /users/:id の両方に対する統合テストを作成してみましょう。
長くなりましたので、別記事で解説します。
-
RegionalRubyKaigi レポート (13) 札幌 Ruby 会議 02の、項目「知らないと損をする Smalltalk」あたりが、「Smalltalkの影響」についての概説的な言及となっています。 ↩
-
定義されていないメソッドの呼び出しに対する動作を定義するメソッドです。 ↩
-
attribute
の値として、文字列'activation'
を与えても、最終的な動作は変わりません。但し、「実行効率・使用メモリ量における優位性」等の理由により、Rubyにおける当該用法ではシンボルを使うことが一般的です。 ↩ -
現在開発中のサンプルアプリケーションで用いているセッターメソッドの例としては、
User#remember_token
やUser#activation_token
があります。 ↩