Undefined method includes? for Nil: NilClass
Thanks for answering my previous question, but I am facing a new problem.
I am creating a custom validator that checks if the user enters a blank word. This one is used on my UserController as a validation method.
I am using the Obscenity gem, but created some of my own methods to provide quality data.
Error message
NoMethodError: Undefined method include? for Nil:NilClass
The problem is that my methods work if the record already exists, but they don't work during the creation of the record. I tried to fight this problem using
:on => [:create, :update]
but I still get the same error.
Verification methods
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 (model)
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
I have also tried using the if statement
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
I tried testing if there was a user with an if statement, but that didn't work either.
Following advice from a similar question, I modified the migration file to combat this area.
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
Question link
undefined method` include? 'for nil: NilClass with partial master gem check
Link for code
http://guides.rubyonrails.org/active_record_validations.html#performing-custom-validations
Changing the migration file by changing the defaults did not resolve the issue, so I decided to post a new question here.
This method works for updating records, but not for creating new records.
Help is appreciated. Thank you for your promotion.
Edit
Just got a great suggestion to pass attributes in parenthesis format. Now my code looks like
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
It currently only has a bug with email and password attributes. If I delete the last two files: \ @ new_mysteries.any? lines, my method works to filter the name.
I would like to keep this professional, so I would like to make it work with two other methods. Perhaps it has something to do with my use of parentheses or || symbol?
Successful guys, keep going.
Edit
Also, if I wanted to name these validation methods for other classes, would it be better to put that in a helper file?
New update
Here is my user controller code
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
source to share
You can write like this:
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
Thanks to @Arup for the fix.
If you want to reduce the number of instance variables, you can change the first line to:
new_mysteries = mystery_setup
and change @new_mysteries
to new_mysteries
.
source to share
|| @new_mysteries.any?{|mystery|user.name.include?(mystery)} \
|| @new_mysteries.any?{|mystery|user.email.include?(mystery)} \
|| @new_mysteries.any?{|mystery|user.password.include?(mystery)}
This error means that the username, email address, or password is zero. To handle this, you need to change each line like this:
user.name && user.name.include?(mystery)
However, we highly recommend a andand
gem that allows you to write the above:
user.name.andand.include?(mystery)
source to share
try it:
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
source to share
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
source to share
I edited the code a bit, let me know if this works:
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
source to share