【Devise】開発途中でmoduleを追加する

## はじめに
現在開発中のWebサイトで管理者画面ログイン機能を`Devise`を用いて作成中です。


開発途中で仕様が変わり、Deviseの機能を一部有効化しようと思った時にどうすればいいのか困ったので備忘録として残しておきます。

## やりたいこと
Deviseのパスワードリセット機能とメール認証機能を追加したい。


## 状況
現在、Deviseで有効化している機能は以下の通りです。

```ruby:app/models/admin.rb
class Admin < ApplicationRecord 
  devise :database_authenticatable, :rememberable, :validatable, :trackable
end
```

```ruby:db/migration.rb
class DeviseCreateAdmins < ActiveRecord::Migration[6.0]
  def change
    create_table :admins do |t|
      ## Database authenticatable
      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

      ## Rememberable
      t.datetime :remember_created_at

      ## Trackable
      t.integer  :sign_in_count, default: 0, null: false
      t.datetime :current_sign_in_at
      t.datetime :last_sign_in_at
      t.inet     :current_sign_in_ip
      t.inet     :last_sign_in_ip

      ## Confirmable
      # t.string   :confirmation_token
      # t.datetime :confirmed_at
      # t.datetime :confirmation_sent_at
      # t.string   :unconfirmed_email # Only if using reconfirmable

      ## 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


      t.timestamps null: false
    end

    add_index :admins, :email,                unique: true
    # add_index :admins, :reset_password_token, unique: true
    # add_index :admins, :confirmation_token,   unique: true
    # add_index :admins, :unlock_token,         unique: true
  end
end
```
```ruby:schema.rb
create_table "admins", force: :cascade do |t|
    t.string "email", default: "", null: false
    t.string "encrypted_password", default: "", null: false
    t.datetime "remember_created_at"
    t.integer "sign_in_count", default: 0, null: false
    t.datetime "current_sign_in_at"
    t.datetime "last_sign_in_at"
    t.inet "current_sign_in_ip"
    t.inet "last_sign_in_ip"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.index ["email"], name: "index_admins_on_email", unique: true
  end
```



ここにパスワードリセット機能とメール認証機能を有効化させたいため、以下のモジュールを追加していきます。

- `recoverable`
- `confirmable`



### ルーティングの確認
ちなみに現在のルーティングは以下の様になっています。

```zsh:ターミナル
new_admin_session GET        /admin/sign_in(.:format)     admins/sessions#new
admin_session POST           /admin/sign_in(.:format)     admins/sessions#create
destroy_admin_session DELETE /admin/sign_out(.:format)    admins/sessions#destroy
```


## 実装方法

### 1. まずは追加したいモジュールをモデルに追記する。

```ruby:app/models/admin.rb
class Admin < ApplicationRecord 
  devise :database_authenticatable, :rememberable, :validatable, :trackable, :recoverable, :confirmable
end
```

### 2. カラムの追加  
マイグレーションファイルのコメントアウトをインにすればいいのでは？とも思いましたが、テーブルに変更を加えないといけないため新たにマイグレーションファイルを作っていきます。
以下コマンドでカラムを追加していきましょう。

```zsh:ターミナル
$ be rails g migration AddRecoverableToAdmins reset_password_token:string reset_password_sent_at:datetime
$ be rails g migration AddConfirmableToAdmins confirmation_token:string confirmed_at:datetime confirmation_sent_at:datetime unconfirmed_email:string

#以下のコマンドで一度に追加もできます。
$ be rails g migration AddDeviseToAdmins reset_password_token:string reset_password_sent_at:datetime confirmation_token:string confirmed_at:datetime confirmation_sent_at:datetime unconfirmed_email:string
```
以下が作成されたファイルです。

```ruby:db/migration.rb
class AddDeviseToAdmins < ActiveRecord::Migration[6.0]
  def change
    add_column :admins, :reset_password_token, :string
    add_column :admins, :reset_password_sent_at, :datetime
    add_column :admins, :confirmation_token, :string
    add_column :admins, :confirmed_at, :datetime
    add_column :admins, :confirmation_sent_at, :datetime
    add_column :admins, :unconfirmed_email, :string
  end
end
```


### 3. マイグレーションの実行  
マイグレーションファイルができたのを確認したらマイグレーションを実行してテーブル追加されるのを確認。

```zsh:ターミナル
$ rails db:migrate 
== 20210412221727 AddDeviseToAdmins: migrating ================================
-- add_column(:admins, :reset_password_token, :string)
   -> 0.0350s
-- add_column(:admins, :reset_password_sent_at, :datetime)
   -> 0.0036s
-- add_column(:admins, :confirmation_token, :string)
   -> 0.0039s
-- add_column(:admins, :confirmed_at, :datetime)
   -> 0.0040s
-- add_column(:admins, :confirmation_sent_at, :datetime)
   -> 0.0067s
-- add_column(:admins, :unconfirmed_email, :string)
   -> 0.0269s
== 20210412221727 AddDeviseToAdmins: migrated (0.0809s) =======================

```

### 4. schema.rbの確認
```ruby:schema.rb
create_table "admins", force: :cascade do |t|
    t.string "email", default: "", null: false
    t.string "encrypted_password", default: "", null: false
    t.datetime "remember_created_at"
    t.integer "sign_in_count", default: 0, null: false
    t.datetime "current_sign_in_at"
    t.datetime "last_sign_in_at"
    t.inet "current_sign_in_ip"
    t.inet "last_sign_in_ip"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.string "reset_password_token"
    t.datetime "reset_password_sent_at"
    t.string "confirmation_token"
    t.datetime "confirmed_at"
    t.datetime "confirmation_sent_at"
    t.string "unconfirmed_email"
    t.index ["email"], name: "index_admins_on_email", unique: true
  end
```
きちんとテーブルにカラムが追加されていますね！

### 5. ルーティングの確認
最後にルーティングも確認しておきましょう。

```zsh
    new_admin_session GET    /admin/sign_in(.:format)                                                                  admins/sessions#new
        admin_session POST   /admin/sign_in(.:format)                                                                 admins/sessions#create
destroy_admin_session DELETE /admin/sign_out(.:format)                                                                admins/sessions#destroy
# 以下が追記されていることを確認
   new_admin_password GET    /admin/password/new(.:format)                                                            devise/passwords#new
  edit_admin_password GET    /admin/password/edit(.:format)                                                           devise/passwords#edit
       admin_password PATCH  /admin/password(.:format)                                                                devise/passwords#update
                      PUT    /admin/password(.:format)                                                                devise/passwords#update
                      POST   /admin/password(.:format)                                                                devise/passwords#create
new_admin_confirmation GET    /admin/confirmation/new(.:format)                                                        devise/confirmations#new
   admin_confirmation GET    /admin/confirmation(.:format)                                                            devise/confirmations#show
                      POST   /admin/confirmation(.:format)                                                            devise/confirmations#create
```

## おわりに
本来なら最初にきちんと仕様を決めておくべきだと思いますが、これで途中でモジュールを追加したい場合にも対応できるようになるので結果オーライです！