Devise for Rails - Validate username as email - ruby-on-rails

I followed this guide to use Username and Email in devise authentication, but when I try to create a new user, ie "newusername" with email "new#username.es" I get the validation error "You have to include an # symbol in your email" but refering to username field.
I tried to use a format validation in the user model, but it doesn't solved anything
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :authentication_keys => [:login]
attr_accessor :login
validates_uniqueness_of :username
validates_presence_of :username
validates :username, length: { in: 4..20 }
validates_format_of :username, :with => /\A[-a-z]+\Z/
##### and also
validates :username,
:presence => true,
:uniqueness => { :case_sensitive => false },
:format => { with: /\A[a-zA-Z]+\z/ },
:length => { in: 4..20 }
I couldn't find in Google anyone else that had the same issue.
I'll appreciate your help.

Related

Before validation method breaking spec with 'undefined method downcase'

In my User model I have a before_validation method called normalize_params that uses downcase.
class User < ApplicationRecord
before_validation :normalize_params, on: [:create, :update]
validates :email, :presence => true
validates :email, :uniqueness => true
validates :username, :presence => true
validates :username, :uniqueness => true
validates_confirmation_of :password
scope :all_users_except, -> (current_user) {
where.not(id: current_user)
}
has_attached_file :avatar, styles: { medium: "300x300>", thumb:
"100x100>" }, default_url: "/images/missing.png"
validates_attachment_content_type :avatar, content_type:
/\Aimage\/.*\z/
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
private
def normalize_params
self.name = name.downcase.titleize
self.email = email.downcase
self.home = home.downcase.titleize
end
end
All of that works perfectly in my app but my tests break when they hit the downcase with this error...
NoMethodError:
undefined method `downcase' for nil:NilClass
Here are my tests...
require 'rails_helper'
describe User, 'validation' do
it { should validate_presence_of :email }
it { should validate_presence_of :username }
it { should validate_presence_of :password }
end
If I take before_validation and normalize_params out then my tests pass.
As per the documentation exemplifies, you could use attribute_present? before:
class User < ApplicationRecord
before_validation :normalize_params, on: %i[create update]
validates :email, presence: true, uniqueness: true
validates :username, presence: true, uniqueness: true
private
def normalize_params
titleize_name
downcase_email
# You can any other here.
end
def titleize_name
self.name = name.downcase.titleize if attribute_present? 'name'
end
def downcase_email
self.email = email.downcase if attribute_present? 'email'
end
end
Note you can:
Use %i[] to create an array of symbols.
Validates presence and uniqueness by separating them by comma.
Prefer the is_expected.to than should syntax (it { is_expected.to validate_presence_of :attribute })
Separate each of your attribute modifications to easily work and include them.
Avoid using hash rocket when not needed. See ruby-style-guide#hash-literals.
Either one of name, email or home may be nil. I'd recommend using the safe navigation operator:
def normalize_params
self.name = name&.downcase&.titleize
self.email = email&.downcase
self.home = home&.downcase&.titleize
end

Rails Validation Triggers on resetting password

Here is my model
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
has_many :events
has_many :appointments
validates :name, presence: true
validates :name, format: {with: /\A[[:alnum:]]+\z/, message: 'solo se permiten letras y/o numeros' }, if: 'name.present?'
validates :lastname, format: {with: /\A[[:alnum:]]+\z/, message: 'solo se permiten letras y/o numeros' }, if: 'lastname.present?'
validates :lastname, :presence => true
validates :document, :presence => true
validates_numericality_of :document, :on => :create, :message => "no es un numero", if: 'document.present?'
validates :cellphone, :presence => true
validates :cellphone, numericality:{ only_integer: true, message:"no es un numero"}, if: 'cellphone.present?', :on => :create
validates :cellphone, numericality:{ only_integer: true, message:"no es un numero"}, if: 'cellphone.present?', :on => :update
validates :cellphone, format: { with: /\d{11}/, message: "mal formato, deben ser 11 digitos, incluyendo codigo de area" }, if: "cellphone.present?", :on => :create
validates :cellphone, format: { with: /\d{11}/, message: "mal formato, deben ser 11 digitos, incluyendo codigo de area" }, if: "cellphone.present?", :on => :update
validates :phone, :presence => true
validates :phone, numericality:{ only_integer: true, message:"no es un numero"}, if: "phone.present?", :on => :create
validates :phone, format: { with: /\d{11}/, message: "mal formato, deben ser 11 digitos, incluyendo codigo de area" }, if: "phone.present?", :on => :create
validates_format_of :email,:with => Devise::email_regexp, :allow_blank => true
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
def medic_with_spec
"#{especialidad}, #{name} #{lastname}"
end
def evento_sin
events.where(available: "1")
end
end
as you can see the validation :cellphone has :on => create and :on => update, so my problem is that when i reset my password from an email link and press update with a new password it shows cellphone validations errors since it has :on => update. How can i make this dissappear?
I tried if: 'cellphone.nil?', it works fine because cellphone is obvioulsy blank (reset password only requires password and password confirmation) but when i go to edit account information it wont validate a none-nil value.
Im using devise.
I read this link Validation errors are triggered when I'm trying to reset password , its the exact same problem, but i have no idea how to implement what they said.
Im new on ruby on rails, if some1 can help me with this i have no idea how to implements advance ruby.
you should disable validation if password_confirmation field is not nil (that means that password was changed). Here's a linked topic Skip validation for some members in Devise model during password reset
So, in your case that would be:
validates :cellphone, numericality:{ only_integer: true, message:"no es un numero"}, :if => :not_recovering_password, :on => :update
def not_recovering_password
password_confirmation.nil?
end

Rails: Simple-Form & Devise: mark email field of user model as required in forms automatically?

I wonder why the field email is not marked as a required field in Simple-Form, as when submitting it empty there is a validation error "can't be blank".
It seems that the validation rules for the email field come from Devise, so they are available to the validation mechanism, but not to Simple-Form. Why's that?
I could simply add another validates :email, presence: true to my User model, but this seems overkill. Or I could add a required: true to the f.input :email method of Simple-Form, but this seems like overkill, too.
Here's the relevant part of my User model:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:confirmable, :lockable, authentication_keys: [:login]
validates :name, presence: true
Do I have something configured incorrect/incomplete?
From Simple Form's README:
For performance reasons, this detection is skipped on validations that
make use of conditional options, such as :if and :unless.
And you can see that Devise will add validation with an :if in https://github.com/plataformatec/devise/blob/master/lib/devise/models/validatable.rb
base.class_eval do
validates_presence_of :email, if: :email_required?
validates_uniqueness_of :email, allow_blank: true, if: :email_changed?
validates_format_of :email, with: email_regexp, allow_blank: true, if: :email_changed?
validates_presence_of :password, if: :password_required?
validates_confirmation_of :password, if: :password_required?
validates_length_of :password, within: password_length, allow_blank: true
end
So you have to mark the field as required in your views.
If u submit it blank it will show ask u to fill in the field,
if you submit it a wrong email is will ask for a valid email.
So it check for blank first, if not blank then it checks if valid.
Your form is doing just fine.

Devise Username with spaces login

I have the following devise model (edited for brevity)
class Student < ActiveRecord::Base
devise :database_authenticatable, :token_authenticatable,
:recoverable, :rememberable, :trackable,
:authentication_keys => [:login], :reset_password_keys => [ :login ]
attr_accessor :login
attr_accessible :name, :email, :password, :password_confirmation, :login
validates :name, :presence => true
validates :email, :presence => true
validates_uniqueness_of :email, :case_sensitive => false, :allow_blank => true, :if => :email_changed?, :scope => :id
validates_format_of :email, :with => Devise.email_regexp, :allow_blank => true, :if => :email_changed?
validates_presence_of :password, :on=>:create
validates_confirmation_of :password, :on=>:create
validates_length_of :password, :within => Devise.password_length, :allow_blank => true
def self.find_first_by_auth_conditions(warden_conditions)
conditions = warden_conditions.dup
if login = conditions.delete(:login)
where(conditions).where(["name = :value OR lower(email) = :value", { :value => login.downcase }]).first
else
where(conditions).first
end
end
def email_required?
false
end
def email_changed?
false
end
end
And I have followed the guide on this link to enable signin with either name or email.
Here's the problem:
Students with names > 2 words cant sign in, e.g. "Adam Bravo Charlie"
Single names e.g "Adam" or names with <= 2 words e.g "Adam Bravo" can be signed in.
The school requires students to use their full names with accompanying spaces to login. How do I enable my students to sign in with their full names and whitespaces?
On the login form, I would take a string and convert it to a slug. Authenticate with the slug as the username. When creating a new user, use the before_create method and parameterize the full name. This would allow you to have an unlimited number of spaces, including punctuation characters (periods and commas). This would give the feel of the user entering their full name as their username when in turn, it's going to parameterize the user's full name and use this as the username to login.
Ok turns out the solution is simpler than i thought.
config/initializers/devise.rb
config.case_insensitive_keys = [ :name]
config.strip_whitespace_keys = [ :name ]
and voila, login with full name is now possible!

Devise before_save filter not applying

Before my User's can register I need to authenticate them via api first to see if their information is valid. Anyhow I have my validate_api method working as it needs to be I have tested this however, I'm not sure as to why when i try to register with a faulty api it still saves the user.
I put my method in a controller and called it with a valid api and it returned true and then with faulty api and it returned false.
So if the method is working it's either being ignored or something overrides it.
My User model
class User < ActiveRecord::Base
attr_accessor :login
before_save :validate_api
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :authentication_keys => [:login]
validates :username, :presence => true, :length => { :minimum => 6, :maximum => 255 }
validates :apiid, :presence => true, :numericality => { :only_integer => true }
validates :vcode, :presence => true, :length => { :minimum => 20, :maximum => 255 }
# Setup accessible (or protected) attributes for your model
attr_accessible :login, :username, :group, :apiid, :vcode, :email, :password, :password_confirmation, :remember_me
# Check if user is banned before login
def active_for_authentication?
super && self.banned == 0
end
# Redefine authentication procedure to allow login with username or email
def self.find_for_database_authentication(warden_conditions)
conditions = warden_conditions.dup
if login = conditions.delete(:login).downcase
#where(conditions).where('$or' => [ {:username => /^#{Regexp.escape(login)}$/i}, {:email => /^#{Regexp.escape(login)}$/i} ]).first
where(conditions).where("username = '#{login}' OR email = '#{login}'").first
else
where(conditions).first
end
end
# Validate API information
private
def validate_api
require 'nokogiri'
require 'open-uri'
uri = "https://*******?keyID=#{self.apiid}&vCode=#{self.vcode}"
xml = Nokogiri::XML(open(uri))
xml.xpath("//row").each do |row|
if row['****'].downcase == '****'
return true
else
return false
end
end
end
end
Instead of using before_save :validate_api you should be using validate :check_api, and then adding an error message (eg: errors[:apiid] << "must be a valid API id.") if the api check fails.

Resources