Railsチュートリアル 第5章 - レイアウトを作成する…ユーザー登録: 最初のステップ

# 概要

ユーザ登録ページへのルーティングを作成します。その前提条件として、2つ目のコントローラを作成します。この先実装していくユーザ登録機能の第一歩です。

# Usersコントローラ

## Usersコントローラの生成

StaticPagesコントローラに次ぐ2つ目のコントローラとして、Usersコントローラを生成します。まず現時点では、新規ユーザー用のユーザー登録ページ（スタブ）のみを実装した、最も簡単な形でコントローラの生成・実装を行っていきます。

コントローラの作成は`rails generate controller`コマンドで行います。コントローラ作成のために与える引数は、以下の通りとします。

* コントローラ名…`Users`
* 最初に定義するアクション名…`new`

```
# rails generate controller Users new
Running via Spring preloader in process 1812
      create  app/controllers/users_controller.rb
       route  get 'users/new'
      invoke  erb
      create    app/views/users
      create    app/views/users/new.html.erb
      invoke  test_unit
      create    test/controllers/users_controller_test.rb
      invoke  helper
      create    app/helpers/users_helper.rb
      invoke    test_unit
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/users.coffee
      invoke    scss
      create      app/assets/stylesheets/users.scss
```

`rails generage controller Users new`により、以下のリソースが生成されました。

* Usersコントローラ
* `users/new`への`GET`リクエストに対するルーティング情報
* `users`ビュー
* Usersコントローラに対するテスト
* `users_helper`ヘルパー
* `users`に対するCoffeeScript形式のスクリプト
* `users`に対するSCSS形式のスタイルシート

## 現時点におけるUsersコントローラとその周辺リソース

生成されたばかりのUsersコントローラのソースコードは、以下のようになっています。

```ruby:app/controllers/users_controller.rb
class UsersController < ApplicationController
  def new
  end
end
```

`app/controllers/users_controller.rb`のコードから、以下のことがわかります。

* `UsersController`クラスが定義されている
    * UsersController`クラスは、`ApplicationController`クラスを継承している
* `UsersController`クラスでは、`new`メソッドが定義されている

続いて、生成されたばかりの`app/views/users/new.html.erb`のソースコードは以下の通りです。

```erb:app/views/users/new.html.erb
<h1>Users#new</h1>
<p>Find me in app/views/users/new.html.erb</p>
```

さらに、生成されたばかりの`test/controllers/users_controller_test.rb`のソースコードは以下の通りです。

```ruby:test/controllers/users_controller_test.rb
require 'test_helper'

class UsersControllerTest < ActionDispatch::IntegrationTest
  test "should get new" do
    get users_new_url
    assert_response :success
  end

end
```

この時点で、テストは問題なく通ります。

```
# rails test
...略

  8/8: [=============================================================================================================================================================] 100% Time: 00:00:06, Time: 00:00:06

Finished in 6.55124s
8 tests, 18 assertions, 0 failures, 0 errors, 0 skips
```

# 演習 - Usersコントローラ

## 1. [表 5.1](https://railstutorial.jp/chapters/filling_in_the_layout?version=5.1#table-url_mapping)を参考にしながら[リスト 5.41](https://railstutorial.jp/chapters/filling_in_the_layout?version=5.1#code-user_new_test)を変更し、`users_new_url`ではなく`signup_path`を使えるようにしてみてください。

```diff:
  require 'test_helper'

  class UsersControllerTest < ActionDispatch::IntegrationTest
    test "should get new" do
-     get users_new_url
+     get signup_path
      assert_response :success
    end

  end
```

## 2. 先ほどの変更を加えたことにより、テストが`red`になったことを確認してください。なお、この演習はテスト駆動開発 ([コラム 3.3](https://railstutorial.jp/chapters/static_pages?version=5.1#aside-when_to_test)) で説明した`red`/`green`のリズムを作ることを目的としています。このテストは次の[5.4.2](https://railstutorial.jp/chapters/filling_in_the_layout?version=5.1#sec-signup_url)で`green`になるよう修正します。 

```
# rails test
...略

ERROR["test_should_get_new", UsersControllerTest, 1.619302900013281]
 test_should_get_new#UsersControllerTest (1.62s)
NameError:         NameError: undefined local variable or method `signup_path' for #<UsersControllerTest:0x0000558df747d378>
            test/controllers/users_controller_test.rb:5:in `block in <class:UsersControllerTest>'

  8/8: [=============================================================================================================================================================] 100% Time: 00:00:01, Time: 00:00:01

Finished in 1.71337s
8 tests, 17 assertions, 0 failures, 1 errors, 0 skips
```

無事（？）テスト結果で不適合が指摘されました。「`signup_path`というメソッドが定義されていない」という不適合ですね。Railsにおけるエンドポイントの扱いにおいて、`_path`や`_url`で終わるメソッドは、名前付きルートを定義したときに自動で定義されるのでしたね。必要になるのは、/signupというエンドポイントです。

# ユーザー登録用URL

## ユーザー登録ページのエンドポイントの変更

表 5.1でも言及されていたように、新規ユーザー登録ページのURLは、/users/newではなくて/signupにしたいです。ルーティングの変更が必要になります。

ルーティングの変更なので、ここで変更するファイルは、`config/routes.rb`です。では早速、先ほどの演習でも登場した/signupというエンドポイントを早速定義してみましょう。

```diff:config/routes.rb
  Rails.application.routes.draw do
-   get 'users/new'

    root 'static_pages#home'
    get '/help', to: 'static_pages#help'
    get '/about', to: 'static_pages#about'
    get '/contact', to: 'static_pages#contact'
+   get '/signup', to: 'users#new'
  end
```

改めて`rails test`を実行してみます。

```
# rails test
...略

  8/8: [=============================================================================================================================================================] 100% Time: 00:00:01, Time: 00:00:01

Finished in 1.55368s
8 tests, 18 assertions, 0 failures, 0 errors, 0 skips
```

先ほどの演習の内容と合わせて、これでテストが通るようになったのが確認できます。

## Homeページのボタンに、ユーザー登録ページへのリンクを追加

先ほど行ったルーティングの変更で、/signupに対し、`signup_path`や`signup_url`というメソッドを呼び出せるようになりました。早速Homeページに/signupへのリンクを追加しましょう。リダイレクトではないので、`signup_path`のほうを使います。

```diff:app/views/static_pages/home.html.erb
  <div class="center jumbotron">
    <h1>Welcome to the Sample App</h1>

    <h2>
      This is the home page for the
      <a href="https://railstutorial.jp/">Ruby on Rails Tutorial</a>
      sample application.
    </h2>

-   <%= link_to "Sign up now!", '#', class: "btn btn-lg btn-primary" %>
+   <%= link_to "Sign up now!", signup_path, class: "btn btn-lg btn-primary" %>
  </div>

  <%= link_to image_tag("rails.png", alt: "Rails logo"),
              'http://rubyonrails.org/' %>
```

最後に、signupページ用のビューのスタブの先頭に、`app/views/layouts/application.html.erb`を参照するための設定を追加します。ページタイトルは'Sign up'とします。

```diff:app/views/users/new.html.erb
+ <% provide(:title, 'Sign up') %>
  <h1>Users#new</h1>
  <p>Find me in app/views/users/new.html.erb</p>
```

この部分は、`app/views/layouts/application.html.erb`の以下の部分に展開されるのでしたね。

```erb:`app/views/layouts/application.html.erb（抜粋）`
<%= yield %>
```

現時点で、/signupをブラウザで表示した結果は以下のようになります。

<img width="1164" alt="スクリーンショット 2019-09-28 10.26.32.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/323778/4d0b4a59-6935-5ee8-b8e8-9c9b1479cd08.png">

# 演習 - ユーザー登録用URL

## 1.1. もしまだ[5.4.1.1](https://railstutorial.jp/chapters/filling_in_the_layout?version=5.1#sec-exercises_users_controller)の演習に取り掛かっていなければ、まずは[リスト 5.41](https://railstutorial.jp/chapters/filling_in_the_layout?version=5.1#code-user_new_test)のように変更し、名前付きルートsignup_pathを使えるようにしてください。

この部分は、先ほどの演習で変更した部分となります。

## 1.2. また、[リスト 5.43](https://railstutorial.jp/chapters/filling_in_the_layout?version=5.1#code-signup_route)で名前付きルートが使えるようになったので、現時点でテストが`green`になっていることを確認してください。

```
# rails test
...略

  8/8: [=============================================================================================================================================================] 100% Time: 00:00:01, Time: 00:00:01

Finished in 1.70987s
8 tests, 18 assertions, 0 failures, 0 errors, 0 skips
```

きちんとテストは通りますね。

## 2.1. 先ほどのテストが正しく動いていることを確認するため、signupルートの部分をコメントアウトし、テスト redになることを確認してください。

`config/routes.rb`を、以下のように変更します。

```diff:config/routes.rb
  Rails.application.routes.draw do
    get 'users/new'

    root 'static_pages#home'
    get '/help', to: 'static_pages#help'
    get '/about', to: 'static_pages#about'
    get '/contact', to: 'static_pages#contact'
-   get '/signup', to: 'users#new'
+ # get '/signup', to: 'users#new'
  end
```

```
# rails test
...略

ERROR["test_should_get_home", StaticPagesControllerTest, 0.4042917999904603]
 test_should_get_home#StaticPagesControllerTest (0.41s)
ActionView::Template::Error:         ActionView::Template::Error: undefined local variable or method `signup_path' ...略

ERROR["test_should_get_root", StaticPagesControllerTest, 0.5494068999832962]
 test_should_get_root#StaticPagesControllerTest (0.55s)
ActionView::Template::Error:         ActionView::Template::Error: undefined local variable or method `signup_path' ...略

ERROR["test_layout_links", SiteLayoutTest, 2.0050760000012815]
 test_layout_links#SiteLayoutTest (2.01s)
ActionView::Template::Error:         ActionView::Template::Error: undefined local variable or method `signup_path' ...略

ERROR["test_should_get_new", UsersControllerTest, 2.0233185999968555]
 test_should_get_new#UsersControllerTest (2.02s)
NameError:         NameError: undefined local variable or method `signup_path' ...略

  8/8: [=============================================================================================================================================================] 100% Time: 00:00:02, Time: 00:00:02

Finished in 2.07825s
8 tests, 8 assertions, 0 failures, 4 errors, 0 skips
```

テストが通らなくなりました。`signup_path`というメソッドが定義されていない、というエラーですね。/signupに対する名前付きルートが書かれていないとこうなります。

実際、現時点で`signup_path`メソッドは定義されていません。以下は`rails console`での確認結果です。

```
>> app.signup_path
...略
NoMethodError (undefined method `signup_path' ...略)
```

## 2.2. 確認できたら、コメントアウトを解除して`green`の状態に戻してください。

```diff:config/routes.rb
  Rails.application.routes.draw do
    get 'users/new'

    root 'static_pages#home'
    get '/help', to: 'static_pages#help'
    get '/about', to: 'static_pages#about'
    get '/contact', to: 'static_pages#contact'
- # get '/signup', to: 'users#new'
+   get '/signup', to: 'users#new'
  end
```

```
# rails test
...略

  8/8: [=============================================================================================================================================================] 100% Time: 00:00:02, Time: 00:00:02

Finished in 2.18233s
8 tests, 18 assertions, 0 failures, 0 errors, 0 skips
```

今度はきちんとテストが通りますね。

```
>> app.signup_path
=> "/signup"
```

`rails console`でも、`signup_path`メソッドが存在することが確認できました。

## 3.1. [リスト 5.32](https://railstutorial.jp/chapters/filling_in_the_layout?version=5.1#code-layout_links_test)の統合テストにsignupページにアクセスするコードを追加してください (getメソッドを使います)。

```diff: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
      get contact_path
      assert_select "title", full_title("Contact")
+     get signup_path
+     assert_select "title", full_title("Sign up")
    end
  end

end
```

## 3.2.コードを追加したら実際にテストを実行し、結果が正しいことを確認してください。

```
# rails test:integration
...略

  1/1: [=============================================================================================================================================================] 100% Time: 00:00:01, Time: 00:00:01

Finished in 1.62317s
1 tests, 7 assertions, 0 failures, 0 errors, 0 skips
```

テストは問題なく通りました。

## 3.余談 signupページの`Sign up`ではなく`Sigh up`にしたらテスト結果が`red`になることを確認します。

```diff:app/views/users/new.html.erb
+ <% provide(:title, 'Sign up') %>
- <% provide(:title, 'Sigh up') %>
  <h1>Users#new</h1>
  <p>Find me in app/views/users/new.html.erb</p>
```

このときのテスト結果は以下の通りになります。

```
# rails test:integration
...略

 FAIL["test_layout_links", SiteLayoutTest, 1.7456585000036284]
 test_layout_links#SiteLayoutTest (1.75s)
        <Sign up | Ruby on Rails Tutorial Sample App> expected but was
        <Sigh up | Ruby on Rails Tutorial Sample App>..
        Expected 0 to be >= 1.
        test/integration/site_layout_test.rb:15:in `block in <class:SiteLayoutTest>'

  1/1: [=============================================================================================================================================================] 100% Time: 00:00:01, Time: 00:00:01

Finished in 1.75320s
1 tests, 7 assertions, 1 failures, 0 errors, 0 skips
```

無事（？）不適合が指摘されました。

もちろん、`app/views/users/new.html.erb`の内容を元に戻しておくのも忘れずに。

```diff:app/views/users/new.html.erb
- <% provide(:title, 'Sigh up') %>
+ <% provide(:title, 'Sign up') %>
  <h1>Users#new</h1>
  <p>Find me in app/views/users/new.html.erb</p>
```

# この先の方針

これにて、Railsチュートリアル第5章は一通り完走となりました。今後は、以下の機能を追加していくこととなります。

* ユーザー登録
* サインイン
* サインアウト
* マイクロポスト機能
* 他のユーザーのフォロー機能