RailsのdeviseのController,View,URLのカスタマイズ（初心者向け）

#どんな記事？
ここではrailsでアプリを作る際に認証機能を簡単に作成するgemである'devise'を使う時の、コントローラーとビュー、URLをカスタマイズする方法とその考え方を解説する。
#deviseとは

```
$ rails g devise モデル名
```

とするだけでモデルを作成してくれて、内部的にビューやコントローラーも作成される。
そのためこのコマンドをうつだけで、モデル、ビュー、コントローラーの三つを作成することができる。
非常に簡単にログイン認証の仕組みを作ることができる。

逆に言うと
デフォルトで設定される URL 
デフォルトで使用されるコントローラー 
デフォルト使用されるビュー 
が存在するため、 細かい設定をするためには色々と変更しなければならない。 今回はその設定方法と考え方について解説する。

#デフォルトではどうなっているか

 まず、

 ```
$ rails g devise member
```
とするとmemberモデルが作成され ルーティングは以下のようになる（必要部分だけ）。


```
                    Prefix Verb   URI Pattern                                                       Controller#Action
        new_member_session GET    /members/sign_in(.:format)                             devise/sessions#new
            member_session POST   /members/sign_in(.:format)                             devise/sessions#create
    destroy_member_session DELETE /members/sign_out(.:format)                            devise/sessions#destroy
       new_member_password GET    /members/password/new(.:format)                        devise/passwords#new
      edit_member_password GET    /members/password/edit(.:format)                       devise/passwords#edit
           member_password PATCH  /members/password(.:format)                            devise/passwords#update
                           PUT    /members/password(.:format)                            devise/passwords#update
                           POST   /members/password(.:format)                            devise/passwords#create
cancel_member_registration GET    /members/cancel(.:format)                              devise/registrations#cancel
   new_member_registration GET    /members/sign_up(.:format)                             devise/registrations#new
  edit_member_registration GET    /members/edit(.:format)                                devise/registrations#edit
       member_registration PATCH  /members(.:format)                                     devise/registrations#update
                           PUT    /members(.:format)                                     devise/registrations#update
                           DELETE /members(.:format)                                     devise/registrations#destroy
                           POST   /members(.:format)                                     devise/registrations#create
```

ここで抑えて欲しいのは
・URL（の先頭）には　members（モデル名+s）がつく
・コントローラーは　devise/〇〇　が使われるということ。（railsアプリのディレクトリ内にあるわけではない）


では、デフォルトの状態からどんなカスタマイズをしたくなるか、以下のようなパターンがある。
1. ビューをカスタマイズしたい（デフォルトはダサいので誰もがしたくなるはず）
2. URLをカスタマイズしたい（ユーザーから見える部分だから綺麗にしたくなったりする）
3. モデルごとに使用するコントローラを変更したい（コントローラーの挙動をカスタマイズしたい）

#コントローラーのカスタマイズ
デフォルトではdevise/〇〇が使われていたがこれを直接編集することはできない。
なので、コマンドでコントローラーを作成し、そのコントローラーを操作したいモデルに割り当て、それを編集する。
挙動のカスタマイズは人ぞれぞれなので、割り当てについて説明する。

```
$ rails g devise:controllers members
```
とするとapp/controllers/membersの中にコントローラが作られる


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/421795/b3ae905c-d535-8fc2-a849-8afe2b3c97a3.png)

しかしまだ、コントローラーを作っただけなのでルーティングは上のものと変化していない。
つまりまだmemberモデルはdevise/〇〇のコントローラ を使ってしまう。なので、memberモデルに今作ったcontrollers/members内のコントローラを割り当てる作業をする。

```routes.rb
Rails.application.routes.draw do
devise_for :members
end
```
を次のように変更する

```routes.rb
Rails.application.routes.draw do
#devise_for :members
#使用するコントローラをapp/contorolers/members内のものにできる
devise_for :members, module: "members"
end
```
もしくは次のようにしても良い

```routes.rb
Rails.application.routes.draw do
#devise_for :members
#以下ようにしても同様に、使用するコントローラをapp/contorolers/members以下にできる
#コントローラーの指定をより細かくできるという違いがある
devise_for :members, :controllers => {
 :sessions => 'members/sessions',
 :passwords => 'members/passwords',
 :registrations => 'members/registrations'
}
end
```
こうするとルーティングは次のようになり、memberモデルに今作ったcontrollers/members内のコントローラが割り当てられる。ここまできてようやくコントローラーがカスタマイズできる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/421795/bd490652-d068-1fe6-6ae3-894207a5a013.png)
##ちなみに
今回はmemberモデルに対応するコントローラ ということで`rails g devise:controllers maembers`としたが`rails g devise:controllers aaa`でも大丈夫だ。（単数形でも大丈夫、モデル名と一致させなくてもroutes.rbで割り当てをするので大丈夫）
その場合以下のようにする

```routes.rb
#これか
devise_for :members, module: "aaa"
#もしくはこれ
devise_for :members, :controllers => {
 :sessions => 'aaa/sessions',
 :passwords => 'aaa/passwords',
 :registrations => 'aaa/registrations'
}
```

#ビューのカスタマイズ

まず、前提として、基本的にビューはコントローラのアクションが実行されてから呼び出される。つまり、__ビューはコントローラーと連携してる必要がある__
これをしっかり覚えておいてください。

では、ビューをカスタマイズ
以下のコマンドでビューファイルを作成

```
$ rails g devise:views XXXs
```

app/views/XXXsディレクトリが作成され、その中にビューファイルが作られる。
XXXsはディレクトリ名になるわけだが、このディレクトリ名が重要。
`rails g devise:controllers maembers`で作った、controllers/members内のコントローラで呼び出されるビューをカスタマイズするのであれば`$ rails g devise:views members`とする。
つまり、__コントローラーのディレクトリ名とビューのディレクトリ名は一致していないといけない。__
また、XXXを省略して`$ rails g devise:views`　とするとディレクトリ名はdeviseになる（デフォルトのdevise/〇〇のコントローラで呼び出されるビューをカスタマイズできる）
<img width="600" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/421795/f8f3d5a1-8ea5-1973-1331-7d4141831924.png">

ここの話は、deviseで作られたモデルが複数ある時のがわかりやすいので、memberモデルとuserモデルを作ったとして改めて説明する。
（一から作ってビューやコントローラーは何もカスタマイズしていないものを考える。）

```
$ rails g devise member
$ rails g devise user
```
この時ルーティングは次のようになる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/421795/c53f5382-07ed-d63a-3772-57ad40f9d629.png)
ここからわかるように、
現状、userモデルもmemberモデルも　devise/〇〇のコントローラーを使ってしまうので、memberモデルには新たに、members内にコントローラを作ってそれを割り当てる作業をする。（先ほど説明した通り）

```
rails g devise:controllers maembers
```
してからroutes.rbを編集（上で説明した通り）

```routes.rb
Rails.application.routes.draw do
#devise_for :members
devise_for :members, module: "members"
devise_for :users
end
```

そうするとルーティングは以下のようになる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/421795/4f0bd095-f74f-ab68-579a-91db32a27b1e.png)
これで、各モデルごとに異なるコントローラーを割り当てることができた。
では、ここでuserモデルを操作するためのビューをカスタマイズするにはどうするのか
そう、`$ rails g devise:views`とすればよかった。なぜなら、「userモデルの操作を担当するコントローラーは__devise__/〇〇だから、viewフォルダ内では__devise__ディレクトリ内にビューファイルを作ればいい」からだ。

では、membersモデルを操作するためのビューをカスタマイズするにはどうするのか
そう、`$ rails g devise:views members`とすればよかった。なぜなら、「memberモデルの操作を担当するコントローラーは__members__/〇〇だから、viewフォルダ内では__members__ディレクトリ内にビューファイルを作ればいい」からだ。

もちろん

```
rails g devise:controllers maembers
rails g devise:controllers users
```
としてから

```routes.rb
Rails.application.routes.draw do
#devise_for :members
#devise_for :users
devise_for :members, module: "members"
devise_for :users, module: "users"
end
```
としても良い
(というかするのが一般的ではある...気がする... どっちか一方のコントローラーだけdeviseのままなのは変な感じがする)

くどくなってきたが、大事なのは
__コントローラーのディレクトリ名に合わせてビューを作ろう__
ということだ。


##注意
deviseでコントローラーをカスタマイズするときも、基本的にコントローラー名は複数形にすべきだが、単数でも大丈夫と説明した。
rails g devise:controllers <font color="Red">member</font>
でもちゃんとディレクトリは作られる。
この時ビューをカスタマイズするのであれば、ビューファイルは app/views/<font color="Red">member</font>内にないといけない。
ならば
rails g devise:views <font color="Red">member</font>
とするべきだろう。
しかし、このビューを作るコマンドでは、作られるディレクトリは必ず複数形になって作られてしまう。
つまり、
rails g devise:views <font color="Red">member</font>
としてもapp/views/<font color="Red">member</font>内にビューファイルはできずにapp/views/<font color="Blue">members</font>内にできる。
なので、コントローラ 名を単数形にしたい時、かつ、ビューファイルをカスタマイズしたいときは、一旦ディレクトリ名を複数形でビューファイルを作成してから、手動でディレクトリ名を変更する必要がある。

#URLのカスタマイズ
一番最初に、URL（の先頭）には　members（モデル名+s）がつくと説明した。こういうのを変えたかったり、sign_inをloginに変えたかったりすることがある。
###デフォルトのURLの先頭を変える方法
memberモデルがあるときデフォルトは `/members/sign_in`とか `/members/sign_up`とかになっている。
これを`/aaaa/sign_in`とか `/aaaa/sign_up`にするなら以下。

```routes.rb
Rails.application.routes.draw do
#devise_for :members
devise_for :members, path: 'aaaa'
end
```
先頭の`/members`を無くして`/sign_in`とか `/sign_up`にするなら以下。

```routes.rb
Rails.application.routes.draw do
#devise_for :members
devise_for :members, path: ''
end
```

###/sign_inをloginにしたりする方法
```routes.rb
Rails.application.routes.draw do
#devise_for :members
devise_for :members, path_names: {
      sign_in: 'login', sign_out: 'logout',
      password: 'secret', confirmation: 'verification',
      registration: 'register', edit: 'edit/profile'
    }
end
```

#まとめ
これまでの説明を踏まえると、例えば以下のようになる

```
$ rails g devise member
$ rails g devise user
$ rails g devise:controllers members
$ rails g devise:views members
$ rails g devise:controllers admins
$ rails g devise:views admins
```
```roures.rb
Rails.application.routes.draw do
devise_for :members,
	module: "members",
	path: '',
	path_names: {
      sign_in: 'login', sign_out: 'logout',
      password: 'secret', confirmation: 'verification',
      registration: 'register', edit: 'edit/profile'
    }

devise_for :users,
	module: "admins",
	path: "admin"
end
```


```
[vagrant@localhost devise-app]$ rails routes
                    Prefix Verb   URI Pattern                                  Controller#Action
        new_member_session GET    /login(.:format)                             members/sessions#new
            member_session POST   /login(.:format)                             members/sessions#create
    destroy_member_session DELETE /logout(.:format)                            members/sessions#destroy
       new_member_password GET    /secret/new(.:format)                        members/passwords#new
      edit_member_password GET    /secret/edit(.:format)                       members/passwords#edit
           member_password PATCH  /secret(.:format)                            members/passwords#update
                           PUT    /secret(.:format)                            members/passwords#update
                           POST   /secret(.:format)                            members/passwords#create
cancel_member_registration GET    /register/cancel(.:format)                   members/registrations#cancel
   new_member_registration GET    /register/sign_up(.:format)                  members/registrations#new
  edit_member_registration GET    /register/edit/profile(.:format)             members/registrations#edit
       member_registration PATCH  /register(.:format)                          members/registrations#update
                           PUT    /register(.:format)                          members/registrations#update
                           DELETE /register(.:format)                          members/registrations#destroy
                           POST   /register(.:format)                          members/registrations#create
          new_user_session GET    /admin/sign_in(.:format)                     admins/sessions#new
              user_session POST   /admin/sign_in(.:format)                     admins/sessions#create
      destroy_user_session DELETE /admin/sign_out(.:format)                    admins/sessions#destroy
         new_user_password GET    /admin/password/new(.:format)                admins/passwords#new
        edit_user_password GET    /admin/password/edit(.:format)               admins/passwords#edit
             user_password PATCH  /admin/password(.:format)                    admins/passwords#update
                           PUT    /admin/password(.:format)                    admins/passwords#update
                           POST   /admin/password(.:format)                    admins/passwords#create
  cancel_user_registration GET    /admin/cancel(.:format)                      admins/registrations#cancel
     new_user_registration GET    /admin/sign_up(.:format)                     admins/registrations#new
    edit_user_registration GET    /admin/edit(.:format)                        admins/registrations#edit
         user_registration PATCH  /admin(.:format)                             admins/registrations#update
                           PUT    /admin(.:format)                             admins/registrations#update
                           DELETE /admin(.:format)                             admins/registrations#destroy
                           POST   /admin(.:format)                             admins/registrations#create

```

<img width="600" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/421795/939df354-9075-cbb7-54fc-143d9535b647.png">

#参考
https://qiita.com/akasakas/items/138c29fa2cecd271cfe4
https://qiita.com/ryosuketter/items/9240d8c2561b5989f049
https://www.rubydoc.info/github/heartcombo/devise/master/ActionDispatch/Routing/Mapper
特に3つ目のやつ見てください。