For those who want to implement subdomain programmatically within your rails application, you can follow these few steps:
1. Generate Account Model
First, let’s create an Account model as a table to store subdomain names that we will accept in our application.
rails g model Account subdomain:string
From the above generator, it generates a migration file. We can then do
2. Add Request-based Constraints dan Modify routes.rb
Create a new file inside the /lib folder and give it the name, say subdomain.rb.
Add this code inside that file:
class Subdomain def self.matches?(request) request.subdomain.present? && request.subdomain != "www" end end
Then we need to modify a bit our routes.rb by adding
constraints to every URLs that we will use under the subdomain. For an example, pages_controller.rb needs to be accessible under a specific subdomain, so we can write our route like this:
Rails.application.routes.draw do # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html constraints(Subdomain) do get '/', to: 'pages#index' end end
3. Add Authorization to Controller
We will then need to add
before_action :require_subdomain since the controllers need to be strictly accessible from the subdomain only. This is achievable by modifying our application_controller.rb:
class ApplicationController < ActionController::Base protect_from_forgery with: :exception helper_method :require_subdomain private def require_subdomain not_found unless subdomain_exist? end def subdomain_exist? account = Account.find_by_subdomain(request.subdomain) !!account end def not_found raise ActionController::RoutingError.new('404') end end
In the application_controller.rb above we now have a helper method called
require_subdomain, which is reusable to each controller that requires to be authorized by the subdomain.
class PagesController < ApplicationController before_action :require_subdomain def index # controller logic goes here end end
4. Try It Out
Let's use rails console to create a dummy for our
Account or subdomain.
$ rails c Running via Spring preloader in process 15151 Loading development environment (Rails 6.0.0) 2.6.5 :001 > Account.new(subdomain: 'test').save (0.4ms) SET NAMES utf8, @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'), @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483 (0.3ms) BEGIN Account Exists (0.7ms) SELECT 1 AS one FROM `accounts` WHERE `accounts`.`subdomain` = BINARY 'test' LIMIT 1 SQL (0.5ms) INSERT INTO `accounts` (`subdomain`, `created_at`, `updated_at`) VALUES ('test', '2017-07-22 14:58:35', '2017-07-22 14:58:35') (35.5ms) COMMIT => true
If the subdomain is registered in
Subdomain doesn't exist in
We can add some extra validation layers in the
Account creation so we can preserve some subdomain names so your user cannot claim those (i.e. www, admin, app, etc).