Extended Email validation for all your models (validates_email)

By | December 5

Ruby on Rails ships with a lot of predefined validations for your models. Unfortunately there is no special email validation. Even Rails 2.0 does not ship it (check the ActiveRecord changelog) yet. Because I needed it I had to write my own validates_email method. I know there are a lot of plugins but non of them suits my needs. validates_email does the following:

  • checks if email is syntactically correct (using regex)
  • checks if the host of the email is supported within the application. This is very important for me because I dont want ppl signing up with one-time emails from e.g. mailinator.com. (can be disabled)

For this we create a validations helper module which holds all our custom validations. We can then inject them into our models where necessary. The following code should live in /helpers/validations_helper.rb in order to be loaded automatically:

  1. #Michal Gabrukiewicz
  2. #helpers for validating ActiveRecord instances
  3. #- load with 'extend' within your model
  4. module ValidationsHelper
  6.   EMAIL_PATTERN = /^[a-zA-Z][w.-]*[a-zA-Z0-9_]@[a-zA-Z0-9][w.-]*[a-zA-Z0-9].[a-zA-Z][a-zA-Z.]*[a-zA-Z]$/
  7.   DISALLOWED_EMAIL_HOSTS = %w(mailinator.com mailinator.org)
  9.   #validates if given attributes are syntactically valid emails
  10.   #- last parameter can be a hash with options
  11.   #   - :allow_all_hosts indicates if every host is allowed. if false then its checked
  12.   #     against the list DISALLOWED_EMAIL_HOSTS and returns fals if the email is
  13.   #     from one of those. default = false
  14.   def validates_email(*attrs)
  15.     config = {:message => 'is no valid email address', :on => :save, :allow_all_hosts => false}
  16.     config.update(attrs.pop) if attrs.last.is_a?(Hash)
  17.     validates_each(attrs, config) do |record, attr, value|
  18.       unless config[:if] and not evaluate_condition(config[:if], record)
  19.         #do every check only if there was no error yet...
  20.         error = false
  21.         error = record.errors.add(attr, config[:message]) unless not error and value =~ EMAIL_PATTERN
  22.         error = record.errors.add(attr, config[:message]) if not config[:allow_all_hosts] and not error and DISALLOWED_EMAIL_HOSTS.include?(value.split("@", 2)[1].strip.downcase)
  23.       end
  24.     end
  25.   end
  27. end

If you now want to validate e.g. the email attribute of a User model, do it like you used to do with the existing validators:

  1. class User
  2.   extend ValidationsHelper
  4.   validates_email :email
  5. end

It still supports all the options which you know from all other validators. So you can specify your own message with :message, use a condition with :if, etc. There is one additional option :allow_all_hosts which prevents the validator to check against the disallowed hosts (all email hosts would be allowed then).

have fun with it and feel free to comment improvements, suggestions, comlaints, etc…

if you are looking for more validators and/or more information about them check Custom validations in rails from Greg

michal - RaRoR (rock and roll ruby on rails)