【Rails/ルーティング】自作したディレクトリ内のコントローラを参照する方法

# 概要

かんたんログイン(テストユーザーログイン)実装にあたり、普段しない形でコントローラファイルを作成しました。

その際に「え、これどうなってんの？」と感じたことがあったのでそれを記録します。

<br>

# 環境

* macOS Catalina 10.15.6

* ruby 2.6.5

* Rails 6.0.3.4

*  MySQL : 5.6.47

<br>

# 今回やりたいこと

* [URL] localhost:3000/admin/usersにアクセス

* app/controllers/admin/users_controller.rbのindexアクションへルーティング

* index.html.erbを表示

<br>

# まず、普通のusersコントローラで動作確認

ターミナルで以下のコマンドを入力します。

```terminal
% rails _6.0.0_ new controller-app -d mysql

% cd controller-app

% rails db:migrate

% rails g controller users
```

<br>

また、ルーティングは以下の通り設定します。

```app/config/routes.rb

Rails.application.routes.draw do
  resources :users, only: :index
end
```

<table>
  <tr>
    <th>HTTP Verb</th>
    <th>URI Pattern</th>
    <th>Controller#Action</th>
  </tr>
  <tr>
    <td>GET</td>
    <td>/users</td>
    <td>users#index</td>
  </tr>
</table>

<br>

そしてコントローラにindexアクションを追加します。

```app/controllers/users_controller.rb
class UsersController < ApplicationController

  def index
  end

end
```

<br>

最後に、ビューを追加します。

```erb:app/views/index.html.erb

<h1>users#index</h1>
```

<br>

これで準備完了。  
試しに'/users'にアクセスすると…

![demo](https://gyazo.com/b15badf0d7b2e3d5ecf02f0b667785e0/raw)

<br>

普通にビューが表示されました！

では、ここから手を加えていきます！

<br><br>

# コントローラをadminディレクトリに移動

## 1. adminディレクトリに移動すると？

* app/controllers/adminディレクトリを作成

* その中にusers_controller.rbを移植

* '/users' にアクセスしてみる

![demo](https://gyazo.com/8cf74a59a8efd23695da244a20310c47/raw)

``uninitialized constant UsersController``というルーティングエラーが出ています。  
これは「<b>定数やクラスが定義されていないエラー</b>」です。

<br>

現状を整理すると、

* GET '/users' をリクエスト

* users#index (usersコントローラ) を探す

* そんなのないよ！というエラー

<br>

ん？もう一度usersコントローラを見てみましょう。

```app/controllers/admin/users_controller.rb
class UsersController < ApplicationController

  def index
  end

end
```

<br>

class UsersControllerって書いてるじゃん！
と言いたいところですが、これはシンプルに記述のミスです。

どういうことかというと…

* 今のルーティングの定義だと <b>app/controllers ディレクトリ</b>直下のコントローラファイルを探す

* ルーティング参照先を <b>app/controllers/admin ディレクトリ直下</b>に変更する必要がある

さて、これをどう実現するか？
次で説明します。

<br>

## 2. namespaceをルーティングに設定

<a href="https://railsguides.jp/routing.html#%E3%82%B3%E3%83%B3%E3%83%88%E3%83%AD%E3%83%BC%E3%83%A9%E3%81%AE%E5%90%8D%E5%89%8D%E7%A9%BA%E9%96%93%E3%81%A8%E3%83%AB%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0:title%5D">こちらのRailsガイドの2.6</a>を参照しました。

app/controllers/adminディレクトリのコントローラを参照したい際は、次のようにルーティングを設定します。

```app/config/routes.rb
Rails.application.routes.draw do
  namespace :admin do
    resources :users, only: :index
  end
end
```

<table>
  <tr>
    <th>HTTP Verb</th>
    <th>URI Pattern</th>
    <th>Controller#Action</th>
  </tr>
  <tr>
    <td>GET</td>
    <td>/admin/users</td>
    <td>admin/users#index</td>
  </tr>
</table>

<br>

これでadminディレクトリのusersコントローラを参照できるようになりました。

では、実際に'admin/users'にアクセスしましょう！

![demo](https://gyazo.com/ca4e065cd5e343d535082e6f830c8881/raw)

だめですか…。
とりあえずエラー文を解読しましょう。

<br>

``expected file /Users/***/app/controllers/admin/users_controller.rb to define constant Admin::UsersController, but didn't``

* 定数Admin::UsersControllerを定義するため

* /Users/***/admin/users_controller.rb というファイルが必要

* でも定義せんかったわ

<br>

現状、``admin/users_controller.rb``は指定のパスに入っています。
なので<b>ここで出てきたAdmin::UsersControllerをクラス名に定義すれば良さそう</b>です。

<br>

というわけでコントローラを以下のように変更します。

```app/controllers/admin/users_controller.rb
class Admin::UsersController < ApplicationController

  def index
  end

end
```

<br>

これでどうでしょう？

<br>

![demo](https://gyazo.com/d286e396b37b58d9b96d6c2ac49b9090/raw)

<br>

まだダメでした。もうひと作業必要なようです。
もう少し頑張りましょう！

<br>

## 3. ビューファイルを移動

さて、先程のエラー文を解読します。

* ``Admin::UsersController#index is missing a template for request formats``

* Admin::UsersController#indexはリクエストフォーマットに対応するテンプレートがありません

<br>

リクエストフォーマットって何？と思いましたが、続く小さい文字にヒントがありました。

* ``Unless told otherwise, Rails expects an action to render a template with the same name,
contained in a folder named after its controller.``

* 特に明記されてなければ、Railsは同じ名前のテンプレートをレンダリングするアクションを想定しています

* コントローラにちなんで名付けられたフォルダに含まれています

<br>

つまり「<b>Admin::UsersControllerがレンダリングするビューがないよ</b>」ということです。
そしてそのファイルはコントローラ名に対応したディレクトリに配置する必要がありそうです。

少し具体的に考えると、

* UsersControllerのindexアクションは

* app/views/users/index.html.erbのビューをレンダリングする

<br>

よって、今回のケースでは、

* Admin::UsersControllerのindexアクションなので

* app/views/admin/users/index.html.erb

<br>

にビューファイルをセットすればいいと考えられます。

というわけで変更します！

```erb:app/views/admin/users/index.html.erb

<h1>admin/users/index.html.erb</h1>
```

<br>

さあ、どうでしょうか。

<br>

![demo](https://gyazo.com/aa9b06c252781eb9047a70f824edf02d/raw)

<br>

やりました！これで目的達成です！

<br>

# まとめ
<b>app/controllersにディレクトリを作ってコントローラファイルを配置するときの注意</b>

1. ルーティングにnamespaceを設定

2. クラス名をAdmin::UsersControllerのように変更 (ディレクトリ名を追加)

3. 同名ディレクトリ直下にビューファイルを設置

<br>

これで実現できます。

ちなみに、今回のケースではURLが変わってしまいました。
これはnamespaceの代わりにmoduleを設定すればURLはそのままになります。

```app/config/routes.rb

Rails.application.routes.draw do
  scope module: :admin do
    resources :users, only: :index
  end
end
```

| HTTP Verb | URI Pattern | Controller#Action |
|:-:|:-:|:-:|
| GET  | /users  | admin/users#index  |


<br>

勉強になりました！これからも地道にRailsのことを学習していきます！

### 参照

* <a href="https://railsguides.jp/routing.html#%E3%82%B3%E3%83%B3%E3%83%88%E3%83%AD%E3%83%BC%E3%83%A9%E3%81%AE%E5%90%8D%E5%89%8D%E7%A9%BA%E9%96%93%E3%81%A8%E3%83%AB%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0">Railsガイド/Railsのルーティング</a>