问题
I'm logging every new user automatically into the site by creating a guest user as described here:
https://github.com/plataformatec/devise/wiki/How-To:-Create-a-guest-user
This allows every user to use some basic stuff on my site, e.g. creating comments.
Now, when the user decides to register, I want to convert the guest user into a registered user.
I tried to implement my own User.new_with_session method like this:
def self.new_with_session(params, session)
user = User.find(session[:guest_user_id])
user.name = params[:name]
user.email = params[:email]
user.password = params[:password]
user.password_confirmation = params[:password_confirmation]
user.created_at = nil
user.updated_at = nil
user.confirmed_at = nil
user.guest = false
user
rescue ActiveRecord::RecordNotFound
super
end
But this doesn't work. Somehow, when submitting the form, devise somehow seems to detect that the user already exists in the DB, and so doesn't even try to do validations on it, etc., but simply exits the sign up process (because it's thinking the user is already signed in) and forwards to some other page, which results in a "You need to sign in or sign up before continuing." flash message.
As far as I can tell, this happens somewhere before Devise::RegistrationsController#create is even called.
So here's my question: is there a way to tell Devise not to think a user is logged in simply because the user already exists in the DB (although I don't have an idea how Devise knows about the user after submitting the registration form)?
Update
I could track the problem down a bit further. Whenever self.new_with_session returns a persisted record, the registration process is cancelled, as described above. When returning a new object, the registration continues. But I can't figure out where this is checked upon...!?
回答1:
For the moment, I stick with the following solution.
Instead of trying to convert the guest user into a registered one during the registration process, I let Devise do its magic and then after the registration completes, using an after hook, I immediately remove the newly registered user, then I convert the guest user to a registered one, assign the attributes provided through the user form to the former guest and sign it in.
class RegistrationsController < Devise::RegistrationsController
after_filter :consolidate_registered_user_with_guest, only: :create,
if: -> { @user.valid? }
def consolidate_registered_user_with_guest
@current_user.annex_and_destroy!(@user)
sign_in @current_user
end
end
Then in User.rb:
def annex_and_destroy!(other)
transaction do
skip_confirmation_notification!
other.delete
other.attributes.except("id").each do |attribute, value|
update_column attribute, value
end
self.save!
end
self
end
By using update_column I make sure that no before/after hooks are triggered, otherwise unpredictable things would happen, e.g. the confirmation mail would be re-sent (when setting the confirmation token), etc.
Keep in mind that any related records are not transferred from the created @user object to the existing @current_user automatically, so if you create any related objects (e.g. in an after hook), you have to manually move them, which is again a quite difficult thing. So I think it's best to avoid such a thing.
If anyone has a better solution, please tell me. Also I'm unsure whether this leaves some Devise stuff not tidied up in the background.
回答2:
I'm overriding the RegistrationsController create method like so:
class RegistrationsController < Devise::RegistrationsController
def create
if user = User.find_by_email params[:user][:email]
user.update_attributes params[:user]
sign_in_and_redirect user, bypass: true
else
super
end
end
end
I'm just finding the user and updating them. Devise also has a helper that combines the sign_in method with a redirect.
If no user is found then carry on as normal.
来源:https://stackoverflow.com/questions/22428680/devise-convert-an-existing-guest-user-to-a-registered-user