始まり
- Facebookからのサインアップ時のみ、確認メールを飛ばすことなくそのまま続きの処理をする、という流れにするために、confirmableで何をやっているのかのコードを読んだ。
サインアップ時の処理
- まずはサインアップ時の処理をやっている
app/controllers/devise/registrations_controller.rb
のcreate
メソッドから見て行く。 - ここを見ると、まず新規Userモデルを保存した後、
user.active_for_authentication?
を呼び出している。 - このメソッドの大元は
lib/devise/models/authenticatable.rb
に書かれているが、lib/devise/models/confirmable.rb
でオーバーライドされて以下のようになっている。
def active_for_authentication?
super && (!confirmation_required? || confirmed? || confirmation_period_valid?)
end
-
super
はデフォルトでtrueを返すので、実質&&
以降の処理。 - 後半の三つは、「
confirmed_at
に値が入っているか?」「確認メールの期限が切れた状態になっていないか」を確認している。 - 早い話が
confirmed_at
に値が入って入ればtrue
になる仕掛け。 - なので、最初のサインアップした直後はこの値が必ず
nil
なので、返り値が必ずfalse
になるようになっている。 - 結果として
after_inactive_sign_up_path_for
に飛ばされる。これが、最初のページに飛ばされる仕組み。(trueの場合には、after_sign_up_path_for
へ飛ばされる) - ではメールはどこで飛んでいるのか。実は
lib/devise/models/confirmable.rb
の中で、以下のようなコードがある。
after_commit :send_on_create_confirmation_instructions, on: :create, if: :send_confirmation_notification?
- つまり、
confirmed_at
に値が入ってなければ、メールを飛ばす処理を仕込んでいるのだ。 - 結論として、
confirmable
をオンにした状態で、確認処理を無くしたい場合には、新規Userを作成する時にconfirmed_at
に値が入っている必要がある。 - なので、
app/controllers/devise/registrations_controller.rb
のcreate
メソッドをオーバーライドしてこのようにしてあげればいい。
def create
build_resource(sign_up_params)
# skip_confirmationはconfirmed_atに現在時刻(UTC)を入れるメソッド
resource.skip_confirmation!
resource.save
...
end
FaceBookの場合
- さてFacebookだが、実はFBでは
app/controllers/devise/registrations_controller.rb
ではなく、app/controllers/devise/omniauth_callbacks_controller.rb
のfacebook
メソッドに処理が帰ってくる。
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def facebook
# You need to implement the method below in your model (e.g. app/models/user.rb)
@user = User.from_omniauth(request.env["omniauth.auth"])
if @user.persisted?
sign_in_and_redirect @user, event: :authentication #this will throw if @user is not activated
set_flash_message(:notice, :success, kind: "Facebook") if is_navigational_format?
else
session["devise.facebook_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
def failure
redirect_to root_path
end
end
- これはdevise公式の例なのだが、この処理のどこかで、
skip_confirmation!
を実行する必要がある。 - この中でUserを保存しているのは
User.from_omniauth
だ。
def self.from_omniauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
user.email = auth.info.email
user.password = Devise.friendly_token[0,20]
user.name = auth.info.name # assuming the user model has a name
user.image = auth.info.image # assuming the user model has an image
# If you are using confirmable and the provider(s) you use validate emails,
# uncomment the line below to skip the confirmation emails.
# user.skip_confirmation!
end
end
- ここでは
first_or_create
を使っているので、もう直接引数で与えてしまう方が早い気がする。
def self.from_omniauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
# skip confirmation
user.confirmed_at = Time.now.utc
end
end
- これなら、FBの時はconfirmationを回避することができるようになる。