This is an extract from a customer model with three different street addresses, in which we validate all three of the zip codes. (In this code, the GeoState.valid_zip_code? method answers if something that looks like a zip code is an actual zip code – not all five digit combinations are in use as zip codes, and we want to make sure we’ve got a live one.)

That’s a bit better. Before we had three custom validation methods that did all the same work using an extracted method. We have replaced those three methods with a single custom class-level validation, validates_zip_code, which validates a zip code attribute by name. It’s used just like any other standard validation, and as many times as you want. Nice and dry! The implementation of validates_zip_code is modeled on some of the standard validation methods found in ActiveRecord’s Validations module (in 'active_record/validations.rb'). Take a look at that code for more examples of how you might write your own custom validations.

Now that’s a good start, but we can do even better. We can take the custom validation and fold in the other validations we do on each zip code.

This example was extracted from the version history of the code for a project I worked on recently. It follows the very steps we took to refactor the validations step by step. I know that Jeff Dean has contributed a cool validator object enhancement that will be appearing in Rails 3, but this approach works out of the box with the current version of Rails, so you don’t have to turn blue holding your breath.

Final note: If you’re wondering why we validate presence and then allow blank on the following validations, it’s so that the user only gets one error message at a time.