设计:将现有访客用户转换为注册用户

时间:2014-03-15 19:30:52

标签: ruby-on-rails devise registration

我正在通过创建访客用户自动将每个新用户登录到站点中,如下所述:

https://github.com/plataformatec/devise/wiki/How-To:-Create-a-guest-user

这允许每个用户在我的网站上使用一些基本内容,例如创建评论。

现在,当用户决定注册时,我想将访客用户转换为注册用户。

我尝试实现我自己的User.new_with_session方法,如下所示:

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

但这不起作用。不知何故,在提交表单时,设计似乎检测到用户已经存在于DB中,因此甚至不尝试对其进行验证等,而只是退出注册过程(因为它在考虑用户)已经登录并转发到其他某个页面,这会导致“您需要在继续之前登录或注册”。 flash消息。

据我所知,这种情况发生在之前 Devise::RegistrationsController#create甚至被调用。

所以这是我的问题:有没有办法告诉Devise不要认为用户登录只是因为用户已经存在于DB中(尽管我不知道如何设计在提交注册表后了解用户)?

更新

我可以进一步跟踪问题。每当self.new_with_session返回持久记录时,注册过程就会被取消,如上所述。返回新对象时,注册继续。但我无法弄清楚这在哪里检查......!

2 个答案:

答案 0 :(得分:2)

目前,我坚持使用以下解决方案。

我没有尝试在注册过程中将访客用户转换为注册用户,而是让Devise发挥其魔力,然后 注册完成后,使用after挂钩,我立即删除新注册的用户,然后我将访客用户转换为注册用户,将通过用户表单提供的属性分配给前访客并签名。

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

然后在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

通过使用update_column我确保没有触发前/后挂钩,否则会发生不可预测的事情,例如:确认邮件将被重新发送(设置确认令牌时),等等。

请记住,任何相关记录都是自动从创建的@user对象传输到现有@current_user,因此如果您创建任何相关对象(例如, after挂钩),你必须手动移动它们,这又是一件非常困难的事情。所以我认为最好避免这样的事情。

如果有人有更好的解决方案,请告诉我。此外,我不确定这是否会留下一些未在背景中整理的设计内容。

答案 1 :(得分:2)

我重写了RegistrationsController创建方法,如下所示:

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

我只是找到用户并更新它们。 Devise还有一个帮助器,它将sign_in方法与重定向结合起来。

如果没有找到用户,则继续正常进行。