custom validations only if all attributes are valid

I can see some advantge of declaring a validation dependency. Checking if an age is less than 16 makes no sense if the age has failed a validation on being a number already.

maybe something like:

class User < AR::Base validates_numericality_of :age

#custom validation validate :user_older_16, :depends => :age

def user_older_16 errors.add(:age, "should be older 16") if age <= 16 end end

Where :user_older_16 would only run if age has no errors. In the context shown above this is a sensible dependency, but used incorrectly the concerns expressed by others would be valid.

Another suggestion:

class User < AR::Base

   validates_with_dependency do validates_numericality_of :age      validate :user_older_16    end

def user_older_16 errors.add(:age, "should be older 16") if age <= 16 end end

In the above the validations in the validate_with_dependency block would execute in order, exiting the sequence if any fail.

I think the suggestion to have dependent validaiton is a good one as it allows for structuring simiple validations where you know a validation cannot possibly pass if an earlier one has failed.

Cheers,

Anthony Richardson.

Yeah, I couldn't understand what Ken meant by his "state-machine" approach.

I can't think of a dry way of currently doing that.

I'm just not much concerned about this as I don't use AR and Sequel's way of doing validation is by defining a "validate" method, so I'd put my logic there, but anyway...

A poor choice of words maybe, I was trying to say that at some point in time, I have had models that needed a hierarchy of potential errors and that some errors are not appropriate to show at certain times. A object that goes thru states is a good example.

- Ken

Anthony, good point!

validates_with_dependency is the a specific case of suggested ‘validate_further’ approach.

Two stage validation doesn’t require you to manage dependencies (order, etc.) so no need to specify them.

You just rely on results from first stage.

One more example from real project:

order.rb

validates_presence_of :shop validates_presence_of :line_items validates_associated :billing_address

validate_further :on => :create do errors.add(:signature, :invalid) unless signature_verified? end

method signature_verified? calculates signature with

  • shop.secret_key - shop should present
  • amount, quantity, name for each line_item
  • attributes for billing/shipping, etc. and compares it with received value from user

do I really have to check manually that everything valid before calculate signature?

If you only want to run a validation on an attribute if it is valid at that point in the validation process then here's a trick:

module AttributeValidation   extend ActiveSupport::Concern

  included do     attribute_method_suffix '_valid?', '_invalid?'   end

  private

  def attribute_valid?(attr)     !attribute_invalid?(attr)   end

  def attribute_invalid?(attr)     errors.include?(attr.to_sym)   end end

include this module in AR::Base or specific models - it's up to you. Once included you can then set up your validations like this:

class User < ActiveRecord::Base   include AttributeValidation

  attr_accessible :name, :age

  validates :name, presence: true, length: { maximum: 100 }   validates :age, presence: true, numericality: { only_integer: true }   validate :over_minimum_age, if: :age_valid?

  def over_minimum_age     errors.add(:age, :invalid) if age < 16   end end

this will only run the minimum age validation once it has passed the presence and numericality checks. One advantage of doing this is you don't need guard checks when comparing the age (normally you'd get a "NoMethodError: undefined method `<' for nil:NilClass")

Andrew