10.3 すべてのユーザーを表示する
index
アクションを追加して、すべてのユーザーを一覧表示する。データベースにサンプルデータを追加する方法や、将来ユーザー数が膨大になってもindexページを問題なく表示できるようにするためのユーザー出力のページネーション(pagination=ページ分割)の方法を学ぶ。
10.3.1 ユーザーの一覧ページ
ユーザーのindexページはログインしたユーザーにしか見せないようにし、未登録のユーザーがデフォルトで表示できるページを制限する。
indexアクションが正しくリダイレクトするか検証するテストを書く。
require 'test_helper'
class UsersControllerTest < ActionDispatch::IntegrationTest
def setup
@user = users(:michael)
@other_user = users(:archer)
end
test "should get new" do
get signup_path
assert_response :success
end
test "should redirect index when not logged in" do
# ログインしていない場合はインデックスをリダイレクトするテストを行う
get users_path
assert_redirected_to login_url
end
.
.
.
beforeフィルターのlogged_in_userにindexアクションを追加して、このアクションを保護。
class UsersController < ApplicationController
before_action :logged_in_user, only: [:index, :edit, :update]
# 「indexアクションが呼び出されたらlogged_in_userアクションを実行する」を追加
before_action :correct_user, only: [:edit, :update]
def index
end
def show
@user = User.find(params[:id])
end
.
.
.
end
indexビューの実装。User.allを使ってデータベース上の全ユーザーを取得し、ビューで使えるインスタンス変数@usersに代入する。
class UsersController < ApplicationController
before_action :logged_in_user, only: [:index, :edit, :update]
.
.
.
def index
@users = User.all
end
.
.
.
end
<% provide(:title, 'All users') %>
<h1>All users</h1>
<ul class="users">
<% @users.each do |user| %>
<li>
<%= gravatar_for user, size: 50 %>
<%= link_to user.name, user %>
</li>
<% end %>
</ul>
users_helper.rb
, custom.scss
, _header.html.erb
にコードを追加してrails.test
演習 1
レイアウトにあるすべてのリンクに対して統合テストを書いてみましょう。ログイン済みユーザーとそうでないユーザーのそれぞれに対して、正しい振る舞いを考えてください。ヒント: log_in_asヘルパーを使ってリスト 5.32にテストを追加してみましょう。
require 'test_helper'
class SiteLayoutTest < ActionDispatch::IntegrationTest
test "layout links" do
get root_path
assert_template 'static_pages/home'
assert_select "a[href=?]", root_path, count: 2
assert_select "a[href=?]", help_path
assert_select "a[href=?]", about_path
assert_select "a[href=?]", contact_path
assert_select "a[href=?]", login_path
get contact_path
assert_select "title", full_title("Contact")
get signup_path
assert_select "title", full_title("Sign up")
end
def setup
@user = users(:michael)
end
test "layout links when loged in" do
log_in_as(@user)
# => テストユーザーでログイン
get root_path
assert_template 'static_pages/home'
assert_select "a[href=?]", users_path
# => /users ユーザー一覧ページ
assert_select "a[href=?]", user_path(@user)
# => /users/:id Profileページ
assert_select "a[href=?]", edit_user_path(@user)
# => /users/1/:id Settingsページ
assert_select "a[href=?]", logout_path
end
end
10.3.2 サンプルのユーザー
indexページに複数のユーザーを表示させる。
演習 1
試しに他人の編集ページにアクセスしてみて、10.2.2で実装したようにリダイレクトされるかどうかを確かめてみましょう。
確認のみなので省略。
10.3.3 ページネーション
1つのページに一度に30人だけユーザーを表示するというような**ページネーション(pagination)**を実装する。
演習 1
Railsコンソールを開き、pageオプションにnilをセットして実行すると、1ページ目のユーザーが取得できることを確認してみましょう。
>> User.paginate(page: nil)
(0.1ms) begin transaction
User Load (2.9ms) SELECT "users".* FROM "users" LIMIT ? OFFSET ? [["LIMIT", 11], ["OFFSET", 0]]
(1.7ms) SELECT COUNT(*) FROM "users"
=> #<ActiveRecord::Relation [#<User id: 1, name: "Example User", email: "example@railstutorial.org", created_at: "2021-02-26 14:35:28", updated_at: "2021-02-26 14:35:28", password_digest: [FILTERED], remember_digest: nil>, #<User id: 2, name: "William Bartell", email: "example-1@railstutorial.org", created_at: "2021-02-26 14:35:30", updated_at: "2021-02-26 14:35:30", password_digest: [FILTERED], remember_digest: nil>, #<User id: 3, name: "Carter Feest", email: "example-2@railstutorial.org", created_at: "2021-02-26 14:35:30", updated_at: "2021-02-26 14:35:30", password_digest: [FILTERED], remember_digest: nil>, #<User id: 4, name: "Arlinda Douglas Sr.", email: "example-3@railstutorial.org", created_at: "2021-02-26 14:35:30", updated_at: "2021-02-26 14:35:30", password_digest: [FILTERED], remember_digest: nil>, #<User id: 5, name: "Dr. Monika Goyette", email: "example-4@railstutorial.org", created_at: "2021-02-26 14:35:31", updated_at: "2021-02-26 14:35:31", password_digest: [FILTERED], remember_digest: nil>, #<User id: 6, name: "Mr. Hilda Prohaska", email: "example-5@railstutorial.org", created_at: "2021-02-26 14:35:31", updated_at: "2021-02-26 14:35:31", password_digest: [FILTERED], remember_digest: nil>, #<User id: 7, name: "Theo Russel", email: "example-6@railstutorial.org", created_at: "2021-02-26 14:35:31", updated_at: "2021-02-26 14:35:31", password_digest: [FILTERED], remember_digest: nil>, #<User id: 8, name: "Robbie Littel", email: "example-7@railstutorial.org", created_at: "2021-02-26 14:35:31", updated_at: "2021-02-26 14:35:31", password_digest: [FILTERED], remember_digest: nil>, #<User id: 9, name: "Azzie Walter", email: "example-8@railstutorial.org", created_at: "2021-02-26 14:35:32", updated_at: "2021-02-26 14:35:32", password_digest: [FILTERED], remember_digest: nil>, #<User id: 10, name: "Shanell Nicolas", email: "example-9@railstutorial.org", created_at: "2021-02-26 14:35:32", updated_at: "2021-02-26 14:35:32", password_digest: [FILTERED], remember_digest: nil>, ...]>
演習 2
先ほどの演習課題で取得したpaginationオブジェクトは、何クラスでしょうか? また、User.allのクラスとどこが違うでしょうか? 比較してみてください。
class: User::ActiveRecord_Relation
User.allと同じ。
10.3.4 ユーザー一覧のテスト
ページネーションに対する簡単なテストも書いておく。
ログイン→indexページにアクセス→最初のページにユーザーがいることを確認→ページネーションのリンクがあることを確認の手順でテストしていく。
演習 1
試しにリスト 10.45にあるページネーションのリンク(will_paginateの部分)を2つともコメントアウトしてみて、リスト 10.48のテストが red に変わるかどうか確かめてみましょう。
FAIL["test_index_including_pagination", #<Minitest::Reporters::Suite:0x000056137403b7a8 @name="UsersIndexTest">, 14.158692726000027]
test_index_including_pagination#UsersIndexTest (14.16s)
Expected at least 1 element matching "div.pagination", found 0..
Expected 0 to be >= 1.
test/integration/users_index_test.rb:13:in `block in <class:UsersIndexTest>'
red
演習 2
先ほどは2つともコメントアウトしましたが、1つだけコメントアウトした場合、テストが green のままであることを確認してみましょう。will_paginateのリンクが2つとも存在していることをテストしたい場合は、どのようなテストを追加すれば良いでしょうか? ヒント: 表 5.2を参考にして、数をカウントするテストを追加してみましょう。
require 'test_helper'
class UsersIndexTest < ActionDispatch::IntegrationTest
def setup
@user = users(:michael)
end
test "index including pagination" do
log_in_as(@user)
get users_path
assert_template 'users/index'
assert_select 'div.pagination', count: 2 # 「count: 2」を追記
User.paginate(page: 1).each do |user|
assert_select 'a[href=?]', user_path(user), text: user.name
end
end
end
FAIL["test_index_including_pagination", #<Minitest::Reporters::Suite:0x0000561374244360 @name="UsersIndexTest">, 16.980905499999608]
test_index_including_pagination#UsersIndexTest (16.98s)
Expected exactly 2 elements matching "div.pagination", found 1..
Expected: 2
Actual: 1
test/integration/users_index_test.rb:13:in `block in <class:UsersIndexTest>'
10.3.5 パーシャルのリファクタリング
一覧ページのリファクタリングを行う。
演習 1
リスト 10.52にあるrenderの行をコメントアウトし、テストの結果が red に変わることを確認してみましょう。
REDになりました。
10.4 ユーザーを削除する
削除を実行できる権限を持つ管理(admin)ユーザーのクラスを作成する。
次にユーザーを削除するためのリンクを追加し、削除を行うのに必要なdestroyアクションも実装する。
10.4.1 管理ユーザー
Boolean型 とは?
真理値の「真 = true」と「偽 = false」という2値をとるデータ型のこと。
toggle!メソッド とは?
toggle!(:flag)と書き、属性(:flag)を反転し保存するメソッド。saveに成功したらtrueを返す。
演習 1
Web経由でadmin属性を変更できないことを確認してみましょう。具体的には、リスト 10.56に示したように、PATCHを直接ユーザーのURL(/users/:id)に送信するテストを作成してみてください。テストが正しい振る舞いをしているかどうか確信を得るために、まずはadminをuser_paramsメソッド内の許可されたパラメータ一覧に追加するところから始めてみましょう。最初のテストの結果は red になるはずです。最後の行では、更新済みのユーザー情報をデータベースから読み込めることを確認します( 6.1.5)。
test "should not allow the admin attribute to be edited via the web" do
log_in_as(@other_user)
# テストユーザー(@other_user)でログイン
assert_not @other_user.admin?
# @other_userは管理者か?=> 管理者ではない => false
# @other_user.admin?がfalse => 成功
patch user_path(@other_user), params: { user: { password: "password", password_confirmation: "password", admin: true } }
# @other_userに管理者権限を持たせる => admin: true
assert_not @other_user.reload.admin?
# 更新した@other_userは管理者か? => 管理者ではない => false
# @other_user.reload.admin?がfalse => 成功
end
assert_not とは?
assert_not hogeの場合 => hoge が trueなら失敗、falseなら成功。
10.4.2 destroyアクション
destroyアクションへのリンクを追加する。
ユーザーindexページの各ユーザーに削除用のリンクを追加し、続いて管理ユーザーへのアクセスを制限する。
演習 1
管理者ユーザーとしてログインし、試しにサンプルユーザを2〜3人削除してみましょう。ユーザーを削除すると、Railsサーバーのログにはどのような情報が表示されるでしょうか?
Started DELETE "/users/4" for 10.0.2.2 at 2021-02-28 19:28:49 +0900
Cannot render console from 10.0.2.2! Allowed networks: 127.0.0.0/127.255.255.255, ::1
Processing by UsersController#destroy as HTML
Parameters: {"authenticity_token"=>"ZUaUvmxMfqGHERKzsE1flF1BHJkxUkLesSKKx047CAkzYAOz6jB3wQrDRQ2xHloAba4P/r9CRI1XAATfArRZTw==", "id"=>"4"}
User Load (0.8ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
↳ app/helpers/sessions_helper.rb:18:in `current_user'
User Load (1.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 4], ["LIMIT", 1]]
↳ app/controllers/users_controller.rb:52:in `destroy'
(0.1ms) begin transaction
↳ app/controllers/users_controller.rb:52:in `destroy'
User Destroy (7.1ms) DELETE FROM "users" WHERE "users"."id" = ? [["id", 4]]
↳ app/controllers/users_controller.rb:52:in `destroy'
(13.6ms) commit transaction
↳ app/controllers/users_controller.rb:52:in `destroy'
Redirected to http://localhost:3000/users
Completed 302 Found in 46ms (ActiveRecord: 22.7ms | Allocations: 4317)
10.4.3 ユーザー削除のテスト
演習 1
試しにリスト 10.59にある管理者ユーザーのbeforeフィルターをコメントアウトしてみて、テストの結果が red に変わることを確認してみましょう。
確認のみなので省略。
さいごに
Webアプリケーションとしての基礎を整えられたようです。
Railsチュートリアルをはじめたばかりの頃はエラーが出ただけで焦っていました。
ですが今では冷静にエラーを読む、わからなければ検索するというのが自然とできるようになりました。
コードもだんだんと読み下せるようになってきました。一歩ずつ前に進んでいる気がします。次章もがんばります!