Custom ActiveModel::Validators are an easy way to validate individual attributes on your Rails models. All that's required is a Ruby class that inherits from ActiveModel::EachValidator and implements a validate_each method that takes three arguments: record, attribute, and value. I have written a few lately, so I pinged the rest of the amazingly talented Viget developers for some contributions. Here's what we came up with.

Simple URI Validator

A "simple URI" can be either a relative path or an absolute URL. In this case, any value that could be parsed by Ruby's URI module is allowed:

classUriValidator<ActiveModel::EachValidatordefvalidate_each(record,attribute,value)unlessvalid_uri?(value)record.errors[attribute]<<(options[:message]||'is not a valid URI')endendprivatedefvalid_uri?(uri)URI.parse(uri)truerescueURI::InvalidURIErrorfalseendend

Full URL Validator

A "full URL" is defined as requiring a host and scheme. Ruby provides a regular expression to match against, so that's what is used in this validator:

classFullUrlValidator<ActiveModel::EachValidatorVALID_SCHEMES=%w(http https)defvalidate_each(record,attribute,value)unlessvalue=~URI::regexp(VALID_SCHEMES)record.errors[attribute]<<(options[:message]||'is not a valid URL')endendend

Email Validator

classEmailValidator<ActiveModel::EachValidatordefvalidate_each(record,attribute,value)unlessvalue=~/\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/irecord.errors[attribute]<<(options[:message]||"is not a valid e-mail address")endendend

If you'd rather validate by performing a DNS lookup, Brian Landau has you covered with this Github gist.

Secure Password Validator

classSecurePasswordValidator<ActiveModel::EachValidatorWORDS=YAML.load_file("config/bad_passwords.yml")defvalidate_each(record,attribute,value)ifvalue.in?(WORDS)record.errors.add(attribute,"is a common password. Choose another.")endendend

Twitter Handle Validator

classTwitterHandleValidator<ActiveModel::EachValidatordefvalidate_each(record,attribute,value)unlessvalue=~/^[A-Za-z0-9_]{1,15}$/record.errors[attribute]<<(options[:message]||"is not a valid Twitter handle")endendend

Hex Color Validator

A validator that's useful when an attribute should be a hex color value:

UPDATE: The regular expression has been simplified thanks to a comment from HappyNoff.

Regular Expression Validator

A great solution for attributes that should be a regular expression:

classRegexpValidator<ActiveModel::EachValidatordefvalidate_each(record,attribute,value)unlessvalid_regexp?(value)record.errors[attribute]<<(options[:message]||'is not a valid regular expression')endendprivatedefvalid_regexp?(value)Regexp.compile(value)truerescueRegexpErrorfalseendend

Bonus Round

Replace all of those default error messages above with I18n translated strings for great justice. For the Regular Expression Validator above, the validate_each method could look something like this: