未定义的方法包括?对于Nil:NilClass

时间:2014-08-05 16:30:35

标签: ruby-on-rails ruby nomethoderror

感谢您回答我之前提出的问题,但我遇到了一个新问题。

我正在创建一个自定义验证程序,用于验证用户是否输入了干净的单词。这个 在我的UsersController上用作验证方法。

我使用的是Obsc​​enity gem,但我创建了一些自己的方法来确保质量数据。

错误消息

NoMethodError: Undefined method include? for Nil:NilClass

问题在于,如果记录已经存在,我的方法可以工作,但是在创建记录期间它们不起作用。我尝试使用

来解决这个问题
:on => [:create, :update]

但我仍然收到同样的错误。

验证方法

class MyValidator < ActiveModel::Validator

  def mystery_setup
    @mystery_words = # This is a mystery, I can't tell you.
    @mystery_c = @mystery_words.map(&:capitalize)
    @mystery_u = @mystery_words.map(&:upcase)
    @mysteries = @mystery_words + @mystery_c + @mystery_u
    @new_mysteries = @mysteries.map{|mystery|mystery.tr("A-Za-z", "N-ZA-Mn-za-m")}
 end

  def validate (user)
  mystery_setup
    if Obscenity.profane?(user.name) \ 
    || @new_mysteries.any?{|mystery|user.name.include?(mystery)} \
    || @new_mysteries.any?{|mystery|user.email.include?(mystery)} \
    || @new_mysteries.any?{|mystery|user.password.include?(mystery)}
      user.errors[:name] << 'Error: Please select a different username'
    end
  end
end

User.rb(型号)

class User < ActiveRecord::Base

  include ActiveModel::Validations
  validates_with MyValidator

  has_many :favorites, foreign_key: "user_id", dependent: :destroy
  has_many :pictures, through: :favorites

  has_secure_password
  before_create :create_remember_token

  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i

  validates_presence_of :name, :password, :email
  validates_uniqueness_of :name, :email
  validates :name, length: { in: 3..20 }
  validates :password, length: { minimum: 6 }
  validates :email, format: { with: VALID_EMAIL_REGEX }, length: { in: 8..50 }

  validates_confirmation_of :password, if: lambda { |m| m.password.present? }

  def User.new_remember_token
    SecureRandom.urlsafe_base64
  end

  def User.digest(token)
    Digest::SHA1.hexdigest(token.to_s)
  end

  private

  def create_remember_token
    self.remember_token = User.digest(User.new_remember_token)
  end

end

我也尝试过使用except语句

def validate (user)
  mystery_setup
  unless User.all.include?(user)
    if (Obscenity.profane?(user.name) 
    || @new_mysteries.any {|mystery|user.name.include?(mystery)})  \
    || @new_mysteries.any?{|mystery|user.email.include?(mystery)} \
    ||   @new_mysteries.any?{|mystery|user.password.include?(mystery)}
        user.errors[:name] << 'Error: Please select a different username'
      end
    end
  end
end

我尝试使用unless语句测试是否有用户,但是也没有。

根据类似问题的建议,我更改了我的迁移文件以对抗此区域。

class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.string :name, default: 'new'
      t.string :password, default: 'new'
      t.string :email, default: 'new'

      t.timestamps
    end
  end
end

问题链接

undefined method `include?' for nil:NilClass with partial validation of wizard gem

参考代码

http://guides.rubyonrails.org/active_record_validations.html#performing-custom-validations

通过更改默认值来更改迁移文件并没有解决这个问题所以我决定在这里提出一个新问题。

此方法适用于更新记录,但不适用于创建新记录。

帮助表示赞赏。先谢谢你。

修改

刚刚收到了一个很好的建议,即以括号格式传递属性。我的代码现在看起来像这样

def validate (user)
    mystery_setup
    unless User.all.include?(user)
      if (Obscenity.profane?(user[:name]) || 
        @new_mysteries.any?{|mystery|user[:name].include?(mystery)})  \
      ||@new_mysteries.any?{|mystery|user[:email].include?(mystery)}
      ||@new_mysteries.any?{|mystery|user[:password].include?(mystery)}
        user.errors[:name] << 'Error: Please select a different username'
      end
    end
end

目前,它只有电子邮件和密码属性的错误。如果我删除最后两个||@new_mysteries.any?我的方法用于过滤名称。

我想保持这种专业性,所以我想让它与其他两种方法一起使用。可能与我使用括号或||有关符号

坚定的进步人员,坚持下去。

修改

另外,如果我想在其他类上调用这些验证方法,将它放在帮助文件中会更好吗?

新更新

这是我的用户控制器代码

class UsersController < ApplicationController
  before_action :signed_in_user, only: [:edit, :update, :destroy]
  before_action :correct_user, only: [:edit, :update]

  def index
    @users = User.all
  end

  def show
    @user = User.find(params[:id])
  end

  def new
    @user = User.new
  end

  def create
    @user = User.new(user_params)
    if @user.save
      flash[:success] = "Congratulations #{@user.name}! You have successfully created an account"
      redirect_to games_path
    else
      render 'new'
    end
  end

  def edit
  end

  def update
    @user = User.find(params[:id])
  end

  def favorites
    @user = User.find(current_user)
  end

  def destroy
  end

  private

    def user_params
     params.require(:user).permit(:name, :email, :password, :password_confirmation)
   end

   def signed_in_user
     unless signed_in?
       store_location
       redirect_to signin_url notice: "Please sign in."
     end
   end

   def correct_user
     @user = User.find(params[:id])
     redirect_to(root_url) unless current_user?(@user)
   end
end

7 个答案:

答案 0 :(得分:4)

|| @new_mysteries.any?{|mystery|user.name.include?(mystery)} \
|| @new_mysteries.any?{|mystery|user.email.include?(mystery)} \
|| @new_mysteries.any?{|mystery|user.password.include?(mystery)}

此错误表示用户名,电子邮件或密码为零。要处理它,您需要将每一行更改为:

user.name && user.name.include?(mystery)

然而强烈推荐andand gem,这将允许您将上述内容写成:

user.name.andand.include?(mystery)

答案 1 :(得分:4)

你可以这样写:

def validate (user)
  mystery_setup
  user.errors[:name] << 'Tsk! Tsk! Please select a different username' if
    Obscenity.profane?(user[:name]) ||
      [:name, :email, :password].product(@new_mysteries).any? { |sym, mystery|
        (str = user.public_send sym) && str.include?(mystery) }
end

感谢@Arup的修复。

如果您希望减少实例变量的数量,可以将第一行更改为:

new_mysteries = mystery_setup

并将@new_mysteries更改为new_mysteries

答案 2 :(得分:2)

试试这个:

def validate (user)
  mystery_setup
  unless User.all.include?(user)
    if user.name && user.email && user.password
      if (Obscenity.profane?(user.name)  
      || @new_mysteries.any {|mystery|user.name.include?(mystery)})  \
      || @new_mysteries.any?{|mystery|user.email.include?(mystery)} \
      || @new_mysteries.any?{|mystery|user.password.include?(mystery)}
          user.errors[:name] << 'Error: Please select a different username'
        end
      end
    end
  end
end

答案 3 :(得分:2)

class MyValidator < ActiveModel::Validator

  def mystery_setup
    mystery_words = # This is a mystery, I can't tell you.
    mystery_c = mystery_words.map(&:capitalize)
    mystery_u = mystery_words.map(&:upcase)
    mysteries = mystery_words + mystery_c + mystery_u
    mysteries.map{ |mystery| mystery.tr("A-Za-z", "N-ZA-Mn-za-m")}
 end

  def validate (user)
    # No need to pollute the class with instance variables, just pass it back in a return
    new_mysteries = mystery_setup

    if Obscenity.profane?(user.name.to_s) || 
       @new_mysteries.any?{ |mystery| user.name.to_s.include?(mystery) ||
                                      user.email.to_s.include?(mystery) ||
                                      user.password.to_s.include?(mystery)}
      user.errors[:name] << 'Error: Please select a different username'
    end
  end
end

答案 4 :(得分:2)

我已经重构了一下代码,请告诉我这是否有效:

def validate (user)
  mystery_setup

  if Obscenity.profane?(user.name)
    user.errors[:name] << 'Error: Please select a different username'
  end

  %w(name email password).each do |attr|
    value = user.send(attr)
    if value.present? and @new_mysteries.grep(/#{value}/).present?
      user.errors[attr] << "Error: Please select a different user#{attr}"
    end
  end
end

答案 5 :(得分:2)

这部分是第一行的错误。

@mystery_words = # This is a mystery, I can't tell you.
@mystery_c = mystery_words.map(&:capitalize)

这应该是

@mystery_words = [] # This is a mystery, I can't tell you.
@mystery_c = mystery_words.map(&:capitalize)

答案 6 :(得分:0)

由于nil是Ruby中原子虚无的表示,因此它从不包含任何内容。 include?方法可以简单地定义为:

def nil.include? arg
  false
end