Rails:devise_invitable,表单验证错误:false:FalseClass 的未定义方法 'errors'

Rails: devise_invitable, form validation errors: undefined method 'errors' for false:FalseClass

我使用 Devise 和 devise_invitable gem 来管理和邀请用户/成员加入我的 Rails 应用程序。我在表单验证方面遇到了一些问题。我的邀请表中有两个字段,一个用于 name,另一个用于 recipient_email名称是可选的。当我单击提交按钮时 - 无论表单是否有效,例如当两个字段都为空时 - 我收到以下错误:

undefined method `errors' for false:FalseClass

错误指向我的 invitations_controller create 操作。此方法如下所示:

def create

  @user = User.find_by(email: invite_params[:email])
  @team = Team.find_by(id: params[:team_id])
  already_invited = false
  # Check if the user already s invited to the team.
  @team.invitations.each do |invitation|
    if @user && invitation.recipient_email.eql?(@user.email)
      already_invited = true
    end
  end

  if already_invited
    redirect_to edit_team_path(@team), notice: "User already invited"
  else
    # The app crashes here
    super
  end
end

我还自定义了这两个devise_invitable方法:after_invite_path_forinvite_resource(&block)。很大程度上基于 this tutorial. 它们看起来像这样:

def after_invite_path_for(resource)
  team = Team.find_by_id(params[:team_id])
  edit_team_path(team)
end

def invite_resource(&block)
  @user = User.find_by(email: invite_params[:email])
  @team = Team.find_by(id: params[:team_id])

  if @user && @user.email != current_user.email
    token = Digest::SHA1.hexdigest([Time.now, rand].join)
    @user.invite_existing_user!(@user, current_user, token)
    member = @team.members.find_by(user_id: current_or_guest_user.id)
    invitation = @team.invitations.build(invitation_token: token, member_id: member.id, recipient_email: invite_params[:email])
    invitation.save
    @user
  else
    @user = resource_class.invite!(invite_params, current_inviter, &block)
    member = @team.members.find_by(user_id: current_or_guest_user.id)
    invitation = @team.invitations.build(invitation_token: @user.raw_invitation_token, member_id: member.id, recipient_email: invite_params[:email])
    invitation.save
  end
end

我有四个相关模型,User(被邀请的实体),Team每个用户可以是 Member(加入 table)多个Team。我还有一个自定义 Invitation 模型,每个 Team 可以有很多邀请,我用它来列出为特定团队发送的邀请。邀请功能以前有效,但当表单无效时我遇到了一些问题,例如当两个字段都为 empty 时。为了解决这个问题,我尝试在 Devise.rb:

中启用它
config.validate_on_invite = true 

在我的 User 模型中有以下内容:

devise [...], :invitable, validate_on_invite: true 

在我的用户模型中我也有这个:

validates_presence_of :name
validates_presence_of :email

在我的自定义 Invitation 模型中,我有这个:

validates_presence_of :recipient_email

我之前遇到的问题是 invite_resource 被调用,尽管输入无效,我会为我的团队创建一个新的自定义 Invitation 资源。

在错误情况下我想要的只是:

  1. 不要创建或发送邀请
  2. 显示错误信息

如何在使用 devise_invitable 时让它工作?

您正在从自定义 invite_resource 实施中返回 false。基本控制器调用此方法并期望返回一个 ActiveRecord 模型实例,然后对其进行检查 errors。但是您要返回 false,因此它会尝试执行 false.errors - 因此您在调用 super.

时会看到问题

见第 27 行:

https://github.com/scambra/devise_invitable/blob/master/app/controllers/devise/invitations_controller.rb#L25

...invite_resource 的默认实现位于:

https://github.com/scambra/devise_invitable/blob/master/app/controllers/devise/invitations_controller.rb#L81

...相对于您的复杂实现:

def invite_resource(&block)
  @user = User.find_by(email: invite_params[:email])
  @team = Team.find_by(id: params[:team_id])

  if @user && @user.email != current_user.email
    token = Digest::SHA1.hexdigest([Time.now, rand].join)
    @user.invite_existing_user!(@user, current_user, token)
    member = @team.members.find_by(user_id: current_or_guest_user.id)
    invitation = @team.invitations.build(invitation_token: token, member_id: member.id, recipient_email: invite_params[:email])
    invitation.save
    @user
  else
    @user = resource_class.invite!(invite_params, current_inviter, &block)
    member = @team.members.find_by(user_id: current_or_guest_user.id)
    invitation = @team.invitations.build(invitation_token: @user.raw_invitation_token, member_id: member.id, recipient_email: invite_params[:email])
    invitation.save
  end
end

请注意,上述 else 案例的计算结果为 invitation.save,对于验证错误,该结果将为 false。您忘记了上述 else 案例中出现的 @user 声明。稍微擦干一下代码,例如如下(风格上这可能不符合每个人的口味):

def invite_resource(&block)
  @user = User.find_by(email: invite_params[:email])
  @team = Team.find_by(id: params[:team_id])

  invitation = if @user && @user.email != current_user.email
    token = Digest::SHA1.hexdigest([Time.now, rand].join)
    @user.invite_existing_user!(@user, current_user, token)
    member = @team.members.find_by(user_id: current_or_guest_user.id)
    @team.invitations.build(invitation_token: token, member_id: member.id, recipient_email: invite_params[:email])
  else
    @user = resource_class.invite!(invite_params, current_inviter, &block)
    member = @team.members.find_by(user_id: current_or_guest_user.id)
    @team.invitations.build(invitation_token: @user.raw_invitation_token, member_id: member.id, recipient_email: invite_params[:email])
  end

  invitation.save
  @user
end

这至少应该修复你的 undefined method 异常,尽管它会忽略 invitation 上的任何验证错误(这里按顺序是 save!,整个内容都包含在 User.transaction do... 以保持所有这些邀请的原子性?)。