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
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 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
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
I have model Article, for example. Article has_one ArticleContent. ArticleContent has validation of all it's attributes by default. But I need additional functionality - to save draft Article, without any validation.
So I pass :draft => false as one of a parameter in Article.new(), next I do #article.build_article_content(). There is some not working code in ArticleContent:
def draft?
raise self.article.draft
end
validates_presence_of :header, :message => "We have no fuckin' header!", :unless => :draft?
Of course it's not work. At the moment of draft? execution there is no any suitable Article object anywhere, so self.article returns nil. Nice try, codemonkey...
Anyone have some sweet ideas? I think to make #content.save! is not a very good idea
UPDATE
I tried so:
def draft
self[:draft]
end
def draft=(value)
self[:draft] = value
end
def draft?
self[:draft]
end
validates_presence_of :field1, :message => "msg1", :unless => :draft?
validates_presence_of :field2, :message => "msg2", :unless => :draft?
validates_presence_of :field3, :message => "msg3", :unless => :draft?
It works, but how can I group this?
unless self.draft?
validates_presence_of :field1, :message => "msg1"
validates_presence_of :field2, :message => "msg2"
validates_presence_of :field3, :message => "msg3"
end
Says that draft? method is not found. Also i should do
#article.content.draft = #article.draft
And it looks like dirty-dirty hack too
This is a common use case for a state machine. There are several rails plugins that provide for those.
http://ruby-toolbox.com/categories/state_machines.html
If you don't need a full state machine implementation it could still be instructive to have a state column in your ArticleContent model. Its values would be "new", "draft", "published" and so on. Your validations would look at that column's value when deciding what to do, like:
validates :content, :presence => true, :unless => Proc.new { |a| a.state == "Draft" }
(I'm pretty sure that's not the correct syntax but you should get what I'm aiming at.)
To answer your UPDATE
Try with_options.
with_options :unless => :draft? do |o|
o.validates_presence_of :field1, :message => "msg1"
o.validates_presence_of :field2, :message => "msg2"
o.validates_presence_of :field3, :message => "msg3"
end
Looking at your code there's a couple of smells. In order to flunk a validation the thing to do is errors.add(blah), not raise an exception. Also, your methods defined for accessing the draft column look a little redundant. They're just doing what AR would do anyway.
I've got a model with its validations, and I found out that I can't update an attribute without validating the object before.
I already tried to add on => :create syntax at the end of each validation line, but I got the same results.
My announcement model have the following validations:
validates_presence_of :title
validates_presence_of :description
validates_presence_of :announcement_type_id
validate :validates_publication_date
validate :validates_start_date
validate :validates_start_end_dates
validate :validates_category
validate :validates_province
validates_length_of :title, :in => 6..255, :on => :save
validates_length_of :subtitle, :in => 0..255, :on => :save
validates_length_of :subtitle, :in => 0..255, :on => :save
validates_length_of :place, :in => 0..50, :on => :save
validates_numericality_of :vacants, :greater_than_or_equal_to => 0, :only_integer => true
validates_numericality_of :price, :greater_than_or_equal_to => 0, :only_integer => true
My rake task does the following:
task :announcements_expiration => :environment do
announcements = Announcement.expired
announcements.each do |a|
#Gets the user that owns the announcement
user = User.find(a.user_id)
puts a.title + '...'
a.state = 'deactivated'
if a.update_attributes(:state => a.state)
puts 'state changed to deactivated'
else
a.errors.each do |e|
puts e
end
end
end
This throws all the validation exceptions for that model, in the output.
Does anybody how to update an attribute without validating the model?
You can do something like:
object.attribute = value
object.save(:validate => false)
USE update_attribute instead of update_attributes
Updates a single attribute and saves the record without going through the normal validation procedure.
if a.update_attribute('state', a.state)
Note:- 'update_attribute' update only one attribute at a time from the code given in question i think it will work for you.
try using
#record.assign_attributes({ ... })
#record.save(validate: false)
works for me
Yo can use:
a.update_column :state, a.state
Check: http://apidock.com/rails/ActiveRecord/Persistence/update_column
Updates a single attribute of an object, without calling save.
All the validation from model are skipped when we use validate: false
user = User.new(....)
user.save(validate: false)
Shouldn't that be
validates_length_of :title, :in => 6..255, :on => :create
so it only works during create?