deviseのconfirmableを使うには対象modelで宣言
devise :confirmable
必要なカラム
class AddConfirmableToUser < ActiveRecord::Migration[5.0] def change add_column :users, :confirmation_token, :string add_column :users, :confirmed_at, :datetime add_column :users, :confirmation_sent_at, :datetime add_column :users, :unconfirmed_email, :string # Only if using reconfirmable add_index :users, :confirmation_token, unique: true end end
メール呼び出し制御
unconfirmed_email
に関してはメールアドレス変更時に、確認メールを送って、そのリンクを踏んだ場合に変更になる仕様の場合のみ追加すればokです。
# config/initializer/devise.rb config.reconfirmable = true
もうこれだけで,
– 新規登録時に確認メールを送信して、リンクを踏んだ場合
– メールアドレス変更時に確認メールを送信して、リンクを踏んだ場合だけ有効
になります。
新規登録時のメールだけ送りたくない場合はskip_confirmation!
をsaveメソッドの前にユーザーオブジェクトに対して呼んでください。
メールアドレス変更時に確認メールを送信をスキップしたい場合は
`skip_reconfirmation’です。
内部的な仕組み
仕組みとしては宣言したモデルのメールアドレスが変更になったのを検知して、
上記追加したカラムにupdateが走ります。
ここで変更する予定のメールアドレスがunconfirmed_emailに一時的に格納されます。
その後mailerが呼ばれます。
SQL (0.5ms) UPDATE "users" SET "updated_at" = ?, "confirmation_token" = ?, "confirmation_sent_at" = ?, "unconfirmed_email" = ? WHERE "users"."id" = ? [["updated_at", 2016-12-13 06:43:56 UTC], ["confirmation_token", "CDk43QgXWYxoUqs4ubU4"], ["confirmation_sent_at", 2016-12-13 06:43:56 UTC], ["unconfirmed_email", "change@email.jp"], ["id", 5]] (1.6ms) commit transaction Rendering users/mailer/confirmation_instructions.text.erb Rendered users/mailer/confirmation_instructions.text.erb (0.7ms) Devise::Mailer#confirmation_instructions: processed outbound mail in 43.2ms
そしてメールが飛び、そこにリンクがあります。
リンクにtokenが埋め込まれているので、そのトークンをもとにユーザーをテーブルから呼び出します。
かんたんなコードにするとこんな感じ。
そしてemailカラムにunconfirmed_emailを代入して、その後nilにする処理がdeviseのconfirm
メソッドで行われます。
@user = User.find_by(confirmation_token: params[:confirmation_token])
Started GET "/users/confirmation?confirmation_token=CDk43QgXWYxoUqs4ubU4" for ::1 at 2016-12-13 15:46:21 +0900 Processing by Users::ConfirmationsController#show as HTML Parameters: {"confirmation_token"=>"CDk43QgXWYxoUqs4ubU4"} User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."confirmation_token" = ? ORDER BY "users"."id" ASC LIMIT ? [["confirmation_token", "CDk43QgXWYxoUqs4ubU4"], ["LIMIT", 1]] (0.1ms) begin transaction SQL (0.3ms) UPDATE "users" SET "confirmed_at" = ?, "unconfirmed_email" = ?, "email" = ?, "updated_at" = ? WHERE "users"."id" = ? [["confirmed_at", 2016-12-13 06:46:21 UTC], ["unconfirmed_email", nil], ["email", "change@email.jp"], ["updated_at", 2016-12-13 06:46:21 UTC], ["id", 5]]
def confirm(args={}) pending_any_confirmation do if confirmation_period_expired? self.errors.add(:email, :confirmation_period_expired, period: Devise::TimeInflector.time_ago_in_words(self.class.confirm_within.ago)) return false end self.confirmed_at = Time.now.utc saved = if pending_reconfirmation? skip_reconfirmation! self.email = unconfirmed_email self.unconfirmed_email = nil # We need to validate in such cases to enforce e-mail uniqueness save(validate: true) else save(validate: args[:ensure_valid] == true) end after_confirmation if saved saved end end
コメントを残す