pithyless pithyless - 9 months ago 86
Ruby Question

How to call ActiveRecord validators as instance methods (ala Sequel)?

I've got a model that needs different validators depending on its current state. How should I go about calling ActiveRecord validators per instance? I'd like to reuse as much plumbing as possible, but I'm not sure how to continue.

class Order < ActiveRecord::Base
attr_accessible :state

validate :state_specific_validations

def state_specific_validations
if :paid == self.state
# Warning: here be Unicorns...

# Wishful thinking...
validate_presence_of :paid_at
validate_associated :purchaser

# Hopeful. What are the validators called internally in Rails?
errors << PresenceValidator.new(self, :paid_at).valid?
errors << AssociationValidator.new(self, :paid_at).valid?

# Plan B
# ... Hoping for help from the audience ...

# Even more complicated validator logic, hoping for some DRY validators

I could just use custom validators, but why would I need to duplicate all the built-in validator logic (i18n error messages, etc.)?

Is there a neat way of calling the Rails validators as instance methods?
I think Sequel's approach of instance-based validators is more reasonable than ActiveRecord's class-based, but I'm not here to judge. I'd just like to get back to solving more interesting problems. I'm just hoping that others have come across this and can point me to some interesting gist or gem.

pat pat
Answer Source

I'm pretty sure all of the validate_* methods can take an :if option - which can point to another method (and likely accept a Proc as well), so you could break up your validations to be more something like:

validates_presence_of :paid_at, :if => :paid?
validates_association :purchaser, :if => :paid?

To clean things up further, there's the with_options helper:

with_options :if => :paid? do |v|
  v.validates_presence_of :paid_at
  v.validates_association :purchaser

Not sure if either can be used with a standard validate :custom_validate_method though - but it wouldn't surprise me.