Namespaced Rails validators

While going source spelunking, I came across this piece of code in Rails’ ActiveModel:

key = "#{key.to_s.camelize}Validator"

begin
  validator = key.include?("::".freeze) ? key.constantize : const_get(key)
rescue NameError
  raise ArgumentError, "Unknown validator: '#{key}'"
end

active_model/validations/validates.rb

This means that you can namespace your custom validators:

# lib/internal/email_validator.rb
module Internal
  class EmailValidator
    def validate_each(record, attribute, value)
      return if value.ends_with?('@private_domain.com')

      record.errors.add(attribute, 'not from private domain')
    end
  end
end

And then use them like this:

# app/models/admin.rb
class Admin < ApplicationRecord
  validates :email, 'internal/email': true
end

Thanks to Bachir Çaoui for reviewing a draft version of this post.