【Rails】Deviseをカスタマイズして使いたい！！ (導入〜モデル編)

# どうした？

Railsでアプリを作る際、ユーザー認証に便利なのがDevise gemです。
ただこのDevise、何でもかんでも言いなりに使用するとブラックボックス化してしまいます。
メソッドのようなおいしいところだけ使わさせて頂き、基本的な設計は自分の手でおこなうためにチャレンジしてみました。

:::note
Deviseを使う前に、Railsチュートリアル等で勉強して自分でユーザー認証できるようにしましょう。
Deviseも[そう言ってます](https://github.com/heartcombo/devise#starting-with-rails)。
:::

# 今回目指すもの
今回目指すのは、以下のようなUserモデルです。

| カラム     | 物理名   | 型      | 条件 |
|:-:       |:-:      |:-:      |:-:|
| id       | id      | integer |  |
| 名前     | name     | string  | 入力: 必須, 最大文字数: 50 |
| Eメール   | email    | string  | 入力: 必須, ユニーク制限: あり, emailのrexexpを指定 |
| パスワード | password | string  | 入力: 必須, 文字数範囲: 8~20 |


# 手順

## 準備

devise gemをGemfileに追加します。

```ruby:Gemfile
gem 'devise'
```

記載したら`bundle install`しましょう。

ターミナルからdeviseをインストールします。

```terminal
$ rails generate devise:install
```

通常はこの後認証用のメールサーバーを指定するのですが、今回メール認証はしないのでこの手順は飛ばします。

次に、日本語の設定をします。
詳細はこちらの記事をご覧ください。

https://pikawaka.com/rails/devise#devise%E3%82%92%E6%97%A5%E6%9C%AC%E8%AA%9E%E5%8C%96%E3%81%97%E3%82%88%E3%81%86



## Userモデルの作成
ターミナルにて以下のコマンドを実行します。
```terminal
$ rails generate devise User
```
これにより、マイグレーションファイルとモデルファイルが生成されます。
これらを今回の実装に合わせて編集していきます。

**マイグレーションファイル**
```ruby:20230208232147_devise_create_users
# frozen_string_literal: true

class DeviseCreateUsers < ActiveRecord::Migration[6.1]
  def change
    create_table :users do |t|
      ## Database authenticatable
      t.string :name,               null: false # 追加
      t.string :email,              null: false, default: ""
      t.string :encrypted_password, null: false, default: ""

      ## Recoverable
      # t.string   :reset_password_token # 使わないのでコメントアウト
      # t.datetime :reset_password_sent_at

# 略

      t.timestamps null: false
    end

    add_index :users, :email,                unique: true
    # add_index :users, :reset_password_token, unique: true # 使わないのでコメントアウト
    # add_index :users, :confirmation_token,   unique: true
    # add_index :users, :unlock_token,         unique: true
  end
end
```

**モデルファイル**
```ruby:user.rb
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable, :recoverable, and :omniauthable
  devise :database_authenticatable, :registerable,
          :rememberable, :validatable #使用しないモジュール(:recoverable)はコメントアウト部分に移動

  validates :name,                presence: true,
                                  length: { maximum: 50 }

  validates :email,               presence: true,
                                  uniqueness: true

  validates :password,            presence: true
end
```

モデルファイルでは、モジュールの指定をすることができます。
各モジュールの機能については参考サイトをご覧ください。

設定が終わったら`rails db:migrate`でデータベースを作成しましょう。

テーブルが正しく作成されたか確認します。

```ruby:terminal
$ rails c

User.columns.map(&:name)
=> ["id", "name", "email", "encrypted_password", "remember_created_at", "created_at", "updated_at"]
```

指定した通りのカラムが入っていることから、無事にテーブルが作成されたことが確認できました。

続いて、バリデーションの確認をします。
コンソールでNGデータのUserを作成し、返ってきたエラーメッセージを確認します。

```ruby:terminal
$ rails c

# 空データを送信
User.create!
  TRANSACTION (0.4ms)  BEGIN
  User Exists? (1.1ms)  SELECT 1 AS one FROM `users` WHERE `users`.`email` = '' LIMIT 1
  TRANSACTION (0.5ms)  ROLLBACK
ActiveRecord::RecordInvalid: バリエーションに失敗しました: Eメール を入力してください, パスワード を入力してください, 名前 を入力してください, Eメール を入力してください, パスワード を入力してください
```
(※ エラーメッセージが日本語訳されていない場合は、`準備`の項で紹介した記事を元に設定してください)

エラーメッセージ中でEメールとパスワードが2回出現しています。
どうやらEメールとパスワードの必須条件はモデルファイルで指定した`validatable`モジュールに既に入っているようです。
モデルファイルを以下のように編集します。


```ruby:user.rb
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable, :recoverable, and :omniauthable
  devise :database_authenticatable, :registerable,
          :rememberable, :validatable

  validates :name,                presence: true,
                                  length: { maximum: 50 }

  validates :email,               uniqueness: true # 必須条件を削除

  # passwordのバリデーションを削除
end
```
もう一度コンソールを開き、バリデーションを確認します。

```ruby:terminal
$ rails c

# 必須入力の確認
User.create!
  TRANSACTION (0.3ms)  BEGIN
  TRANSACTION (0.4ms)  ROLLBACK
ActiveRecord::RecordInvalid: バリエーションに失敗しました: Eメール を入力してください, パスワード を入力してください, 名前 を入力してください

# nameの最大文字数、emailの形式、passwordの最小文字数を確認
User.create!(
  name: 'a'*51,
  email: 'sample',
  password: 'a')  
  TRANSACTION (0.3ms)  BEGIN
  TRANSACTION (1.3ms)  ROLLBACK
ActiveRecord::RecordInvalid: バリエーションに失敗しました: Eメール の形式が異なっています, パスワード は6文字以上で入力してください, 名前 は50文字以内で入力してください
```
先程発生していたバリデーションの重複がクリアしていますね。
また、必須入力、nameの最大文字数もクリアしていることが分かります。
emailの形式指定も指定していないのになぜかクリアしています。
パスワード文字数について、6文字以上にするように怒られてしまいました(狙いは8文字以上)。

emailの形式やパスワードの文字数については指定していないのに、なぜバリデーションにかかるのでしょうか？
それは、deviseの`varidatable`モジュールに元々組み込まれているからなのです。

`config/initializers/devise.rb`を開いてみましょう。
ここには、モジュールの設定値が記載されています。

```ruby:config/initializers/devise.rb
Devise.setup do |config|
# 略
  config.password_length = 6..128

  config.email_regexp = /\A[^@\s]+@[^@\s]+\z/
# 略
end
```
と記載されているのが分かります。
ここで設定されているため、我々が改めてバリデーションを指定する必要がなかったのですね。
これらの設定を変えることで、バリデーションの指定を変更することができます。

```ruby:config/initializers/devise.rb
Devise.setup do |config|
# 略
  config.password_length = 8..20 # 範囲を変更

  config.email_regexp = /\A[^@\s]+@[^@\s]+\z/ # 変更なし
# 略
end
```

設定したらコンソールで確認します。

```ruby:terminal
$ rails c

User.create!(
  name: 'sample',
  email: 'sample@example.com',
  password: 'a')  
  TRANSACTION (1.7ms)  BEGIN
  TRANSACTION (0.5ms)  ROLLBACK
ActiveRecord::RecordInvalid: バリエーションに失敗しました: パスワード は8文字以上で入力してください

User.create!(
  name: 'sample',
  email: 'sample@example.com',  
  password: 'a'*21)  
  TRANSACTION (0.4ms)  BEGIN
  TRANSACTION (0.2ms)  ROLLBACK
ActiveRecord::RecordInvalid: バリエーションに失敗しました: パスワード は20文字以内で入力してください
```

パスワードの最小文字数と最大文字数についてのバリデーションが設定できていることが確認できました。

# まとめ

今回の記事はここまでです。
引き続き、ルーティングの設定やコントローラの設定編をまとめていきます。

https://qiita.com/ysk91_engineer/items/35d71e25261286a52b7b

# 参考

https://github.com/heartcombo/devise

https://pikawaka.com/rails/devise