rails email validation only if present - ruby-on-rails

In my user model, I have the field email, not required.
I want to validate the field, if and only if it's present.
It can be blank but if it's present should be validated.
Well, how it is now, it's never validated.
If I remove the unless condition it is always validated (normally)
class User < ActiveRecord::
validates :email,
format: { with: /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i },
uniqueness: true,
unless: lambda { email.nil? }
Any suggestion?

if you are using devise,then you dont need to wory about it...if not then you can use the below code in the model:-
EMAIL_REGEX = /^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i
validates :email, :presence => true, :uniqueness => true, :format => EMAIL_REGEX,unless: lambda { self.email.blank? }

Probably because it's not nil, but an empty string. Use allow_blank instead:
validates :email,
format: { with: /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i },
uniqueness: true,
allow_blank: true

Related

how to create Proc to prevent DRY in rails model? Rails 5.2.1, rails_admin

class Client < ApplicationRecord
has_many :projects
validates :name, presence: true
validates :phone,
presence: {
message: "Phone or Email can not be blank",
if: Proc.new { |a| a.email.blank? }
},
length: {
minimum: 10,
unless: Proc.new { |a| a.phone.blank? }
}
validates :email,
uniqueness: {
unless: Proc.new { |a| a.email.blank? }
},
presence: {
message: "Phone/Email can't both be blank",
if: Proc.new { |a| a.phone.blank? }
},
format: {
with: URI::MailTo::EMAIL_REGEXP,
unless: Proc.new { |a| a.email.blank? }
}
def phone_blank?
Proc.new { |a| a.phone.blank? }
end
end
How do I create a method to replace with all the Proc?
I just learned about Proc and I'm not too familiar with that yet. I tried to use :phone_blank to replace all the proc after if:/unless:, but it failed to work. Can someone tell me how to make the phone_blank? method work to replace all the proc embeded in the code? thanks~
edited:
I forgot to mention I'm using rails_admin for the admin interface. If I call methods in if:/unless:, the admin panel will show Model 'Client' could not be found then the model would disappear from the admin panel. I'm not sure it's a rails_admin thing or that's how Rails 5 behaves. I'm quite new to RoR and still quite confuse with all different versions of Rails....
For using method there is no need in Proc wrapper.
e.g.
class Client < ApplicationRecord
has_many :projects
validates :name, presence: true
validates :phone,
presence: {
message: "Phone or Email can not be blank",
if: email_blank?
},
length: {
minimum: 10,
unless: phone_blank?
}
validates :email,
uniqueness: {
unless: email_blank?
},
presence: {
message: "Phone/Email can't both be blank",
if: phone_blank?
},
format: {
with: URI::MailTo::EMAIL_REGEXP,
unless: email_blank?
}
def phone_blank?
phone.blank?
end
def email_blank?
email.blank?
end
end
Also you can simply specify this condition in validation directly without method or Proc as a string.
e.g.
class Client < ApplicationRecord
has_many :projects
validates :name, presence: true
validates :phone,
presence: {
message: "Phone or Email can not be blank",
if: 'email.blank?'
},
length: {
minimum: 10,
if: 'phone.present?'
}
validates :email,
uniqueness: {
if: 'email.present?'
},
presence: {
message: "Phone/Email can't both be blank",
if: 'phone.blank?'
},
format: {
with: URI::MailTo::EMAIL_REGEXP,
if: 'email.present?'
}
end
You could write a class method that returns a lambda, something like:
def self.blank_field?(field)
->(m) { m.send(field).blank? }
end
and then say things like:
validates :phone,
presence: {
message: "Phone or Email can not be blank",
if: blank_field?(:email)
},
length: {
minimum: 10,
unless: blank_field?(:phone)
}
Note that we use blank_field? instead of blank? since blank? is already taken and we don't want to override it. And since this is an "internal" method, we don't have to worry about public_send versus send.
Not a direct answer, but an alternative approach in DRY-ing things is to make use of with_options:
with_options if: -> { email.blank? } do
validates :phone, presence: { message: "Phone or Email can not be blank" }
end
with_options if: -> { phone.blank? } do
validates :email, presence: { message: "Phone/Email can't both be blank" }
end
with_options if: -> { email.present? } do
validates :phone, length: { minimum: 10 }
validates :email, uniqueness: true, format: { with: URI::MailTo::EMAIL_REGEXP }
end
This is especially useful when the validations have conditions depending on different... say, categories (if you have a category column), and you can just simply group these validations up with_options
Trivia:
You can think of -> { ... } just like Proc.new { ... } which you were already familiar with (though accurately speaking it's a lambda ... which is like a special type of Proc. If you're interested further, see these SO posts: HERE and HERE

If else in validates in rails

In model I want something like below
validates :category, :presence => true if self.post = "this_is_post"
Is it possible or I have to use a hook method before save for this checking?
This should work:
validates :category, :presence => true, :if => ->(a){ a.post == 'this_is_post' }
Here, more than one code snippest for validate condition in rails:
class Person < ActiveRecord::Base
validates :surname, presence: true, if: "name.nil?"
end
========================
validates :category,
:presence => true,
:if => :valid?
def valid?
self.post == "this_is_post"
end
================
class Person < ActiveRecord::Base
validates :category, presence: true, if: "self.post.eql?('this_is_post')"
end

Issues with validation in rails

I have a working validation on my user model. But since we're discovered that users tend to give up during their long registration form, we've split up registration form into steps so that user can give up any time and still use the site.
I'm having issues with the first step of user registration with validations. Here is the relevant bit of my User model :
with_options :if => lambda { |u| u.current_step == "get_started" } do |user|
user.validates :firstname, presence: true, length: { maximum: 50 }
user.validates :surname, presence: true, length: { maximum: 50 }
user.validates :email, presence: true,
format: { with: VALID_EMAIL_REGEX }
user.validates :password_digest, presence: true
user.validates :password, length: { minimum: 6 }
end
This validation works perfectly. However we now added facebook so its possible to login with facebook and we have hidden fields in our form facebook_uid. So I want to validate password only if these fields are nil.
Here is how I tried to modify my current validation :
with_options :if => lambda { |u| u.current_step == "get_started" } do |user|
user.validates :firstname, presence: true, length: { maximum: 50 }
user.validates :surname, presence: true, length: { maximum: 50 }
user.validates :email, presence: true,
format: { with: VALID_EMAIL_REGEX }
with_options :if => lambda { |u| u.facebook_uid.nil? } do |user|
user.validates :password_digest, presence: true
end
with_options :if => lambda { |u| u.user.password_digest_changed? && !u.password.nil? } do |user|
user.validates :password, length: { minimum: 6 }
end
end
Or inline (the relevant password part only) :
user.validates :password_digest, presence: true, if: lambda { |u| u.facebook_uid.nil? }
user.validates :password, length: { minimum: 6 }, if: lambda { |u| u.user.password_digest_changed? && !u.password.nil? }
None of these worked for me, whatever I tried I get this error :
undefined method `user' for #<User:0x00000008e924a8>
Again if I remove ifs and lambdas works as charm. Is there another way of accomplishing this or something I'm currently doing wrong with my code?
In your password validation lambda, you're calling
u.user.password_digest_changed? && !u.password.nil?
ie, you're sending a user method to the u object, which is your User instance. That object doesn't respond to user. You probably just want
u.password_digest_changed? && !u.password.nil?

how to skip model validation for particular function in rails?

I want to skip some model validation for controller functions. I am doing like this
Model :
attr_accessible :skip_method_2
validates :name, presence: true, length: { maximum: 50 }, :unless => :skip_method_2
VALID_PHONE_REGEX = /\(?([0-9]{3})\)?([ .-]?)([0-9]{3})\2([0-9]{4})/
validates :phoneno, presence: true,uniqueness: { case_sensitive: false, :scope => :user_id}, format: { with: VALID_PHONE_REGEX }, :unless => :skip_method_2
Controller :
def contacts_callback
#contacts = request.env['omnicontacts.contacts']
#contacts.each do |contact|
next if current_user.contacts.exists?(:email => "#{contact[:email]}")
contact1 = current_user.contacts.new(:skip_method_2 => true)
contact1.name = contact[:name]
contact1.email = contact[:email]
contact1.group = "Others"
contact1.save
end
redirect_to "/contact"
end
I dont want to save it by :validation => false. I want to skip name and phoneno validation for contacts_callback function. But it is not working.
It gives error in controller -
undefined local variable or method `skip_method_2' for contacts_callback. I already mentioned attr_accessible in my model
Change validates :name, presence: true, length: { maximum: 50 }, :unless => :skip_method_2
to
validates :name, presence: true, length: { maximum: 50 }, :unless => lambda {|x| x.skip_method_2}
Also checkout this answer

Generate reset password token don't save on model

Hello I trying to create a reset password for my rails app; but when I try to save I get the following error:
Validation failed: Password can't be blank, Password is too short
(minimum is 6 characters), Password confirmation can't be blank
This is my user model.
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation
has_secure_password
before_save { |user| user.email = email.downcase }
before_save :create_remember_token
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false }
validates :password, presence: true, length: { minimum: 6 }
validates :password_confirmation, presence: true
def send_password_reset
self.password_reset_token = SecureRandom.urlsafe_base64
self.password_reset_at = Time.zone.now
self.password = self.password
self.password_confirmation = self.password
save!
end
private
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64
end
end
the method "send_password_reset" doesn't update the user and I don't understand why is trying to save the user instead on updating just password_reset_token and the password_reset_at.
Does anybody can help me, please?
When you call save! on the model instance, it's going to run the validations on your User model; all of them.
There are a number of ways to skip the password validations conditionally. One is to use a Proc
validates :password, presence: true, length: { minimum: 6 }, unless: Proc.new { |a| !a.new_record? && a.password.blank? }
This will allow the User instance to be saved and will skip the validation of the :password field if it's blank and the User is not new (already persisted to the database).
Here is most of a password validation I use in my applications
validates :password, confirmation: true,
length: {:within => 6..40},
format: {:with => /^(?=.*\d)(?=.*([a-z]|[A-Z]))([\x20-\x7E]){6,40}$/},
Notice, you don't need the separate validation on :password_confirmation. Instead just pass confirmation: true to the :password validator.
Suggested Reading:
http://guides.rubyonrails.org/active_record_validations_callbacks.html#confirmation

Resources