Skip to content

Instantly share code, notes, and snippets.

@stuartbain
Last active October 25, 2021 22:33
Show Gist options
  • Save stuartbain/7212385 to your computer and use it in GitHub Desktop.
Save stuartbain/7212385 to your computer and use it in GitHub Desktop.
Custom validator for Subdomains
class SubdomainValidator < ActiveModel::EachValidator
# http://matthewhutchinson.net/2010/10/27/rails-3-subdomain-validation-activemodeleachvalidator
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'
end
object.errors[attribute] << 'must have between 3 and 63 characters' unless (3..63) === value.length
object.errors[attribute] << 'cannot start or end with a hyphen' unless value =~ /\A[^-].*[^-]\z/i
object.errors[attribute] << 'must be alphanumeric (or hyphen)' unless value =~ /\A[a-z0-9\-]*\z/i
end
end
@stuartbain
Copy link
Author

Use as follows:

# In model
validates  :subdomain, :presence   => true,
                       :uniqueness => true,
                       :subdomain  => true

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

@abhishek77in
Copy link

👍

@strickland84
Copy link

Thanks for this!

@rizwanreza
Copy link

Thanks.

Here are the tests for it written in RSpec:

require 'rails_helper'
require 'subdomain_validator'

class Subdomain
  include ActiveModel::Model
  attr_accessor :subdomain

  validates :subdomain, subdomain: true
end

describe SubdomainValidator do
  it "validates if the value is not present" do
    subdomain = Subdomain.new(subdomain: nil)
    expect(subdomain).to be_valid
  end

  it "does not validate subdomains with reserved names" do
    %w(www ftp mail pop smtp admin ssl sftp).each do |reserved_name|
      subdomain = Subdomain.new(subdomain: reserved_name)
      expect(subdomain).to_not be_valid
    end
  end

  it "does not validate subdomains when the length is not between 3 and 63 letters" do
    short_subdomain = Subdomain.new(subdomain: "ww")
    expect(short_subdomain).to_not be_valid

    long_subdomain = Subdomain.new(subdomain: "a" * 64)
    expect(long_subdomain).to_not be_valid

    valid_length_subdomain = Subdomain.new(subdomain: "a" * 55)
    expect(valid_length_subdomain).to be_valid
  end

  it "does not validate subdomains when they start or end with a hyphen" do
    hyphen_prefixed_subdomain = Subdomain.new(subdomain: "-hello")
    expect(hyphen_prefixed_subdomain).to_not be_valid

    hyphen_suffixed_subdomain = Subdomain.new(subdomain: "bye-")
    expect(hyphen_suffixed_subdomain).to_not be_valid

    hyphened_subdomain = Subdomain.new(subdomain: "hello-world")
    expect(hyphened_subdomain).to be_valid
  end

  it "does not validate if the subdomain is not alphanumeric" do
    non_alphanumeric_subdomain = Subdomain.new(subdomain: "_!3482asdkjas")
    expect(non_alphanumeric_subdomain).to_not be_valid
  end
end

@mohammadwali
Copy link

Thanks ❤️
I created a javascript version of this with a small tweak.
https://gist.github.com/mohammadwali/af11bb30b7a687a8908c9496c49f4faa

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment