Basic Subdomains in Ruby on Rails

In this article we will cover the basics of using subdomains in Ruby on Rails. Subdomains are quite useful in a number of different scenarios. For example, lets say you are building a multi user blog application. Each user gets their own blog. Being able to give each user a unique subdomain allows you to give users their own personal space. For this article we will build a simple blog application that contains this feature.

Rails Application Setup

The first thing we need to do is generate a couple models. The first model, Blog, represents a specific blog tied to a specific subdomain. A blog contains many posts. The second model, Post, is a simple blog post containing a title and body. Run the commands below to create these models now.

Terminal Commands:
rails g model Blog name subdomain
rails g model Post title body:text blog:references
rake db:migrate

The next thing we need to do is create our controllers. Similarly to the models, we will have a Blogs controller as well as a Posts controller. Run the commands below to create these now.

Terminal Commands:
rails g controller blogs index show
rails g controller posts show

Great, now we need to edit our routes file and set up a few routes. Open up your routes file and modify it so that it looks like the code listed below, being sure not to overwrite the first line of your routes file.

config/routes.rb:
SubdomainsExample::Application.routes.draw do
  resources :posts
  resources :blogs

  match '/', to: 'blogs#index', constraints: { subdomain: 'www' }, via: [:get, :post, :put, :patch, :delete]
  match '/', to: 'blogs#show', constraints: { subdomain: /.+/ }, via: [:get, :post, :put, :patch, :delete]

  root to: "blogs#index"
end

The first two lines are self explanatory. The magic happens on the next 2 lines. The first match line tells rails to match the subdomain ‘www’ to the index method of your blogs controller. This makes it so that typing http://www.yourdomain.com doesn’t search for a subdomain, but rather goes right to the blogs index method. The next match line tells rails to pass the subdomain request onto the blogs/show method.

Next we need to add some code to our application controller. We will add a get_blog method that will find the specific blog by it’s subdomain and return the result. If the blog doesn’t exist the user will be redirected to the blog index. Modify your application controller so that it looks like the code listed below.

app/controllers/application_controller.rb:
class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
  before_filter :get_blog

private
  def get_blog

    blogs = Blog.where(subdomain: request.subdomain)

    if blogs.count > 0
      @blog = blogs.first
    elsif request.subdomain != 'www'
        redirect_to root_url(subdomain: 'www')
    end
  end
end

Excellent, now lets modify our blogs controller. Open up your blogs controller and add in the code listed below.

app/controllers/blogs_controller.rb:
class BlogsController < ApplicationController
  def index
    @blogs = Blog.all
  end

  def show
    @posts = @blog.posts
  end
end


The code above is pretty self explanatory. The index method gets a list of blogs, the show method shows the contents of a specific blog.

Now let’s add some code to our posts controller. Modify your posts controller so that it looks like the code listed below.

app/controllers/posts_controller.rb:
class PostsController < ApplicationController
  def show
    @post = @blog.posts.find(params[:id])
  end
end

This code is pretty simple. It simply grabs the specified post for the current blog.

Now lets do the views. First lets modify our application layout to include bootstrap to make this example pretty. Modify your application layout so that it looks like the code listed below.

app/views/layouts/application.html.erb:
<!DOCTYPE html>
<html>
<head>
  <title>SubdomainsExample</title>
  <%= stylesheet_link_tag    "application", media: "all", "data-turbolinks-track" => true %>
  <%= javascript_include_tag "application", "data-turbolinks-track" => true %>
  <%= stylesheet_link_tag "http://yandex.st/bootstrap/3.0.2/css/bootstrap.min.css", media: "all" %>
  <%= javascript_include_tag "http://yandex.st/bootstrap/3.0.2/js/bootstrap.min.js" %>
  <%= csrf_meta_tags %>
</head>
<body>
class="container"> yield %>
</body> </html>

Great, now lets create our blog index view. Open up your blog index view and modify it so that it looks like the code listed below.

app/views/blogs/index.html.erb:
<h1>Blogs on this Server</h1>

<ul>
    <% @blogs.each do |blog| %>
        <li><%= link_to blog.name, root_url(subdomain: blog.subdomain) %></li>
    <% end %>
</ul>

Okay, now lets update the show view for blogs to look like the code listed below.

app/views/blogs/index.html.erb:
<h1><%= @blog.name %></h1>
<hr />
<% @posts.each do |post| %>

<h3><%= post.title %></h3>
<p><%= truncate post.body, length: 160 %></p>
<%= link_to "Read More", post %>
<% end %>

Great, finally lets modify the show view for posts to look like the code listed below.

app/views/posts/show.html.erb:
<h1><%= @post.title %></h1>
<p>
    <%= @post.body %>
</p>

One final thing. Lets add some seed data. Open up your seeds.rb file and add in the code listed below.

db/seeds.rb:
Blog.delete_all
Blog.create(id: 1, name: "My Example Blog", subdomain: "example")
Blog.create(id: 2, name: "Awesome Blog", subdomain: "awesome")


Post.delete_all
Post.create(id: 1, blog_id: 1, title: "An Example of a Post", body: "This is a perfect example of a blog post.  Feel free to reference this example in your other applications.  Note that the author of this blog post does not accept responsibility for the contents of this blog post.")
Post.create(id: 2, blog_id: 1, title: "Another Example of a Post", body: "This is yet another example of a blog post.  This one is less perfect than the first one.")
Post.create(id: 3, blog_id: 2, title: "An Utterly Awesome Post", body: "This is a super awesome example of a really good blog post.  You should...like...totally copy this!")
Post.create(id: 4, blog_id: 2, title: "Yet Another Utterly Post", body: "This is yet ANOTHER example of a super awesome blog post.  You should totally copy this one as well!")

Now lets run a db:seed to add in the seed data.

Terminal Commands:
rake db:seed

Great, now it’s time to test our application. The easiest way to test on development is to use the vcap.me domain name. vcamp.me and all subdomains point to localhost, so it’s quite simple to test on development. Start a rails server and navigate to localhost:3000. You’ll see a list of blogs. Clicking on one of these blogs will show the specific blog with it’s posts. You can then click on that specific post to display the post. That’s it! Don’t forget to define model association between Blog and Post.