Skip to content

Instantly share code, notes, and snippets.

Last active November 11, 2022 00:15
Show Gist options
  • Save JeremiahChurch/5a36880bb6ce55cd5e882413dac716a0 to your computer and use it in GitHub Desktop.
Save JeremiahChurch/5a36880bb6ce55cd5e882413dac716a0 to your computer and use it in GitHub Desktop.
Custom Subdomains from Models in Rails & Rspec testing too
class SubdomainConstraint
# used in routes to generate subdomains
# note there is a cache buster in company.rb in case the subdomain is updated - `reload_routes`
# update it if we write any additional cache entries here
www app admin test staging help support wwww cdn owner media intercom mail r _domainkey blog webhooks webhook
hooks api api-test api-staging referral outbound mail webmail w3 mailboxes io heroku inbound google search
def initialize
def generate_subdomains
dynamic_subdomains = Company.where.not(subdomain: nil).pluck(:subdomain)
@domains = dynamic_subdomains - RESERVED_SUB_DOMAINS
# done to allow codeship to run assets:precompile in prod mode without touching the DB
rescue ActiveRecord::StatementInvalid
def set_timestamp
Rails.cache.write('rails_routes_ts', Digest::MD5.hexdigest(@domains.inspect), expires_in:
rescue StandardError
warn "WARNING - Error in set_timestamp method, maybe cache host is down? (method:#{__method__} in #{__FILE__}"
def matches?(request)
# In development/staging allow multiple subdomains like
subdomain_to_check = !Rails.env.production? ? request.subdomains.first : request.subdomain
rescue StandardError
warn "WARNING - Error detecting Route auth for current domain #{} (method:#{__method__} in #{__FILE__}"
def check_if_expired_routes?
generate_subdomains if Rails.cache.fetch('rails_routes_ts') != Digest::MD5.hexdigest(@domains.inspect)
rescue StandardError
warn "WARNING - Error in check_if_expired_routes? method, maybe cache host is down? (method:#{__method__} in #{__FILE__}"
class Company < ApplicationRecord
SUBDOMAIN_REGEX = /\A^[a-z\d]+(-[a-z\d]+)*\z/i # I normally put this in application_record.rb or somewhere else but this is easier for a gist
# associations
has_many :users
# validations
validates :subdomain, length: 3..64, allow_nil: true, uniqueness: true,
format: { with: SUBDOMAIN_REGEX, message: 'Valid subdomain characters only' }
# related to subdomain_constraint.rb
def reload_routes
return unless saved_change_to_subdomain?
Rails.application.routes.draw do
# keep this above other routes otherwise it will be overridden
namespace :subdomain, constraints:, path: nil do
# whatever subdomain routes you want served - note the namespace - you can change it of course
# ... generic routes
# Add additional requires below this line. Rails is not loaded until this point!
# ...
require 'support/subdomain_support' # After any SSL - but up top by the rest of your requires
# ...
# Support for Rspec / Capybara subdomain integration testing
def switch_to_subdomain(subdomain)
# always resolves to
hostname = subdomain ? "#{subdomain}" : 'localhost'
Capybara.app_host = "http://#{hostname}:#{Capybara.server_port}"
def switch_to_main_domain
switch_to_subdomain nil
Capybara.configure do |config|
config.always_include_port = true

rspec usage is something like:

before(:each) do

after(:each) do
  switch_to_main_domain # keeps everything working fine with random test order

and then whatever you want to test on the subdomains

accessing in dev and other services redirect to localhost or for testing - work great for testing subdomains. if you aren't working directly on machine (I work on a VM sometimes.) Then just add the subdomains you work with to your hosts file.

You should also add whatever loopback service you use to your CI server's host file so any outages don't cause CI problems


models and attributes aren't magic, use whatever you like.

files are named with dashes in place of slashes just in case you're super fresh. The first time I picked up subdomains from railscast I was two weeks in to rails and not familiar - hopefully that helps someone.

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