Rails 3 subdomain validation (ActiveModel::EachValidator)


# subdomain_validator.rb (place in your lib/ or extra/ load path)
class SubdomainValidator < ActiveModel::EachValidator
  def validate_each(object, attribute, value)
    return unless value.present?
    reserved_names = %w(www ftp mail pop smtp admin ssl sftp)
    reserved_names = options[:reserved] if options[:reserved]
    if reserved_names.include?(value)
      object.errors[attribute] << 'cannot be a reserved name'
    object.errors[attribute] << 'must have between 3 and 63 letters' unless (3..63) === value.length
    object.errors[attribute] << 'cannot start or end with a hyphen' unless value =~ /^[^-].*[^-]$/i                                                                                                    
    object.errors[attribute] << 'must be alphanumeric; A-Z, 0-9 or hyphen' unless value =~ /^[a-z0-9\-]*$/i

# And in your model
validates  :subdomain, :presence   => true,
                       :uniqueness => true,
                       :subdomain  => true

# Or with your own reserved names
validates  :subdomain, :presence   => true,
                       :uniqueness => true,
                       :subdomain  => { :reserved => %w(foo bar) }

For more on creating custom Rails 3 validators, check our Ryan Bates’s screencast on the topic.

4 comments so far

  • photo of Rodrigo Dellacqua Rodrigo Dellacqua Oct 28, 2010

    Validators is a nice way to reuse validation code, but your code is wrong. There’s no options hash and there’s a typo in the error message for reserved words.

  • photo of Rodrigo Dellacqua Rodrigo Dellacqua Oct 28, 2010

    Alright, my bad, options hash is just being used, its from another object.

  • photo of Matt Matt Oct 30, 2010

    Typo fixed, thanks for the heads up. Yep the options hash originates from the hash passed in the model validates call.

  • photo of Palash Palash Apr 15, 2012

    I want my website subdomain valid.

Leave a comment