deviseのカスタマイズ

[株式会社TECH LUCK](https://techluck.co.jp/?utm_source=qiita&utm_medium=article&utm_campaign=myarticle)という会社で代表兼エンジニアをしている齊藤です。

DXプロジェクト、開発プロジェクト、Rails開発などでお困りごとがありましたら[弊社HP](https://techluck.co.jp/?utm_source=qiita&utm_medium=article&utm_campaign=myarticle)からご相談をいただけますと幸いです。
以下のような問題に対応することが可能です。
- プロジェクトでRailsエンジニアが足りなくて困っている
- Railsのバージョンアップをしたいがノウハウ・リソースが足りなくて困っている
- オフショア開発をしているが、要件の齟齬やコード品質が悪いので改善したい

また、Railsエンジニアも募集しておりますので、興味がありましたら[弊社HP](https://techluck.co.jp/?utm_source=qiita&utm_medium=article&utm_campaign=myarticle)からご連絡いただけますと幸いです。

# 前提

ユーザー管理機能の作成のためにdeviseを導入しカスタマイズする際に苦労することが多くあるので備忘録として残しておきます。

# 基本的なdeviseのインストール

Gemのインストール
```ruby:Gemfile
gem 'devise'
```

```bash:ターミナル
bundle install
```

deviseの関連ファイルをインストールする。
```bash:ターミナル
rails g devise:install
```

# Userモデルの作成とマイグレーション

```bash:ターミナル
rails g devise user
```

```bash:ターミナル
bundle exec rails db:migrate
```


# カスタマイズ
ここからが本題。

## 複数のモデルにdeviseを導入する場合
UserやCompanyでdeviseを複数のモデルで使いたい場合を想定します。

この場合には、UserはUser専用のコントローラー（`Devise::~~~Controller`を継承したコントローラー）とビュー、CompanyはCompany専用のコントローラー（`Devise::~~~Controller`を継承したコントローラー）とビューを作成するようにします。

```bash:ターミナル
# Userモデルの作成と、User専用のコントローラーとビューの作成
rails g devise user

bundle exec rails db:migrate

rails g devise:controllers users

rails g devise:views users

# Comapnyモデルの作成と、Company専用のコントローラーとビューの作成
rails g devise company

bundle exec rails db:migrate

rails g devise:controllers companies

rails g devise:views companies
```

## ルーティングをカスタマイズする
ログインの画面は`/login`, 新規登録の画面は`/signup`のようなURLにしたい場合は、以下のように`routes.rb`を編集するいいです。

```ruby:routes.rb
# skip: :allでdeviseで作成されたusersに関するすべてのルーティングを無効にすることができる
devise_for :users, skip: :all

# users専用のコントローラーを作成していない場合は、以下のようにdevise配下のコントローラーへルーティングを設定する
devise_scope :user do
  get 'login' => 'devise/sessions#new', as: :new_user_session
  post 'login' => 'devise/sessions#create', as: :user_session
  delete 'logout' => 'devise/sessions#destroy', as: :destroy_user_session
  get 'signup' => 'devise/registrations#new', as: :new_user_registration
  post 'signup' => 'devise/registrations#create', as: :user_registration
end

# users専用のコントローラーを作成した場合は、以下のようにusers配下のコントローラーへルーティングを設定する
devise_scope :user do
  get 'login' => 'users/sessions#new', as: :new_user_session
  post 'login' => 'users/sessions#create', as: :user_session
  delete 'logout' => 'users/sessions#destroy', as: :destroy_user_session
  get 'signup' => 'users/registrations#new', as: :new_user_registration
  post 'signup' => 'users/registrations#create', as: :user_registration
end
```

## 日本語対応する。
deviseはデフォルトでは英語になっているので、日本語に対応させます。

i18n対応のためのGemをインストール
```ruby:Gemfile
gem 'devise-i18n'
gem 'devise-i18n-views'
```

```bash:ターミナル
bundle install
```

deviseを日本語化対応するための`devise.views.ja.yml`ファイルの作成。

```bash:ターミナル
rails g devise:views:locale ja
```

deviseを日本語化対応するためのビューファイルの作成。

```bash:ターミナル
rails g devise:i18n:views user
```

ここで注意が必要なのが、上記のコマンドはuser専用のビューファイルが作成されることになることです。
これで作成した場合には、さきほど`devise.views.ja.yml`の中で以下のようになっていますが、`devise`となっている箇所をusersと変更しないと適用されないので注意しましょう。

```bash:ターミナル
ja:
  devise:
    confirmations:
      new:
        resend_confirmation_instructions: "アカウント確認メール再送"
```

以下の記述を、`config/application.rb`に追加する。
```ruby:config/application.rb
config.i18n.default_locale = :ja
```

これでサーバーを再起動させれば、日本語化対応となります。

## 新規登録の際に他のカラムも保存する
`configure_permitted_parameters`を`app/controllers/application_controller.rb`でオーバーライドする方法もありますが、複数のモデルに対応するために、それぞれの`registrations_controller.rb`で対応しましょう。

`registrations_controller.rb`の`configure_sign_up_params`と`configure_account_update_params`に追加するキーを設定し、` before_action :configure_sign_up_params, only: [:create]`と`before_action :configure_account_update_params, only: [:update]`を設定する。
これらはコメントアウトされているので、コメントアウトを外して利用できるようにして追加で設定を行なっていきましょう。

また、parameterなどをさらにカスタマイズしたい場合には以下ようにも書くことができます。

```ruby:registrations_controller.rb
    devise_parameter_sanitizer.permit(:sign_up) do |user_params|
      user_params.permit(:last_name,
                         :first_name)
                 .merge(registration_date: Date.today)
      # devise_parameter_sanitizerにはmergeメソッドは使えないので、このようにする
    end
```


## メールアドレスを認証する際に新規登録とメールアドレス変更時で送信するメールを分ける
ほぼ以下の記事と同じです。

https://qiita.com/haman29/items/2f1cbfb3fa5606c38e52

> deviseではテンプレートを分けるための拡張ポイント`Devise::Models::Confirmable#send_on_create_confirmation_instructions`を用意している。

とのことです。

user.rbに以下のメソッドを定義します。

```ruby:user.rb
def send_on_create_confirmation_instructions
  generate_confirmation_token! unless @raw_confirmation_token
  send_devise_notification(:confirmation_on_create_instructions, @raw_confirmation_token, {})
end
```

そうすると新規作成の際には、`confirmation_on_create_instructions.html.erb`をActionMailerで送信されるようになります。

`app/mailers/users/mailer.rb`を作成します。
ここで、`Devise::Mailer`を継承したクラスを作るのと、deviseで読み込む`Mailer`のファイルを変更します。

```ruby:app/mailers/users/mailer.rb
class Users::Mailer < Devise::Mailer
  default from: "info@example.com"
  include Devise::Controllers::UrlHelpers
  default template_path: 'devise/mailer'

  # メールアドレス変更の際の認証メール
  def confirmation_instructions(record, token, opts = {})
    opts[:subject] = "メールアドレス変更手続きを完了してください"
    opts[:from] = FROM
    super
  end

  # 新規登録の際の認証メール
  def confirmation_on_create_instructions(record, token, opts = {})
    opts[:subject] = "メールアドレス認証のお手続を完了してください"
    opts[:from] = FROM
    @token = token
    devise_mail(record, :confirmation_on_create_instructions, opts)
  end
end
```

```ruby:app/views/user_mailer/confirmation_on_create_instructions.html.erb
アカウント登録を完了するためには、以下のリンクをクリックしてメールアドレスの認証を完了してください。<br/>

<p><%= link_to '認証する', confirmation_url(@resource, confirmation_token: @token) %></p>
```

```ruby:app/views/user_mailer/confirmation_instructions.html.erb
メールアドレスの変更を行うためには、以下のリンクをクリックして認証を行ってください。

<p><%= link_to '認証する', confirmation_url(@resource, confirmation_token: @token) %></p>
```

```ruby:config/initializers/devise.rb
Devise.setup do |config|
  # 省略
  config.mailer = 'Users::Mailer'
  # 省略
end
```


`users/mailer/confirmation_on_create_instructions.html.erb`には、新規作成時のメール送信の文面を記載することになります。

`users/mailer/confirmation_instructions.html.erb`には、メールアドレスの変更の際に送信されるメールの文面を記載することになります。


## メールアドレスの認証後に完了画面を表示する
`confirmations_controller.rb`の`after_confirmation_path_for`に任意のパスを指定すれば、認証完了画面を表示することができます。

```ruby:confirmations_controller.rb
# The path used after confirmation.
def after_confirmation_path_for(_resource_name, _resource)
  confirmation_complete_path # ここのControllerとViewは作成が必要。
end
```


また、以下のようにすると、メールアドレス認証後に自動でログインして、自動でホーム画面へ遷移することができます。

```ruby:cofirmations_controller.rb
def after_confirmation_path_for(resource_name, resource)
  sign_in(resource)
  home_path(resource)
end
```


## メールアドレス認証の際に再送信をした後に再送信完了の画面を表示する
`confirmations_controller.rb`の`after_resending_confirmation_instructions_path_for`に中に任意のパスを指定すれば、メールアドレス認証の際にメールを再送信後に認証完了画面を表示することができます。

```ruby:confirmations_controller.rb
# The path used after resending confirmation instructions.
def after_resending_confirmation_instructions_path_for(_resource_name)
  confirmation_resend_path #ここのパスの先にあるControllerとViewは作成が必要
end
```
  

## ログイン画面でのエラーメッセージの表示
以下の記事を参考にすればフラッシュメッセージを表示することができるようになります。

https://qiita.com/4ma9147/items/5a6eead86f10c89c5b18


# 参考にした記事(圧倒的感謝)

https://qiita.com/Hal_mai/items/350c400e8763ce0487a3

https://qiita.com/chamao/items/de00920c343a3e237d20

https://qiita.com/suin/items/b479000c49d2468a6260

https://qiita.com/ryuuuuuuuuuu/items/48dec280cf8925968c65

https://blog.tanebox.com/archives/590/

https://easyramble.com/strong-parameters-on-rails-devise.html

https://qiita.com/haman29/items/2f1cbfb3fa5606c38e52

https://qiita.com/4ma9147/items/5a6eead86f10c89c5b18