I'm working on a model that has two associations that need to be set when an object is created, EXCEPT in one case.
Basically, it needs to work like this.
class Example < ActiveRecord::Base
has_one :foo
has_one :bar
validates_presence_of :foo
validates_presence_of :bar, :unless => :foo == Foo.find_by_name('ThisFooDoesntLikeBars')
end
I'm not sure how to build the :unless condition here, as it needs to check whether :foo is a specific object or not.
How do you do something like this?
:unless accepts a Proc
validates_presence_of :bar, :unless => Proc.new { |ex| ex.foo == Foo.find_by_name('ThisFooDoesntLikeBars') }
:unless - Specifies a method, proc or string to call to determine if the validation should not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The method, proc or string should return or evaluate to a true or false value.
http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html
How about the following:
class Example < ActiveRecord::Base
has_one :foo
has_one :bar
validates_presence_of :foo
validates_presence_of :bar, :unless => Proc.new { |example| example.foo == Foo.find_by_name('ThisFooDoesntLikeBars') }
end
Related
Right so I have one user table with different users. I need to validate user tables on update prior to them moving onto the main site.
so far I've just done the following, is there a way I can block the validations depending on a role without doing a custom validation method, like with_options :on => :update
before_validation :check_role
if check_role = "developer" do |dev|
dev.validate :first_name, presence: true # this doesn't work btw...
end
def check_role
return self.role_type unless self.role_type == nil
end
I figure it out and this looks like the best way to do it:
class YourModel
with_options :if => lambda { |o| o.whatever == "whatever" } do |on_condition|
on_condition.validates_presence_of :address
on_condition.validates_presence_of :city
end
with_options :if => lambda { |o| o.condition_the_second == "whatever" } do |on_condition|
on_condition.validates_presence_of :foo
on_condition.validates_presence_of :bar
end
end
validates :first_name, presence: true, if: :developer?
def developer?
role == 'developer'
end
with_options :if => Proc.new {|user| user.role_type == 'developer'} do |developer|
developer.validates :first_name, :presence => true
end
I'm trying to create a before_save to set the name of an entry to the date it was created if it was left blank. I got it to work with a method, here is my model:
class Entry < ActiveRecord::Base
belongs_to :user
has_many :entry_ingredients
has_many :ingredients, :through => :entry_ingredients
before_save :default_name
accepts_nested_attributes_for :ingredients, :reject_if => lambda { |a| a[:name].blank? }, :allow_destroy => true
def default_name
if self.name.blank?
self.name = Time.new
else
self.name
end
end
end
However, what I think is the same code doesn't seem to work in the before_save. Basically, I was trying to do this before_save :default_name, :if => self.name.blank? and then my method would be shorter like so:
def default_name
self.name = Time.new
end
Why doesn't that if statement on before_save work? I get the error undefined methodbefore_save' for false:FalseClass`
You cant do it this way, because the self.name.blank? gets evaluated when the class gets evaluated for the first time. So you end up with ":if => false" or ":if => true" or an errors occurs.
before_save :default_name, :if => self.name.blank?
In the same file you have an example of how this can be done:
before_save :default_name, :if => lambda { |entry| entry[:name].blank? }
However there are other options. Check the documentation.
You can do like this:
before_save :default_name
def default_name
self.name ||= Time.new
end
or:
before_save :default_name, :unless => :name?
def default_name
self.name = Time.new
end
Can someone please point me to the right direction.
I'm using after_validation to compete geocoding and I wanted to know how I can tact on an or || option to an if clause.
This works as intended
class Address < ActiveRecord::Base
after_validation :geocode, if: :address_changed?
But this doesn't
class Address < ActiveRecord::Base
after_validation :geocode, if: :address_changed? || :zipcode_changed?
Thanks in advance.
If you don't want to use Thilo's Proc approach then you can add your own custom method and use that:
class Address < ActiveRecord::Base
after_validation :geocode, if: :address_or_zip_changed?
private
def address_or_zip_changed?
address_changed? || zipcode_changed?
end
end
This may (or may not) read better and is handy if you need the same compound condition on multiple validations.
:if - Specifies a method, proc or string to call to determine if the validation should occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The method, proc or string should return or evaluate to a true or false value.
So try a proc:
if: Proc.new { |a| a.address_changed? || a.zipcode_changed? }
You can do this using :if
validates :email, :unique=> true, :if => Proc.new { email.present? }
or you could also use lambda
validates :email, :unique=> true, :if => lambda { email.present? }
When using state_machine, how can you conditionally validate fields in the below way?
state :unlit do
end
state :fire do
if is_big_fire?
validates_presence_of :big_log
end
if is_small_fire?
validates_presence_of :small_log
end
end
It seems to just ignore the if conditions and validate everything inside the state D:
The only sort of solution I came up with was
validates_presence_of :big_log, :if => Proc.new { |fire| fire.is_big_fire? }
But this gets nuts if there are more validations.
validates_presence_of :big_log, :if => Proc.new { |fire| fire.is_big_fire? }
validates :fire_epicness_rating, :inclusion => { :in => %w(epic whowa RUNFORTHEHILLS) }, :if => Proc.new { |fire| fire.is_big_fire? }
etc
Is there some nice way of neatly wrapping these in if blocks?
Grouping validations thanks to with_optionsis really neat. See here.
Here's an example using the with_options for group validation.
with_options :if => :driver? do |driver|
driver.validates_presence_of :truck_serial
driver.validates_length_of :truck_serial, :maximum => 30
end
def driver?
roles.any? { |role| role.name == "driver" }
end
Source: http://rubyquicktips.com/post/411400798/conditional-validation-using-with-options-to-improve
I have a model with many validations that can be grouped based on various conditions. The brute force way to handle it would be:
validates_presence_of :attr1, :if => :condition1
validates_something :attr2, :if => :condition1
validates_something_else :attr3, :if => :condition1
...
validates_presence_of :attr4, :if => :condition2
validates_something :attr5, :if => :condition2
validates_presence_of :attr6, :if => :condition2
...
But that doesn't seem very DRY. Is there a good way to group the validations based on the conditions? The approach I came up with is:
class Condition1Validator < ActiveModel::Validator
def validate(record)
record.instance_eval do
validates_presence_of :attr1
validates_something, :attr2
validates_something_else :attr3
end
end
end
validates_with Condition1Validator, :if => :condition1
class Condition2Validator < ActiveModel::Validator
...
end
validates_with Condition2Validator, :if => :condition2
Can anyone think of a better way?
Update: the way I posted above is flawed in that you cannot have if, unless, etc on the nested validators. Jesse's solution is much better.
This approach is from the multi-step wizard, where you only want to validate if you are on that wizard step. Should work for you as well
class YourModel
with_options :if => lambda { |o| o.whatever == "whatever" } do |on_condition|
on_condition.validates_presence_of :address
on_condition.validates_presence_of :city
end
with_options :if => lambda { |o| o.condition_the_second == "whatever" } do |on_condition|
on_condition.validates_presence_of :foo
on_condition.validates_presence_of :bar
end
end