Rails6にDeviseを導入する

# 環境
Rubyバージョン: `ruby 2.7.4p191 (2021-07-07 revision a21a3b7d23) [x64-mingw32]`
Rails Gemバージョン: `6.1.4.1`
devise Gemバージョン: `4.8.0`
devise-i18n Gemバージョン: `1.10.0`
ユーザモデル名: `User`
テンプレートエンジン: `erb`

# 参考になるサイト
英語読める&README読む能力がある方は正直公式見るのが一番早いと思います。

- https://github.com/heartcombo/devise/blob/c82e4cf47b02002b2fd7ca31d441cf1043fc634c/README.md
- https://github.com/tigrish/devise-i18n/blob/802be774c28beda1587333dd7b5c761c9b5f8107/README.md

# この記事ではなにをする？
- [Deviseの導入](#deviseの導入)
- [i18n(日本語)に対応させる](#i18n日本語に対応させる)

# Deviseの導入

## Gemfile編集
Gemfileの一番下に追記

```rb:Gemfile
gem 'devise'
```

## Gemインストール
ターミナルでコマンド実行

```shell:Terminal
bundle install
```

## Devise用ファイル作成
ターミナルでコマンド実行

```shell:Terminal
rails generate devise:install
```

## DeviseのViewを編集できるようにする
※必要がない場合、もしくは後述する日本語化をする場合は不要

ターミナルでコマンド実行

```shell:Terminal
rails generate devise:views
```

これによって、`app/views/devise`の中にviewファイルが作成され、それを自由に編集することができます

## Deviseで必要な設定を追加
※既に設定されている場合は不要

`config/environments/development.rb`の一番下(endよりは上)に追記
ホスト名(host)、ポート(port)は場合によっては適当なものに置き換える
ここでのホスト名とポートはdevelopment環境のものです

```rb:config/environments/development.rb
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
```

## rootルーティングを追加
※既に設定されている場合は不要

`config/routes.rb`に追加(追加する場所は好きな場所でOK)
コントローラー名、アクション名は適当なものに置き換え

```rb:config/routes.rb
Rails.application.routes.draw do
  # ~~~~~~~~~~
  root 'home#index'
end
```

## フラッシュメッセージを表示するようにする
※既に設定している場合は不要

`app/views/layouts/application.html.erb`のbodyの中に追加

```erb:app/views/layouts/application.html.erb
<% if flash[:notice] %>
    <p>Notice: <%= flash[:notice] %></p>
<% end %>
<% if flash[:alert] %>
    <p>Alert: <%= flash[:alert] %></p>
<% end %>
```

## Devise用ユーザモデルを作成
ターミナルでコマンドを実行
この記事ではモデル名を`User`としていますが、任意の名前にすることができます(`Member`など)

```shell:Terminal
rails generate devise User
```

このとき、`config/routes.rb`に`devise_for :users`が追加されていて、これによってdeviseで必要なすべてのルーティングが自動で追加されます(`/users/sign_in`など)

## Deviseの機能をカスタマイズ

以下は使用する場合だけ行います
複数の機能を使用することもできます

### 全機能共通モデル編集
`app/models/user.rb`に使用したい機能を追加
使用したい機能全てをカンマ区切りで追加します

```diff:app/models/user.rb
-  devise :database_authenticatable, :registerable,
-         :recoverable, :rememberable, :validatable
+  devise :database_authenticatable, :registerable,
+         :recoverable, :rememberable, :validatable, :confirmable, :foo, :bar
```

### Trackable

ログイン履歴や状態が記録されるようになります

#### マイグレーション編集
`db/migrate/xxxxxxxxxxxxxx_devise_create_users.rb`の一部コメントを解除

```diff:db/migrate/xxxxxxxxxxxxxx_devise_create_users.rb
-      ## Trackable
-      # t.integer  :sign_in_count, default: 0, null: false
-      # t.datetime :current_sign_in_at
-      # t.datetime :last_sign_in_at
-      # t.string   :current_sign_in_ip
-      # t.string   :last_sign_in_ip
+      ## Trackable
+      t.integer  :sign_in_count, default: 0, null: false
+      t.datetime :current_sign_in_at
+      t.datetime :last_sign_in_at
+      t.string   :current_sign_in_ip
+      t.string   :last_sign_in_ip
```

### Confirmable

新規登録時にメールアドレスの確認メールが送信されるようになります(よくあるやつ)

#### マイグレーション編集
`db/migrate/xxxxxxxxxxxxxx_devise_create_users.rb`の一部コメントを解除
`t.string   :unconfirmed_email`は、メールアドレスを後から変更する場合に必要になります

```diff:db/migrate/xxxxxxxxxxxxxx_devise_create_users.rb
-      ## Confirmable
-      # t.string   :confirmation_token
-      # t.datetime :confirmed_at
-      # t.datetime :confirmation_sent_at
-      # t.string   :unconfirmed_email # Only if using reconfirmable
+      ## Confirmable
+      t.string   :confirmation_token
+      t.datetime :confirmed_at
+      t.datetime :confirmation_sent_at
+      t.string   :unconfirmed_email # Only if using reconfirmable
```

```diff:db/migrate/xxxxxxxxxxxxxx_devise_create_users.rb
-      # add_index :users, :confirmation_token,   unique: true
+      add_index :users, :confirmation_token,   unique: true
```

### Lockable

一定回数ログインに失敗した場合アカウントを凍結します
通常、メールで本人確認することで解除されます
`t.string   :unlock_token`は、`config.unlock_strategy`が`email`か`both`のときだけコメント解除します(デフォルトはboth)

#### マイグレーション編集
`db/migrate/xxxxxxxxxxxxxx_devise_create_users.rb`の一部コメントを解除

```diff:db/migrate/xxxxxxxxxxxxxx_devise_create_users.rb
-      ## Lockable
-      # t.integer  :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
-      # t.string   :unlock_token # Only if unlock strategy is :email or :both
-      # t.datetime :locked_at
+      ## Lockable
+      t.integer  :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
+      t.string   :unlock_token # Only if unlock strategy is :email or :both
+      t.datetime :locked_at
```

```diff:db/migrate/xxxxxxxxxxxxxx_devise_create_users.rb
-      # add_index :users, :unlock_token,         unique: true
+      add_index :users, :unlock_token,         unique: true
```

### Timeoutable

一定期間何も行われていないと自動でサインアウトさせる
※マイグレーションの編集はなし

## マイグレーション

ターミナルでコマンド実行

```shell:Terminal
rake db:migrate
```

## 各種リンクをViewに追加
`app/views/layouts/application.html.erb`のbodyの中に追加

```erb:app/views/layouts/application.html.erb
<ul>
    <% if current_user %>
        <li><%= current_user.email %>さん</li>
        <li><%= link_to 'アカウント編集', edit_user_registration_path %></li>
        <li><%= link_to 'ログアウト', destroy_user_session_path, method: :delete %></li>
    <% else %>
        <li><%= link_to 'アカウント登録', new_user_registration_path %></li>
        <li><%= link_to 'ログイン', new_user_session_path %></li>
    <% end %>
</ul>
```

## 動作確認
※Railsを再起動しておく

リンクやメッセージが正常に表示されていることを確認します

![screenshot1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/620130/d2239c7a-37a9-ab2f-0015-e5e98daead52.png)
![screenshot2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/620130/640425b1-d16d-b905-fc2d-41332009ed69.png)

## メールを使用する場合
development環境ではデフォルトでメールの送信内容は**コンソールに表示されます**が、流れてしまって見づらいので[letter_opener gem](https://github.com/ryanb/letter_opener)を使用するのがおすすめです(よくgmailを設定したりしますが、development環境でそれを行うのはあまり良くないと思います)
もしよければ[5分でRailsにletter_opener gemを導入する](https://qiita.com/PJZ9n/items/9a87e1fe813b7c96a4d1)もご覧ください

### メールの送信元を変更する
デフォルトでは、メールの送信元が`please-change-me-at-config-initializers-devise@example.com`となっています(development環境ではこれでも特に問題ありません)が、`config/initializers/devise.rb`を編集することでこれを変更することもできます。
※再起動必須

```diff:config/initializers/devise.rb
- config.mailer_sender = 'please-change-me-at-config-initializers-devise@example.com'
+ config.mailer_sender = 'sukina_mail_address@example.com'
```

# i18n(日本語)に対応させる

## rails-i18nが導入されていない場合
[[簡単] Rails6の標準メッセージを日本語に対応させる(i18n)](https://qiita.com/PJZ9n/items/1dea7285d3b68280aeda)を参考に導入する

## Gemfile編集
Gemfileの一番下に追記

```rb:Gemfile
gem 'devise-i18n'
```

## Gemインストール
ターミナルでコマンド実行

```shell:Terminal
bundle install
```

## 動作確認
ブラウザでログイン画面などを開き、日本語化されていることを確認する
![screenshot3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/620130/622059d8-2b57-ee53-6de4-ca333c1261e3.png)

## 日本語化できていない場合もしくはViewを編集したい場合

### すでに`app/views/devise`が存在する場合

#### viewをカスタマイズしていない場合か、変更箇所が少ない場合
`app/views/devise`をディレクトリごと削除する

#### viewをかなりカスタマイズしている場合
[こちら](https://github.com/tigrish/devise-i18n/tree/802be774c28beda1587333dd7b5c761c9b5f8107/app/views/devise)を参考に、すべてのメッセージをi18nの形(`t('devise.confirmations.confirmed')`など)に置き換える

### deviseのviewを作成する
ターミナルでコマンド実行

```shell:Terminal
rails g devise:i18n:views
```

これで、`app/views/devise`にi18n(日本語)に対応したviewが作成されます
それを自由に編集することができます

## deviseのメッセージを編集したい場合

### ロケールファイルを作成する
ターミナルでコマンド実行

```shell:Terminal
rails g devise:i18n:locale ja
```

`config/locales/devise.views.ja.yml`が作成されて、これを編集することができます