Thanks for answering my previous question, but I ran into a new problem.
I'm creating a custom validator that validates whether a user typed in a clean word. This is used on my UsersController as a validation method.
I am using the Obscenity gem but created some of my own methods to ensure quality data.
Error Message
NoMethodError: Undefined method include? for Nil:NilClass
The problem with this is that my methods work if a record already exists, but they don't work during record creation. I've tried to combat this problem by using
:on => [:create, :update]
but I still receive the same error.
Validation 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 an unless 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 by using the unless statement but that didn't work either.
Following advice from a similar question here, I changed my migrations 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 validation of wizard gem
Reference for Code
http://guides.rubyonrails.org/active_record_validations.html#performing-custom-validations
Changing the migration file by changing the default values didn't solve this question so I decided to ask a new question here.
This method works for updating records but not for creating new records.
Help is appreciated. Thank you in advanced.
Edit
Just received an excellent suggestion to pass in the attributes in bracket format. My code now looks like this
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
Right now, it only has an error with the email and password attributes. If I delete the last two ||@new_mysteries.any? lines, my method works for filtering the name.
I would like to keep this professional though, so I'd like to get it to work with the other two methods. Possibly has to do with my use of parentheses or the || symbol?
Solid progress guys, keep it up.
Edit
Also, if I would like to call these validation methods on other classes, would it be better to put this in a helper file?
New Update
Here is my Users 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
You could write that 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 wished to reduce the number of instance variables, you could change the first line to:
new_mysteries = mystery_setup
and change @new_mysteries
to new_mysteries
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With