There’s one thing that almost every web app and web site needs: static pages

Many Rails users turn to a gem like Thoughtbot’s High Voltage to address this need. I tend not to do that for a few reasons:

Having cleared that, how do we go about creating static pages in Rails?

Each static page needs 3 things:

  1. A controller action
  2. A view
  3. A route

Let’s step through the process of providing these things for a static FAQ page.

Step 1 is easy peasy. Create a PagesController and add an action method to it:

class PagesController < ApplicationController
  def faq

That call to render is optional, but I think it’s clearer than just leaving the method empty.

Step 2 is required for any static page because it has to have some content. Create a view file:

$ touch app/views/pages/faq.html.erb

The brute force way to handle Step 3 would be to manually add a get route1 to config/routes.rb, like this:

MyApp::Application.routes.draw do
  # other routes ...
  get "/faq", to: "pages#faq", as: :faq_page

That works, but gets pretty un-DRY as we add more static pages. Instead, we can dynamically generate the routes based on the controller’s action methods.

We put this in our routes in place of the manual route:2

MyApp::Application.routes.draw do
  # other routes ...
  PagesController.action_methods.each do |action|
    get "/#{action}", to: "pages##{action}", as: "#{action}_page"

From now on we can skip Step 3 when adding new static pages!

We are also dynamically naming these routes so we can use all of the related helper methods, like faq_page_path and friends.

Good, not great

I’ve found this to be a pretty good solution, but it’s not ideal.

Ideally, we’d be able to sniff out the view files and generate action methods for them, but this has never been enough of a pain point for me to dig in and come up with a solution.

If you know how to get that done in an elegant fashion, please let me know!

  1. you can also use matches, but Rails 4 and beyond prefer get, post, etc.

  2. Rails puts controllers’ public instance methods into action_methods, so any methods that you don’t want having routes generated for need to be protected or private.