【Railsチュートリアル】第10章 ユーザーの更新・表示・削除②

# 10.3 すべてのユーザーを表示する
`index`アクションを追加して、すべてのユーザーを一覧表示する。データベースにサンプルデータを追加する方法や、将来ユーザー数が膨大になってもindexページを問題なく表示できるようにするためのユーザー出力のページネーション（pagination=ページ分割）の方法を学ぶ。

## 10.3.1 ユーザーの一覧ページ
ユーザーのindexページはログインしたユーザーにしか見せないようにし、未登録のユーザーがデフォルトで表示できるページを制限する。

indexアクションが正しくリダイレクトするか検証するテストを書く。

```ruby:test/controllers/users_controller_test.rb
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アクションを追加して、このアクションを保護。

```ruby:app/controllers/users_controller.rb
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に代入する。

```ruby:app/controllers/users_controller.rb
class UsersController < ApplicationController
  before_action :logged_in_user, only: [:index, :edit, :update]
  .
  .
  .
  def index
    @users = User.all
  end
  .
  .
  .
end
```

```ruby:app/views/users/index.html.erb
<% 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にテストを追加してみましょう。

```ruby:test/integration/site_layout_test.rb
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をセットして実行すると、１ページ目のユーザーが取得できることを確認してみましょう。

```ruby
>> 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の部分）を２つともコメントアウトしてみて、リスト 10.48のテストが red に変わるかどうか確かめてみましょう。

```ruby

 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
先ほどは２つともコメントアウトしましたが、１つだけコメントアウトした場合、テストが green のままであることを確認してみましょう。will_paginateのリンクが２つとも存在していることをテストしたい場合は、どのようなテストを追加すれば良いでしょうか? ヒント: 表 5.2を参考にして、数をカウントするテストを追加してみましょう。

```ruby:test/integration/users_index_test.rb
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
```

```ruby

 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）。

```ruby:test/controllers/users_controller_test.rb
  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
管理者ユーザーとしてログインし、試しにサンプルユーザを２〜３人削除してみましょう。ユーザーを削除すると、Railsサーバーのログにはどのような情報が表示されるでしょうか?

```ruby

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チュートリアルをはじめたばかりの頃はエラーが出ただけで焦っていました。
ですが今では冷静にエラーを読む、わからなければ検索するというのが自然とできるようになりました。
コードもだんだんと読み下せるようになってきました。一歩ずつ前に進んでいる気がします。次章もがんばります！