I have just had my sexy validations patch accepted 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 value =~ /^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!
