Sexy Validation in Edge Rails (Rails 3)

I have just had my sexy validations patch excepted into Rails. Much thanks to José Valim for helping me get this applied.

The reason for the name “sexy validations” is that it gives a much more concise way of defining validation and reusing custom validator classes. Much like what sexy migrations did for defining your database schema.

Simple example of using existing Rails validations, the “sexy” way:

class Film < ActiveRecord::Base
  validates :title, :presence => true, :uniqueness => true, :length => { :maximum => 100 }
  validates :budget, :presence => true, :length => { :within => 1..10000000 }
end

The power of the “validates” method comes though, when using in conjunction with custom validators:

class IntenseFilmTitleValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    record.errors[attribute] << "must start with 'The'" unless =~ /^The/
  end
end
 
class SpendValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    spend = case options[:size]
      when :big then 100000000
      when :small then 100000
    end
    record.errors[attribute] << "must not exceed #{spend}" if value > spend
  end
end
 
class Film < ActiveRecord::Base
  validates :title, :presence => true, :intense_film_title => true
  validates :budget, :spend => { :size => :big } # using custom options
end

All validations in Rails, along with other common model functionality have been extracted into ActiveModel, so you can also use validations and Validator classes without ActiveRecord e.g.

class EmailValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    record.errors[attribute] << (options[:message] || "is not an email") unless
      value =~ /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
  end
end
 
class Person
  include ActiveModel::Validations
  attr_accessor :name, :email
 
  validates :name, :presence => true, :length => { :maximum => 100 }
  validates :email, :presence => true, :email => true
end

Have fun!

4 comments ↓

#1 ahe on 02.15.10 at 10:14 pm

Hi,

Thanks for this!
Is it possible to use numericality options with this?

For example, the following line doesn’t work :

validates :price, :numericality => true, :greater_than => 100

#2 Jamie on 02.15.10 at 11:25 pm

@ahe you could do the following:

validates :price, :numericality => true, :length => { :minimum => 100 }

or, if you wanted a range

validates :price, :numericality => true, :length => [100..1000]

#3 Josef Schmitz on 02.19.10 at 10:15 am

When using Validation in non ActiveRecords, when is Validation triggered?

#4 Jamie on 02.22.10 at 4:12 pm

@Josef Validation is triggered when calling the “valid?” method on an instance. The “errors” method will then return any errors.

Leave a Comment