Deviseで新規登録時とメールアドレス変更時のテンプレートを分ける

## 目的

デフォルト状態では、
新規登録時(confirmation)とメールアドレス変更時(reconfirmation)に送信するメールのテンプレートが共通で、細かく分けたい時に不便である。例えばメール本文(body)は条件分岐などで対処できるが、件名(subject)は変更できない。

deviseではテンプレートを分けるための拡張ポイント[`Devise::Models::Confirmable#send_on_create_confirmation_instructions`](https://github.com/plataformatec/devise/blob/v3.4.1/lib/devise/models/confirmable.rb#L157-L159)を用意している。
deviseを継承したモデルで拡張できるので、簡単に別のテンプレートを定義することができる。
今回のサンプルは[こちら](https://github.com/haman29/sample/tree/master/devise-sample)に置いています。

## 手順

### Deviseのインストール

```zsh
$ rails new devise-sample -T --skip-bundle
```

```rb:Gemfile
+gem 'devise'
+group :development do
+  gem 'letter_opener_web' # これは無くてもOK
+end
```

```zsh
$ bundle install
```

```zsh
$ rails generate devise:install
```

```rb:config/environments/development.rb
Rails.application.configure do
# ...
  config.action_mailer.raise_delivery_errors = false
+  config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
+  config.action_mailer.delivery_method = :letter_opener_web
# ...
```

```zsh
$ rails generate devise user
$ rails g devise:views
```

###confirmable, reconfirmableを有効にする

```rb:app/models/user.rb
class User < ActiveRecord::Base
   devise :database_authenticatable, :registerable,
-         :recoverable, :rememberable, :trackable, :validatable
+         :recoverable, :rememberable, :trackable, :validatable,
+         :confirmable
 end
```

```rb:db/migrate/20150321024955_devise_create_users.rb
class DeviseCreateUsers < ActiveRecord::Migration
       t.string   :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
+      t.string   :confirmation_token
+      t.datetime :confirmed_at
+      t.datetime :confirmation_sent_at
+      t.string   :unconfirmed_email # Only if using reconfirmable
end
```
```rb:config/initializers/devise.rb
+  config.reconfirmable = true
```

### テンプレートを分ける

| 用途 | テンプレート | | 
|---|---|---|
| 新規登録(confirmation) | confirmation_on_create_instructions.html.erb | 任意の名前(統一する)
| メールアドレス変更(reconfirmation) | confirmation_instructions.html.erb　| デフォルト |

[Devise::Models::Confirmable::send_confirmation_instructions](https://github.com/plataformatec/devise/blob/v3.4.1/lib/devise/models/confirmable.rb#L260-L267)を参考に実装しました。

```rb:app/models/user.rb
class User < ActiveRecord::Base
   devise :database_authenticatable, :registerable,
          :recoverable, :rememberable, :trackable, :validatable,
          :confirmable
+
+  # override Devise::Models::Confirmable#send_on_create_confirmation_instructions
+  def send_on_create_confirmation_instructions
+    generate_confirmation_token!  unless @raw_confirmation_token
+    send_devise_notification(:confirmation_on_create_instructions, @raw_confirmation_token, {})
+  end
 end
```

```erb:app/views/devise/mailer/confirmation_instructions.html.erb
-<p>Welcome <%= @email %>!</p>
+<p>Reconfirmation <%= @email %>!</p>
 
 <p>You can confirm your account email through the link below:</p>
```
 
```erb:devise-sample/app/views/devise/mailer/confirmation_on_create_instructions.erb
+<p>Welcome <%= @email %>!</p>
+
+<p>You can confirm your account email through the link below:</p>
+
+<p><%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %></p>
```
[Devise::Mailer](https://github.com/plataformatec/devise/blob/v3.4.1/app/mailers/devise/mailer.rb)を参考に実装しました。

```rb:config/initializers/monkey_patches/devise/mailer.rb
+if defined?(ActionMailer)
+  class Devise::Mailer < Devise.parent_mailer.constantize
+    include Devise::Mailers::Helpers
+
+    def confirmation_instructions(record, token, opts={})
+      @token = token
+      devise_mail(record, :confirmation_instructions, opts)
+    end
+
+    def reset_password_instructions(record, token, opts={})
+      @token = token
+      devise_mail(record, :reset_password_instructions, opts)
+    end
+
+    def unlock_instructions(record, token, opts={})
+      @token = token
+      devise_mail(record, :unlock_instructions, opts)
+    end
+
+    # 新規追加!
+    def confirmation_on_create_instructions(record, token, opts={})
+      @token = token
+      devise_mail(record, :confirmation_on_create_instructions, opts)
+    end
+  end
+end
```

```rb:config/locales/devise.en.yml
       unauthenticated: "You need to sign in or sign up before continuing."
       unconfirmed: "You have to confirm your email address before continuing."
     mailer:
-      confirmation_instructions:
+      confirmation_on_create_instructions:
         subject: "Confirmation instructions"
+      confirmation_instructions:
+        subject: "Reconfirmation instructions"
       reset_password_instructions:
         subject: "Reset password instructions"
       unlock_instructions:
```

## 確認

sign_upしてメールが飛んでるか確認する(下記では会員登録時だけ..)

![スクリーンショット 2015-03-21 12.34.14.png](https://qiita-image-store.s3.amazonaws.com/0/3556/c5250c34-bc85-78ad-9d16-1cb0517a2e23.png)


`letter_opener_web`便利

![スクリーンショット 2015-03-21 12.34.27.png](https://qiita-image-store.s3.amazonaws.com/0/3556/6d756fbb-e827-49f3-4878-8144b3e37e46.png)

## 参考

http://willnet.in/86
https://github.com/plataformatec/devise
https://github.com/fgrehm/letter_opener_web