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.