Validation Groups
On my previous project we found that our object.valid? method needed to depend on the role that an object was currently playing. This led to the introduction of groups.
Validation groups can be used to validate an object when it can be valid in various states. For example a mortgage application may be valid for saving (saving a partial application), but that same mortgage application would not be valid for underwriting. In our example a application can be saved as long as a Social Security Number is present; however, an application can not be underwritten unless the name attribute contains a value.
class MortgageApplicationAs you can see, you can use an array if the validation needs to be part of various groups. However, if the validation only applies to one group you can simply use a symbol for the group name.
include Validatable
validates_presence_of :ssn, :groups => [:saving, :underwriting]
validates_presence_of :name, :groups => :underwriting
attr_accessor :name, :ssn
end
application = MortgageApplication.new
application.ssn = 377990118
application.valid_for_saving? #=> true
application.valid_for_underwriting? #=> false
The inspiration for adding this functionality came from the ContextualValidation entry by Martin Fowler.
validates_true_for
The validates_true_for method was added to allow for custom validations.
The validates_true_for method can be used to specify a proc, and add an error unless the evaluation of that proc returns true.
class PersonThe logic option is required.
include Validatable
validates_true_for :first_name, :logic => lambda { first_name == 'Book' }
attr_accessor :first_name
end
person = Person.new
person.valid? #=> false
person.first_name = 'Book'
person.valid? #=> true
validates_numericality_of
This release also adds the validates_numericality_of method. The validates_numericality_of method takes all of the standard parameters that the other validations take: message, times, level, if, group.
Validates that the specified attribute is numeric.
class Personafter_validate hook method
include Validatable
validates_numericality_of :age
end
Another new feature of this release is the after_validate hook. This feature allows you to manipulate the instance after a validation has been run. For example, perhaps you are happy with the default messages; however, you also want the attribute to be appended to the message. The following code uses that example and shows how the after_validate hook can be used to achieve the desired behavior.
class PersonThe after_validate hook yields the result of the validation being run, the instance the validation was run on, and the attribute that was validated.
include Validatable
validates_presence_of :name
attr_accessor :name
end
class ValidatesPresenceOf
after_validate do |result, instance, attribute|
instance.errors.add("#{attribute} can't be blank") unless result
end
end
person = Person.new
person.valid? #=> false
person.errors.on(:name) #=> "name can't be blank"
include_validations_for takes options
The include_validations_for method was changed to accept options. The currently supported options for include_validations_for are :map and :if.
class PersonThe person attribute will be validated. If person is invalid the errors will be added to the PersonPresenter errors collection. The :map option is used to map errors on attributes of person to attributes of PersonPresenter. Also, the :if option ensures that the person attribute will only be validated if it is not nil.
include Validatable
validates_presence_of :name
attr_accessor :name
end
class PersonPresenter
include Validatable
include_validations_for :person, :map => { :name => :namen },
:if => lambda { not person.nil? }
attr_accessor :person
def initialize(person)
@person = person
end
end
presenter = PersonPresenter.new(Person.new)
presenter.valid? #=> false
presenter.errors.on(:namen) #=> "can't be blank"
validates_confirmation_of
The validates_confirmation_of method now takes the :case_sensitive option. If :case_sensitive is set to false, the confirmation will validate the strings based a case insensitive comparison.
validates_length_of
The validates_length_of method now takes the :is option. If the :is option is specified, the length will be required to be equal to the value given to :is.
Bugs
The comparison in validates_format_of was changed to call to_s on the object before execution. The new version allows you to validate the format of any object that implements to_s.