Yesterday I wrote about namespacing and scoping a Rails app to separate concerns and today I want to extend this to subdomains.
On the BattleHack site we serve every year from the same app (e.g. 2014 and 2015) while displaying different layouts. To do this we use the same controllers for each year (every one of them inheriting from controllers/app/base_controller.rb
).
In the base controller I first extract the current year:
# /app/controllers/app/base_controller.rb
def selected_year
@selected_year ||= begin
if ["2014", "2015"].include?(request.subdomain)
request.subdomain
else
"2015"
end
end
end
A few things to note here:
Next up I changed the base controllers to use the current year to load the correct layout and views.
# /app/controllers/app/base_controller.rb
class App::BaseController < ApplicationController
layout -> { selected_year }
before_filter :set_view_path
def set_view_path
prepend_view_path("app/views/#{selected_year}")
end
...
end
With this setup your controllers will default to:
/app/views/layouts/2015.html.erb
as a layout/app/views/2015/app/
as their view pathOne big caveat with this setup is that any changes in your models or controllers will require changes in every template for every year. If you remove an attribute on a model, or change the representation of an attribute, this will affect every subdomain.
There are a few solutions to this (besides good tests). One way is to use the presenter pattern (and using a separate presenter based on each subdomain) to keep your changes from breaking anything. Alternatively you can also rely on helpers per subdomain to achieve a similar effect.