rails fields validate if - ruby-on-rails

I have a Model Product with fields:
name price product_type_id:integer size:string page_quantity:integer.
How can make this validation work for Product model create action?
- if product_type.id == "1"
validates :size, presence: true
validates :page_quantity, presence:false
- else
validates :size, presence: false
validates :page_quantity, presence:true
end

validates :size, presence: true, if: Proc.new { |p| p.product_type_id == "1" }
validates :page_quantity, presence:false, if: Proc.new { |p| p.product_type_id == "1" }
validates :size, presence: false, unless: Proc.new { |p| p.product_type_id == "1" }
validates :page_quantity, presence:true, unless: Proc.new { |p| p.product_type_id == "1" }
end

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

Wrap multiple validations in a context

With validation context we can do:
validates :title, presence: true, on: :published
validates :content, length: { maximum: 50 }, on: :published
Is it possible to wrap multiple validations that share a context something like the following?
on: :published do
validates :title, presence: true
validates :content, length: { maximum: 50 }
end
Yes, you can group validations using the with_options method:
with_options(on: :published) do |record|
record.validates :title, presence: true
record.validates :content, length: { maximum: 50 }
end
See the Rails Guides, this article and the sources for more info.

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

Resources