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

第7章の備忘録です。

##環境
Rails 6.0.3
Ruby 2.6.3

##目次
[1 開発環境でデバッグ情報表示](#1-開発環境でデバッグ情報表示)
[2 リソース](#2-リソース)
[3 debuggerメソッド](#3-debuggerメソッド)
[4 Gravatar画像設定](#4-gravatar画像設定)
[5 form_withヘルパー](#5-form_withヘルパー)
[6 ストロングパラメーター](#6-ストロングパラメーター)
[7 パーシャルでエラーメッセージ表示](#7-パーシャルでエラーメッセージ表示)
[8 ユーザー登録成功時のテスト](#8-ユーザー登録成功時のテスト)
[9 SSLとPumaの設定込みのデプロイ方法](#9-sslとpumaの設定込みのデプロイ方法)


###1 開発環境でデバッグ情報表示

```app/views/layouts/application.html.erb
.
.
      <%= yield %>
      <%= render 'layouts/footer' %>
      <%= debug(params) if Rails.env.development? %>
    </div>
  </body>
</html>
```
このように記述することで、開発環境のみでページ下にデバッグが可能になる。


###2 リソース

```config/routes.rb
.
.
  resources :users
end
```
この1行を追加すると、URLを生成するための多数の名前付きルートと共に、RESTfulなリソースで必要な全てのアクションが設定される。

| HTTPリクエスト |URL | アクション | 名前付きルート |
|:-----------------|:------------------|:------------------|:------------------|
| GET | /users | index | users_path |
| GET | /users/1 | show | user_path(user) |
| GET | /users/new | new | new_user_path |
| POST | /users | create | users_path |
| GET | /users/1/edit | edit | edit_user_path(user) |
| PATCH | /users/1 | update | user_path(user) |
| DELETE | /users/1 |  destroy| user_path(user) |

`index`と`create`の名前付きルートが同じだが、`new`アクションの後に**POSTリクエスト**が送られたら`user_path`では、`create`アクションが呼ばれるようになっている。

また、値をデータベースに追加・更新する際はそれぞれnew/createアクションと、edit/updateアクションがあるが、どちらも前者の`GET`リクエストで、ページ表示、後者の`POST`/`PATCH`リクエストで、追加・更新を行い、成功したら`show.html.erb`にリダイレクトされる。

つまり、`DELETE`含め、`GET`メソッド以外のアクション.html.erbファイルが作られることはない。（RESTfulではないということになる）

そもそも`GET`が**ページを取得する**と、第3章で勉強済みのため、よくよく考えたら当然のことなんだろうけど。。💦


###3 debuggerメソッド
`byebug`gemを追加することで、下のように`debugger`メソッドが使用できるようになる。

```app/controllers/users_controller.rb
.
.
  def show
    @user = User.find(params[:id])
    debugger
  end
.
.
```

こうすることで、実際にアクションが実行された時、コンソールからデバッグしたい変数等を入力すると中身を確認することができる！


####4 Gravatar画像設定

Gravatarはプロフィール画像を自動で設定してくれる。（正確には違う）
特に最初のユーザー登録時に、画像設定されていないことによるトラブルを回避してくれるから便利！
GravatarのURLは、メールアドレスを**MD5**という仕組みでハッシュ化しているので、下のように設定することで使用することが可能になる

```app/helpers/users_helper.rb
module UsersHelper
  # 引数で与えられたユーザーのGravatar画像を返す
  # 後の演習での、キーワード引数（size: 80）込み
  def gravatar_for(user, size: 80)
    gravatar_id = Digest::MD5::hexdigest(user.email.downcase)
    gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}"
    image_tag(gravatar_url, alt: user.name, class: "gravatar")
  end
end
```

```app/views/users/show.html.erb
.
.
  <%= gravatar_for @user %>
.
```


###5 form_withヘルパー
確かRails5あたりで、form_for / form_tag といった別のヘルパーが使われていたが、現在はform_withが推奨らしい。
今回はこの3つの違いではなく、form_withヘルパー内で生成される中身をHTMLソースと見比べて学んだことについてまとめる。

```app/views/users/new.html.erb
.
.
<%= form_with(model: @user, local: true) do |f| %>
      <%= f.label :name %>
      <%= f.text_field :name %>
      .
      .
      <%= f.label :password_confirmation, "Confirmation" %>
      <%= f.password_field :password_confirmation %>

      <%= f.submit "Create my account", class: "btn btn-primary" %>
    <% end %>
.
```

下がヘルパーで生成されたHTMLソース

```
<form accept-charset="UTF-8" action="/users" class="new_user"
      id="new_user" method="post">
  <input name="authenticity_token" type="hidden"
         value="NNb6+J/j46LcrgYUC60wQ2titMuJQ5lLqyAbnbAUkdo=" />
  <label for="user_name">Name</label>
  <input id="user_name" name="user[name]" type="text" />

  .
  .

  <label for="user_password_confirmation">Confirmation</label>
  <input id="user_password_confirmation"
         name="user[password_confirmation]" type="password" />

  <input class="btn btn-primary" name="commit" type="submit"
         value="Create my account" />
</form>
```

ここで着目したいのが、
**formタグ**では、**action**と**method**属性

**action**で設定されている**/users**は、formが`https://~~~.com/users`へ飛ぶということ。
（ [2 リソース](#2-リソース) で、`create`アクションに対応するURLが`/users`であったことからも理解できる)
**method**の**post**は、文字通り`POST`リクエストを送信するということ



そして**inputタグ**では、**name**と**type**属性

**name**で設定された値がparamsで渡される。
**type**で値の種類を設定しておくと、例えばpasswordの入力時に、実際の値が見えないようになるなど、最適化してくれる。



###6 ストロングパラメーター
ここまでの実装方法だと、paramsで渡ってくる予定以外の値も、渡ってきた場追加してしまう。例えば管理者かどうかの`admin`属性を簡単にいじることが出来てしまうらしい。。💦

それを防ぐために、下のようにprivateメソッドで、paramsから渡ってくる値の種類に制限をかける。

```app/controllers/users_controller.rb
  .
  .
  .
  def create
    @user = User.new(user_params)
    if @user.save
      .
      .
    end
  end

  private

    def user_params
      params.require(:user).permit(:name, :email, :password,
                                   :password_confirmation)
    end
end
```
こうすることで先ほど例にあげた、admin属性が不正に含まれていた場合でも、適切に弾かれてエラーが発生する！


###7 パーシャルでエラーメッセージ表示
ユーザー登録時、エラー内容がフォーム付近に表示されたらユーザーにとって優しい！！
ということで、下のようなパーシャルを作っておき、フォームに加える(renderメソッドを使う)

```app/views/shared/_error_messages.html.erb
<% if @user.errors.any? %>
  <div id="error_explanation">
    <div class="alert alert-danger">
      The form contains <%= pluralize(@user.errors.count, "error") %>.
    </div>
    <ul>
    <% @user.errors.full_messages.each do |msg| %>
      <li><%= msg %></li>
    <% end %>
    </ul>
  </div>
<% end %>
```
`any?`メソッドは、`empty?`メソッドの逆の意味を持ち、エラーが含まれていると`true`を返す。

また`pluralize`メソッドは、第一引数の値によって、第二引数に設定した文字を単数/複数形に自動に変化させて表示してくれる便利機能！


###8 ユーザー登録成功時のテスト
今回一番勉強になったテストコードを掲載

```test/integration/users_signup_test.rb
require 'test_helper'

class UsersSignupTest < ActionDispatch::IntegrationTest
  .
  .
  .
  test "valid signup information" 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
    follow_redirect!
    assert_template 'users/show'
  end
end
```
流れは、
1. GETリクエストで、signup_pathに当たるビューを表示
2. 適切なユーザーが登録され、`User.count`が1増えることをチェック
3. `create`に成功したことで、適切に`/users/show`が表示されているかチェック

特に`assert_difference`と`follow_redirect!`に注目！

`assert_difference`メソッドは、`do~end`の処理後に、第一引数で設定した変数の数が、第二引数で設定した値分変化したかどうかをチェックすることが可能。
反対に、不適切なユーザーを登録せずに、`User.count`が増減していないことを確認するには、`assert_no_difference`メソッドを使う。

`follow_redirect!`メソッドは、**POSTリクエストを送信した結果を見て、指定されたリダイレクト先に移動する**メソッドであり、今回の場合、`create`アクションで`POST`リクエストを送信したので、ユーザーが登録成功した場合、`/users/show`が表示されるはずである。
その意味を含めたメソッドであり、次の`assert_template 'users/show'`で表示されているかを実際に確認している。



###9 SSLとPumaの設定込みのデプロイ方法
この章で実際にデータベースにCRUD操作が加わったことで、しっかりとしたセキュリティを考えなければならない。
そこで今回は、**情報を暗号化するSSL**と、デフォルトの`WEBrick`Webサーバーより**多数のリクエストを捌くことができるPuma**サーバーを組み込んでデプロイする。

まずは本番環境でSSLを使うよう、1行追加する

```config/environments/production.rb
Rails.application.configure do
  .
  .
  # Force all access to the app over SSL, use Strict-Transport-Security,
  # and use secure cookies.
  config.force_ssl = true
  .
end
```

Herokuのサブドメインの場合は、遠隔にあるサーバーのSSLセットアップも記述した設定のみで可能である。
ただし、独自ドメインの場合は、SSL証明書を購入する必要がある。。。

次にPumaの設定だが、まず、
Rails5以降、デフォルトで`Puma`gemが追加されている
次に設定ファイルを記述していくが、Pumaのドキュメントのコードそのままの引用で、理解していなくても大丈夫とのこと。

```config_puma.rb
# Pumaの設定ファイル
max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count }
threads min_threads_count, max_threads_count
port        ENV.fetch("PORT") { 3000 }
environment ENV.fetch("RAILS_ENV") { ENV['RACK_ENV'] || "production" }
pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" }
workers ENV.fetch("WEB_CONCURRENCY") { 2 }
preload_app!
plugin :tmp_restart
```

次に、`Procfile`にHeroku上でPumaのプロセスを走らせる設定ファイルを作成する。（Gemfileと同じ階層に作成）

```:./Procfile
web: bundle exec puma -C config/puma.rb
```

最後に、本番データベース（PostgreSQL）を明示的に設定する（明示的に設定することを推奨されているらしい・・）

```config/database.yml
.
.
test:
  <<: *default
  database: db/test.sqlite3

production:
  adapter: postgresql
  encoding: unicode
  # For details on connection pooling, see Rails configuration guide
  # https://railsguides.jp/configuring.html#データベース接続をプールする
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  database: sample_app_production
  username: sample_app
  password: <%= ENV['SAMPLE_APP_DATABASE_PASSWORD'] %>
```

以上で第7章終了。
だんだん1日でQiitaにまとめるところまでやり切るのが大変になってきた。
でも書くことにも慣れてきて、前半の記事より少しは役に立つ記事になってきただろうか💦
アウトプットすることで、学びを再確認できることは実感中。。