Ruby on Rails チュートリアル第7章をやってみて

#ユーザー登録
■第7章
Userモデルができあがったので、ユーザー登録機能を追加していく。
この第7章からは難易度が少しずつ上がっていくっぽい。

##7.1 ユーザーを表示する
ユーザーの名前とプロフィール写真を表示するためのページを作成していく。

###7.1.1 デバッグとRails環境
`debug`メソッドと`params`変数を使って、各プロフィールページにデバッグ用の情報が表示されるようにする。

`application.html.erb`に追記。

```rb
<%= debug(params) if Rails.env.development? %>
```

if以降のコードは「開発環境？」という意味。

デバッグ出力をきれいに整形するために、cssを弄る。

```rb
@import "bootstrap-sprockets";
@import "bootstrap";

/* mixins, variables, etc. */

$gray-medium-light: #eaeaea;

@mixin box_sizing {
  -moz-box-sizing:    border-box;
  -webkit-box-sizing: border-box;
  box-sizing:         border-box;
}
.
.
.
/* miscellaneous */

.debug_dump {
  clear: both;
  float: left;
  width: 100%;
  margin-top: 45px;
  @include box_sizing;
}
```

Sassのミックスイン機能 (ここでは`box_sizing`) を使っている。ミックスイン機能を使うことで、CSSルールのグループをパッケージ化して複数の要素に適用できる。

###7.1.2 Usersリソース
6章での操作で、データベースに1人のユーザーがいる。データの作成、表示、更新、削除をリソース (Resources) として扱う。

/users/1 のURLを有効にするために、routesファイル (`config/routes.rb`)に次の1行を追加します。


```rb
resources :users
```

サンプルアプリケーションにこの１行を追加すると、ユーザーのURLを生成するための多数の名前付きルートと共に、RESTfulなUsersリソースで必要となるすべてのアクションが利用できるようになる。便利すぎ。

show用のファイルを手動で作成し、ユーザー表示ビューが正常に動作するためには、Usersコントローラ内の`show`アクションに対応する`@user`変数を定義する。

```rb
User.find(params[:id])
```

このコードでid=1のユーザーを検索できる。

###7.1.3 debuggerメソッド
`byebug` gemによる`debugger`メソッドでもっと直接的にデバッグできる。

今後Railsアプリケーションの中でよく分からない挙動があったら、上のように`debugger`を差し込んで調べてみる。

###7.1.4 Gravatar画像とサイドバー
今度は各ユーザーのプロフィール写真のあたりをもう少し肉付けし、サイドバーも作り始める。

`gravatar_for`ヘルパーメソッドを使ってGravatarの画像を利用できるようにする。`show.html.erb`を書き換え。

```rb
<% provide(:title, @user.name) %>
<h1>
  <%= gravatar_for @user %>
  <%= @user.name %>
</h1>
```

`gravatar_for`ヘルパーメソッドを定義する。

```rb
module UsersHelper

  # 引数で与えられたユーザーのGravatar画像を返す
  def gravatar_for(user)
    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
```

Gravatarの画像タグに`gravatar`クラスとユーザー名のaltテキストを追加したものを返す。

ユーザーの`show`ビューにサイドバーを追加する。また`show.html.erb`を書き換え。

```rb
<% provide(:title, @user.name) %>
<div class="row">
  <aside class="col-md-4">
    <section class="user_info">
      <h1>
        <%= gravatar_for @user %>
        <%= @user.name %>
      </h1>
    </section>
  </aside>
</div>
```

HTML要素とCSSクラスを配置したおかげで、プロフィールページにスタイルを与えられるように。

![スクリーンショット 2022-02-14 0.56.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2210462/90fb3f1c-65d9-4ab9-4ac9-ccd9ef4d6b2c.png)


##7.2ユーザー登録フォーム
今度はユーザー登録フォームを作成。

###7.2.1 form_forを使用する
フォームを作るために、`form_for`ヘルパーメソッドを使う。

`@user`変数の定義。

```ruby:users_controller.rb
class UsersController < ApplicationController

  def show
    @user = User.find(params[:id])
  end

  def new
    @user = User.new
  end
end
```

新規ユーザーのためのユーザー登録フォーム。

```ruby:new.html.erb
<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>

<div class="row">
  <div class="col-md-6 col-md-offset-3">
    <%= form_for(@user) do |f| %>
      <%= f.label :name %>
      <%= f.text_field :name %>

      <%= f.label :email %>
      <%= f.email_field :email %>

      <%= f.label :password %>
      <%= f.password_field :password %>

      <%= f.label :password_confirmation, "Confirmation" %>
      <%= f.password_field :password_confirmation %>

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

CSSもいじったらこんな感じに。
![スクリーンショット 2022-02-14 1.13.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2210462/6bc719fc-a6a8-b3c1-a228-9d8429a77848.png)

###7.2.2 フォームHTML
フォームを理解していく。

```rb
<%= form_for(@user) do |f| %>
  .
  .
  .
<% end %>
```

変数fは “form” のf。このfオブジェクトは、HTMLフォーム要素に対応するメソッドが呼び出されると、`@user`の属性を設定するために特別に設計されたHTMLを返す。

ユーザーの作成で重要なのは`input`ごとにある特殊な`name`属性。`params`変数経由で取りに行くため。

次に重要な要素は、`form`タグ自身。`action`と`method`が重要。

##7.3ユーザー登録失敗
入力が失敗したときにエラーを表示するようにする。

###7.3.1 正しいフォーム
ユーザー登録フォームを動かす。

以下のコードを追加。

```ruby:users_controller.rb
def create
    @user = User.new(params[:user])    # 実装は終わっていないことに注意!
    if @user.save
      # 保存の成功をここで扱う。
    else
      render 'new'
    end
  end
```

実装の出発点は完了。

###7.3.2 Strong Parameters
実装していく。Strong Parametersを使う。これで、必須のパラメータと許可されたパラメータを指定することができる。

```rb
@user = User.new(params[:user])
```

これだとセキュリティ上かなり危険らしいので、次のようにする。

```ruby:users_controller.rb
class UsersController < ApplicationController
  .
  .
  .
  def create
    @user = User.new(user_params)
    if @user.save
      # 保存の成功をここで扱う。
    else
      render 'new'
    end
  end

  private

    def user_params
      params.require(:user).permit(:name, :email, :password,
                                   :password_confirmation)
    end
end
```

`private`キーワードを使って外部から使えないようにする。

やっぱり難易度が上がったこともあって、結構何言ってるかわかんないとこがチラホラ出てきました。

###7.3.3 エラーメッセージ
エラーメッセージを追加していく。ブラウザで表示するために、ユーザーの`new`ページでエラーメッセージのパーシャル (partial) を出力する。

```ruby:qiita.rb
<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>

<div class="row">
  <div class="col-md-6 col-md-offset-3">
    <%= form_for(@user) do |f| %>
      <%= render 'shared/error_messages' %>

      <%= f.label :name %>
      <%= f.text_field :name, class: 'form-control' %>

      <%= f.label :email %>
      <%= f.email_field :email, class: 'form-control' %>

      <%= f.label :password %>
      <%= f.password_field :password, class: 'form-control' %>

      <%= f.label :password_confirmation, "Confirmation" %>
      <%= f.password_field :password_confirmation, class: 'form-control' %>

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

`shared/error_messages`というパーシャルをrenderする。
複数のビューで使われるパーシャルは専用のディレクトリ`shared`によく置かれる。ただ、ディレクトリが存在しないので作る必要がある。

```rb
$mkdir app/views/shared
```


パーシャルを作っておく。


```ruby:_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 %>
```

`count`メソッドはいくつあるか数え、`any?`メソッドは`empty`?メソッドと逆で存在していればtrue。

###7.3.4 失敗時のテスト
Railsではフォーム用のテストを書くことができる。無効な送信をしたときの正しい振る舞いについてテストを書いていく。

```ruby:users_signup_test.rb
require 'test_helper'

class UsersSignupTest < ActionDispatch::IntegrationTest

  test "invalid signup information" do
    get signup_path
    assert_no_difference 'User.count' do
      post users_path, params: { user: { name:  "",
                                         email: "user@invalid",
                                         password:              "foo",
                                         password_confirmation: "bar" } }
    end
    assert_template 'users/new'
  end
end
```

このコードと等価になるらしい。

```rb
before_count = User.count
post users_path, ...
after_count  = User.count
assert_equal before_count, after_count
```

ブロックの実行前後で引数が変わらないことをテストしている。また`get`メソッドを使っておらず、ユーザー登録ページにアクセスしなくても、直接`post`メソッドを呼び出してユーザー登録ができることを意味している。

##7.4 ユーザー登録成功
新規ユーザーを実際にデータベースに保存できるようにし、ユーザー登録フォームを完成させていく。

###7.4.1 登録フォームの完成
ーザー登録に成功した場合はページを描画せずに別のページにリダイレクト するようにしてみる。

```ruby:users_controller.rb
def create
    @user = User.new(user_params)
    if @user.save
      redirect_to @user
    else
      render 'new'
    end
  end
```

`redirect_to`メソッドに着目する。次のコードと等価。

```rb
redirect_to user_url(@user)
```

###7.4.2 flash
登録完了後に表示されるページにメッセージを表示し、2度目以降にはそのページにメッセージを表示しないようにする。Railsではこういった情報の表示のためにflashという変数を用いる。

```ruby:users_controller.rb
def create
    @user = User.new(user_params)
    if @user.save
      flash[:success] = "Welcome to the Sample App!"
      redirect_to @user
    else
      render 'new'
    end
  end
```

`flash`変数に代入したメッセージは、リダイレクトした直後のページで表示できるようになる。

```ruby:application.html.erb
<!DOCTYPE html>
<html>
  .
  .
  .
  <body>
    <%= render 'layouts/header' %>
    <div class="container">
      <% flash.each do |message_type, message| %>
        <div class="alert alert-<%= message_type %>"><%= message %></div>
      <% end %>
      <%= yield %>
      <%= render 'layouts/footer' %>
      <%= debug(params) if Rails.env.development? %>
    </div>
    .
    .
    .
  </body>
</html>
```

これで`flash`変数の内容をWebサイトのレイアウトに追加できる。

###7.4.3実際のユーザー登録
ここまでの変更の確認のため、データベース初期化。

```rb
$ rails db:migrate:reset
```

そしてサーバーを起動して、ユーザー登録してみる。無事成功。フラッシュメッセージも表示されました。
![スクリーンショット 2022-02-14 14.01.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2210462/fddeea44-0515-da19-b49f-9555d5a93bc0.png)

###7.4.4 成功時のテスト
有効な送信に対するテストを書いてみる。これでアプリケーションの振る舞いを検証し、もし今後バグが埋め込まれたらそれを検知できるようになる。

```ruby:users_signup_test.rb
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
```

##7.5 プロのデプロイ
初めてデータを操作できるようにするデプロイを行う。

###7.5.1 本番環境でのSSL 
SSLとはローカルのサーバーからネットワークに流れる前に、大事な情報を暗号化する技術。

本番環境ではSSLを使うように修正する。以下のコードを追記。


```ruby:production.rb
config.force_ssl = true
```

###7.5.2 本番環境用のWebサーバー
Herokuのデフォルトサーバーの、Rubyだけで実装されたWEBrickでなくPumaに置き換える。

###7.5.3 本番環境へのデプロイ
いつものプッシュしてからマイグレーション。
SSLの設定をしたので鍵付きに。

##感想
さらに難しくなった感じがしました。なんとなくサーバー上のやりとりについて書いてるんだろうなーくらいしかわかんないままでしたが、それでもエラーなく進めることができました。