devise+confirmableでsign up完了時にメールを飛ばしたりする方法

メモ。

deviseはRails3で便利な認証ライブラリですが、viewは $rails generate devise:views を実行すればapp/views/devise以下に展開されるので自由に変更できるものの、modelやcontrollerのロジックはライブラリ内にあるのであまりいじれません。というのが前提。

で、ユーザに登録時には登録ありがとうメールを飛ばしたりとか、何かしらメソッドを実行したいことがあります。deviseでも、modelのクラスそのものはアプリ側で定義できるので、標準ではたとえばafter_createに実行したいメソッドを指定しておけば、modelの生成時点=登録時点で実行されるようです。便利。

ところが、deviseにはconfirmableというオプションをつけられます。これは、いったん確認メールを送り、それに書かれているURLを踏むと登録が完了するというよくあるタイプのものです。この辺りは気が効いていて重宝するのですが、これだと上記の方法は完了する前に実行されてしまうので使えません。

まあmonkey patchingを駆使することもできますが、あんまりお行儀が良くないというか、ライブラリのささやかな変更でダメージを食らいそうなので避けたいところです。というわけで、たぶんこれだとうまく行くという方法。

confirmableでは、modelにconfirm!というメソッドが追加されます。調べてみたところだと、これが登録完了のために実行するメソッドのようです。ので、ここをアプリ側で定義しているmodelでoverrideします。Userクラスの場合は↓こんな感じ。

class User < ActiveRecord::Base

  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable, :confirmable, :timeoutable, :token_authenticatable

  ## override Devise::Models::Confirmable#confirm!
  def confirm!
    super
    if confirmed?
      UserMailer.welcome_email(self).deliver
    end
  end

  # Setup accessible (or protected) attributes for your model
  attr_accessible :email, :password, :password_confirmation, :remember_me
end

confirm!メソッドの中では、まずsuperでDevise::Models::Confirmable::confirm!を実行します。そのあとで、confirmed?メソッドで登録完了したかどうかを確認します。もし完了していれば、そこでUserMailer.welcome_emailでメールを作成し、deliverで送信しています。

というわけで、(調べるにはそれなりに手間がかかったものの)簡単に実現できるようです。devise便利。