除非选项评估为true,否则Rails验证仍在触发

时间:2014-02-10 23:52:37

标签: ruby-on-rails ruby-on-rails-3 validation devise devise-invitable

我在我的应用中使用devise_invitable来允许用户发送邀请。我意识到一个不好的情况,用户被邀请但忽略了邀请,然后返回到应用程序以自行注册。由于devise_invitable通过使用提供的邀请电子邮件地址创建新user来处理邀请,因此我在电子邮件字段中的唯一性验证将导致Rails投诉,告知用户该电子邮件地址已被占用。

我正在尝试编写一些逻辑来处理这种情况。我看到两条路径 - 要么找到一种方法来检测它并破坏先前创建的用户并允许创建新用户,或者检测用户被邀请并执行另一个流程。我已经决定实施第二个选项,因为我希望尽可能使用邀请。

我有限的经验让我质疑我写的内容是否有效,但我实际上无法完全测试它,因为触发了电子邮件上的Rails验证。我确保Devise的:validatable模块处于非活动状态。我创建了一个方法(我认为)会检测用户是否被邀请,在这种情况下应该跳过唯一性验证。

#user.rb
...
validates :email, uniqueness: true, unless: :was_invited?

...
def was_invited?
  if self.invitation_sent_at.present? && self.sign_in_count == 0
    true
  else
    false
  end
end

FWIW,我最初用简写而不是打破if / else,但我想非常明确地找到错误/失败。

希望一旦表单通过验证,create操作就会对用户的邀请状态进行一些检测,如果他们被邀请,则会将其重定向到accept_user_invitation_path。再说一遍,我还没能真正测试这个,因为我无法绕过验证。

#registrations_controller.rb
def create
  if User.find_by_email(params[:email])
    @existing_user = User.find_by_email(params[:email])
    @existing_user.save(validate: false)
    if @existing_user.was_invited?
      redirect_to accept_user_invitation_path(:invitation_token => @existing_user.invitation_token)
    end
  else
    super
  end
end

在绝望的努力中,你会看到我还添加了.save(validate: false)以试图在那里短路,但它甚至没有达到那么远。

如果我完全注释掉电子邮件验证,只是为了测试其余的逻辑/流程,由于电子邮件地址上的索引,我得到PG错误抱怨唯一性 - 我不想将所有这些分开只是为了测试这种方法。

我已经试着把这个弄得好几个小时而且我不知所措 - 任何帮助都表示赞赏。如果您想要查看其他任何代码,请告诉我。

2 个答案:

答案 0 :(得分:2)

查看重定向:

redirect_to accept_user_invitation_path(:invitation_token => @existing_user.invitation_token)

我可以看到没有return这意味着如果调用该重定向,您应该收到AbstractController::DoubleRenderError错误,因为父控制器的create方法应该尝试渲染new视图。

从这里我猜想你用来查找现有用户的查询实际上并没有返回结果,可能是因为你正在使用params[:email]而如果你使用的是默认视图或格式正确的表单应该是params[:user][:email]

答案 1 :(得分:0)

也许你应该给你的控制器更多的责任......

如果找到该用户,请使用该用户,否则请创建一个新用户。假设您的表单显示为http://yourapp/users/new,请将您的路线更改为http://yourapp/users/new/:email,让用户在进入表单之前输入他们的电子邮件。

def new
    @existing_user = User.find_by_email("#{params[:email]}.#{params[:format]}") || User.new
    if @existing_user.was_invited? # will only work for existing user
        redirect_to accept_user_invitation_path(:invitation_token => @existing_user.invitation_token)
    else
        render 'new'
    end
end

def create
    # do maybe something before saving
    if @existing_user.save(user_params)
        # do your magic
    else
        render 'new', notice: "Oops, I didn't save"
    end
end