Building Static Content Pages in Rails6

Share: Twitter Facebook LinkedIn

One of the most common starting points of a website or web application are pages that provide mostly unchanging content. These are often referred to as “static” pages.

There are usually three reasons for this; one, most web applications or sites are trying to build a connection with the visitor, and I’m not talking about the technical network connection. From the second you arrive at a website, the company or people behind it are trying to create a human connection with you. Secondly, most web applications / sites rely on communication with a database. While this communication is speedier than ever, Google has shown statistics in the past that the bounce rate (eg. visitors leaving your site) is proportional to the time it takes to load the starting home page. In other words, the longer it takes to load your website, the faster people are leaving it. The third reason, is that static pages are often used as “landing pages” - content that highlights some product or service that you want to attract attention to.

With that in mind, the focus of this tutorial is to go through the process of putting some static pages together in Rails 6.

I’m super excited to help you make this happen!

Assumptions & Goals

  • You are on a system that has Ruby on Rails 6 setup and available to you
  • We want to have our application start on a “default” Home page

Getting Started

Open your shell and head to your favorite project directory. Then let’s rails new the project!

1
rails new rails-static-pages

Once Rails and the bundler finish setting up the new project, let’s get it committed to the git repository that Rails initializes for you.

1
2
3
cd rails-static-pages
git add .
git commit -m "initial commit"

NB: I may forget to write / mention it from time to time, but it’s *usually* a good idea to “sanity check” your system configuration by running bin/rails s before you do anything else, just to quickly verify your app loads ok.

Now let’s get a welcome page going. Usually, you’re explaining your product, or your personal / company goals. Let’s use the handy rails generate to help us out.

NB: remember you can use rails g as a shortcut for rails generate

1
rails g controller pages 

Once the generation is finished, find app/controllers/pages_controller.rb. By default, it’s an empty controller, so let’s add a new action to handle our static page, called home.

1
2
3
4
class PagesController < ApplicationController
    def home
    end
end

With an action in our controller defined, let’s create a view to match. You’ll notice that an app/views/pages folder was created during the generation process. To match the view with our controller action, create a file app/views/pages/home.html.erb.

You can put anything in there…right now we just need to verify that the view we want is what we see.

1
<h1>Home Page!</h1>

creating the route

With the controller and view setup, let’s update Rails so that it can find it. Open up the config/routes.rb:

1
2
3
Rails.application.routes.draw do
    get "/home" => "pages#home
end

You’re adding a url mapping here. This new mapping means that whenever your browser uses /home, then Rails can find the controller / view template you are trying to get to.

Let’s test it out!

start the server

Since we have a controller / view template configured, let’s see how it looks right now in the browser. From the root of your project folder:

1
bin/rails s

Open your browser to http://localhost:3000/home. You should be able to confirm the words Home Page!!

adding an rspec test for this controller

With our proper home page displaying correctly, let’s add a unit test for it! By default, Rails sets us up with RSpec for handling tests.

Navigate to test/controllers/pages_controller_test.rb. To help demonstrate, let’s create a unit test for the home static page, but let’s start off making it “fail”.

The test itself should be empty (or have some default comments inserted by Rails), so let’s create:

1
2
3
4
5
6
class PagesControllerTest < ActionDispatch::IntegrationTest
    test "should get the home page" do
        get pages_home_url
        assert_response :success
    end
end

Now run bin/rails test.

NB: If you get a screen full of strange errors, it’s likely due to the fact that we haven’t created our database yet. Go ahead and run bin/rails db:migrate

If all goes well, you should see some output like:

1
2
3
4
5
# Running:

.

1 runs, 1 assertions, 0 failures, 0 errors, 0 skips

There’s some other timing information in there which will perhaps be slightly different from machine to machine, but the important output is seeing that 1 assertions display - eg. test passed!

now we break the unit test

Just to help underscore how helpful test coverage is as your application grows, let’s change our url in the config/routes.rb file without updating the unit test.

Change:

1
  get "/home" => "pages#home"

To:

1
  get "/marketing" => "pages#home"

Now run the unit tests again with bin/rails test… what happens?

We get an error!

Imagine this app grows over time, and has over 50 URL mappings. Not all of them would be to static pages, but just 50+ route mappings in general. How will you be able to keep track of which ones are still active, and which ones are not used at all anymore?

adding a root Route

With our static pages configured, there’s the question of what Rails refers to as the root Route. This is usually the default index page of your application or website that is displayed when you visit the website - in our specific case, this would be the “default” page that we want shown when we hit http://localhost:3000.

Let’s go back into config/routes.rb and add an entry for root:

1
2
3
4
5
6
Rails.application.routes.draw do
    get "/home" => "pages#home

    # define our root / default route
    root "pages#home"
end

In other words, we’re creating another url mapping for the default / root Route.

Now open your browser, and head to http://localhost:3000. Instead of the familiar “Yay! You’re on Rails!” page, Rails will take you to pages#home.

Let’s update our test coverage!

Go back into test/controllers/pages_controller_test.rb, and add a new unit test for root_url:

1
2
3
4
test "root route should get the home page" do
    get root_url
    assert_response :success
end

Are we done? Not quite.

Where’s the flaw in our testing strategy? Hint: content

Our unit tests are able to determine if the url in question resolves, but not that we’ve got the correct page contents.

How do we do this?

One way is to make use of some special ruby functions, provide and yield. But to REALLY help illustrate how handy they are, we’re going to quickly add another static page, About.

adding an About static page

This is straight repetition of what we already did for the Home static page, so this should take no time at all.

For a good challenge, stop reading right here and try adding it for yourself (don’t forget the new unit test!).

Back? Okay, let’s do a recap.

app/controllers/pages_controller.rb:

1
2
3
4
5
6
7
8
9
class PagesController < ApplicationController
    # Action to handle the About view
    def about
    end

    # Action to handle the Home view
    def home
    end
end

That’s the PagesController.

Let’s add a new page for the HTML template, app/views/pages/about.html.erb:

1
<h1>About</h1>

Next, we update the config/routes.rb file:

1
2
3
4
5
6
7
Rails.application.routes.draw do
    get "/about" => "pages#about"
    get "/home" => "pages#home"

    # define our root / default route
    root "pages#home"
end

Finally, update the PagesControllerTest in test/controllers/pages_controller_test.rb:

1
2
3
4
  test "should get the about page" do
    get about_url
    assert_response :success
  end

Phew. Not too bad at all, considering we added a brand new route!

setting up provide and yield (and title)

Our strategy is to update the actual document <title></title> element within each view. This will help us greatly when updating our tests to confirm the page we want is the page we get.

First, let’s update our main application layout file, app/views/layouts/application.html.erb:

1
2
3
4
5
6
<% provide(:title, "") %>
<!DOCTYPE html>
<html>
    <head>
        <title><%= yield(:title) %></title>
        *snip*

The key updates here is to add the provide call at the top of the page. Next, we’re going to update the <title> section to call yield(:title).

Next, go back to your Home view template at app/views/pages/home.html.erb:

1
2
<% provide(title, "Home") %>
<h1>Home</h1>

Next, return to the About view template at app/views/pages/about.html.erb:

1
2
<% provide(title, "About") %>
<h1>About</h1>

Finally, let’s go back to our unit tests and update them to test the <title> elements within each request:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class PagesControllerTest < ActionDispatch::IntegrationTest
    
    test "should get the about page" do
        get about_url
        assert_response :success
        assert_select "title", "About"
    end

    test "should get the home page" do
        get home_url
        assert_response :success
        assert_select "title", "Home"
    end

    test "root route should get the home page" do
        get root_url
        assert_response :success
        assert_select "title", "Home"
    end
end

We went through quite a bit of steps here. To recap, we updated the main application layout file, to yield() any given title string within each view. Then we went into our 2 views, and just made sure to specific a different title for each one - Home and About.

Lastly, we updated the unit tests to call assert_select to query our title element within each resulting page to make sure that the title element we’re expecting, is the one we’re getting.

Go ahead and run bin/rails test to verify everything.

You can find the code for this project on my github - https://github.com/erikyuzwa/rails-static-pages

If you found this tutorial helpful, please share it!

Comments

You are seeing this because your Disqus shortname is not properly set. To configure Disqus, you should edit your _config.yml to include either a disqus.shortname variable.

If you do not wish to use Disqus, override the comments.html partial for this theme.