Rails チュートリアル 第10章 備忘録

第10章の備忘録です。

##目次
[1 新しいタブでリンクを開く方法](#1-新しいタブでリンクを開く方法)
[2 PATCHリクエストの送り方](#2-patchリクエストの送り方)
[3 テストreloadする必要性](#3-テストreloadする必要性)
[4 パスワードの例外処理](#4-パスワードの例外処理)
[5 認証と認可の違い](#5-認証と認可の違い)
[6 フレンドリーフォワーディング](#6-フレンドリーフォワーディング)
[7 Faker gem](#7-faker-gem)
[8 ページネーション](#8-ページネーション)
[9 確認ログの出し方](#9-確認ログの出し方)


##1 新しいタブでリンクを開く方法
クリック先のページを新しいタブで開きたい時は、target属性を下のコードのように追加する。
また`target="_blank"`だけだとフィッシングサイトなどを導入される恐れがあるので、`rel="noopener"`属性を下のコードのように追加する必要がある。

```app/views/users/edit.html.erb
    .
    .
    <div class="gravatar_edit">
      <%= gravatar_for @user %>
      <a href="https://gravatar.com/emails" target="_blank" rel="noopener">change</a>
    </div>
  </div>
</div>
```


##2 PATCHリクエストの送り方

`new`の時と同様に`edit`でフォームヘルパーを使ってコードを書くと、HTMLソースで見てみると`new`の時と違う箇所がある。

```html
<form accept-charset="UTF-8" action="/users/1" class="edit_user"
      id="edit_user_1" method="post">
  <input name="_method" type="hidden" value="patch" />
  .
  .
  .
</form>
```

まず、Webブラウザそのままでは`PATCH`リクエストを送信することができないため、`type="hidden"`でフォームを隠し、そこで`PATCH`を設定している。

また、`new`の時と`form_with(@user)`が全く同じにも関わらず、`edit`時では`PATCH`リクエストを送信するように自動で設定される理由は、Railsはユーザーが新規なのか、データベースに存在する既存のユーザーであるかを、`new_record?`論理値メソッドで区別してくれるからである。

新規の時は`true`既存の時は`false`を内部で判定し、そこで`POST`か`PATCH`を自動で判定している。


##3 テストreloadする必要性
この章のテストコードで、新しく`reload`メソッドが出てきた。
使用箇所は、下のコードなどが挙げられる。
このメソッドを差し込むことで、ページを更新した後に、`assert_equal``assert_select`メソッドを使って、ページの更新前後で変更されているかなどのチェックができる。

```test/integration/users_edit_test.rb
require 'test_helper'
  .
  .
  test "successful edit" do
    get edit_user_path(@user)
    assert_template 'users/edit'
    name  = "Foo Bar"
    email = "foo@bar.com"
    patch user_path(@user), params: { user: { name:  name,
                                              email: email,
                                              password:              "",
                                              password_confirmation: "" } }
    assert_not flash.empty?
    assert_redirected_to @user
    @user.reload                      # ページを更新
    assert_equal name,  @user.name
    assert_equal email, @user.email
  end
end
```


##4 パスワードの例外処理
名前や画像だけを編集したい時、現状だとパスワードは変更せずとも入力しないと、バリデーションでエラーが起きる。
新規登録時は空のパスワードをバリデーションし、編集時はパスワードを入力せず正常に動作させるためには、`allow_nil: true`オプションを使用する。

```app/models/user.rb
class User < ApplicationRecord
  .
  .
  has_secure_password
  validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
  .
end
```
こうすることで、passwordが`nil`になることを許容することができる。

また、has_secure_passwordではオブジェクト生成時だけ存在性を検証するようになっているため、新規作成時は空を許さず、編集時は再度パスワードを入力する必要性をなくすことが可能となる。

##5 認証と認可の違い

- 認証
  サイトのユーザーを識別すること。
- 認可
  そのユーザーが実行可能な操作を管理すること。
  今回では、`current_user`のみ編集等ができるように管理

##6 フレンドリーフォワーディング
仮に、ログインしていないユーザーが編集ページにアクセスしようとすると、ログインページへ飛ばされる。ログイン後に変種ページでリダイレクトしてあげたら親切である。
このようなユーザーが閲覧しようとしていたページにしてあげることをフレンドリーフォワーディングという。

実装する時は、開こうとしていたページをsessionを用いて管理する。

```app/helpers/sessions_helper.rb
module SessionsHelper
  .
  .
  # 記憶したURL（もしくはデフォルト値）にリダイレクト
  def redirect_back_or(default)
    redirect_to(session[:forwarding_url] || default)
    session.delete(:forwarding_url)
  end

  # アクセスしようとしたURLを覚えておく
  def store_location
    session[:forwarding_url] = request.original_url if request.get?
  end
end
```


##7 Faker gem
サンプルユーザーを作成したい時は`Faker`gemを使うと簡単に作成することができる。

通常は開発環境のみだが、今回は全ての環境で使用する。

```:Gemfile
.
gem 'rails', '6.0.3'
gem 'bcrypt', '3.1.13'
gem 'faker', '2.1.2'
.
```

```
$ bundle install
```

```db/seeds.rb
# メインのサンプルユーザーを1人
User.create!(name:  "Example User",
             email: "example@railstutorial.org",
             password:              "foobar",
             password_confirmation: "foobar")

# 追加のユーザー
99.times do |n|
  name  = Faker::Name.name
  email = "example-#{n+1}@railstutorial.org"
  password = "password"
  User.create!(name:  name,
               email: email,
               password:              password,
               password_confirmation: password)
end
```

また、`create!`メソッドにすることで、失敗した時にfalseではなく、例外を発生してくれるためエラーを見過ごさない

```
$ rails db:migrate:reset
$ rails db:seed
```

##8 ページネーション
ページネーションを使用したい時は、
`will_paginate`gem
`bootstrap-will_paginate`gem を使う。

```:Gemfile
.
gem 'faker', '2.1.2'
gem 'will_paginate', '3.1.8'
gem 'bootstrap-will_paginate', '1.0.0'
.
.
```

```
$ bundle install
```

表示したい位置に`<%= will_paginate %>`を記述

```app/views/users/index.html.erb
<% provide(:title, 'All users') %>
<h1>All users</h1>

<%= will_paginate %>

<ul class="users">
  <% @users.each do |user| %>
    <li>
      <%= gravatar_for user, size: 50 %>
      <%= link_to user.name, user %>
    </li>
  <% end %>
</ul>

<%= will_paginate %>
```

最後に`index`アクション時の`@user = User.all`を変更して、ページネーションを追加する。
また、`:page`パラメーターには初期値は1が入り、最初のページを返す。（デフォルトでは1pageに30）

```app/controllers/users_controller.rb
 .
  def index
    @users = User.paginate(page: params[:page])
  end
  .
  .
```




##9 確認ログの出し方
何かデータを削除する時、ボタンを1度押しただけで実行されてしまう状態だと、押し間違いで削除してしまう恐れがある。
そのために、削除ボタンを押したら確認ログのようなものを出すことで防ぐことができる。
その設定はすごく簡単で、`data: { confirm: "表示したい文字列" }`を加えるだけで可能！！

```
<%= link_to "delete", user, method: :delete,
            data: { confirm: "You suer?" }
```