Quick and easy static pages in Rails

February 10th, 2010

Sometimes you have a Rails app, the core functionality is not a CMS, and you need to add some static pages to your app that don’t get changed often, eg, an about us page, privacy policy, etc.

This post shows you a quick and easy way to do that in about 60 seconds, without using a database model or having to write any sort of CMS.

1) First off, add some routes for your static pages. Here I want an FAQ page, About page, Privacy page, and Terms and Conditions pages.

  map.with_options :controller => 'static_pages', :action => 'show' do |static_page|
    static_page.faq 'faq', :id => 'faq'
    static_page.about 'about', :id => 'about'
    static_page.privacy 'privacy', :id => 'privacy'
    static_page.terms_and_conditions 'terms_and_conditions', :id => 'terms_and_conditions'
  end

2) Add a functional test so we know it works now, and we can check we don’t retrospectively break it later. Create a new file in test/functionals/static_pages_controller_test.rb:

require File.dirname(__FILE__) + '/../test_helper'

class StaticPagesControllerTest < ActionController::TestCase

  should_route :get, '/privacy', :action => 'show', :id => 'privacy'
  should_route :get, '/terms_and_conditions', :action => 'show', :id => 'terms_and_conditions'
  should_route :get, '/faq', :action => 'show', :id => 'faq'
  should_route :get, '/about', :action => 'show', :id => 'about'

  %w(privacy about terms_and_conditions faq).each do |static_page_template|
    context "Accessing the #{static_page_template} page" do
      setup do
        get :show, :id => static_page_template
      end

      should_respond_with :success
      should_render_template static_page_template
    end
  end
end

This is fairly simple but covers everything we need it to. It just checks your routing is setup and the pages render from the static template files correctly.

3) Create your controller, app/controllers/static_pages_controller.rb:

class StaticPagesController < ApplicationController

  def show
    page = params[:id]
    render :template => "static_pages/#{params[:id]}"
  end

end

Again, really simple. Use a RESTful ’show’ method to select a template to render, then render it.

4) from a shell, make a directory for your pages to live in, and create the empty files:

$ mkdir app/views/static_pages
$ touch app/views/static_pages/about.html.erb
$ touch app/views/static_pages/faq.html.erb
$ touch app/views/static_pages/privacy.html.erb
$ touch app/views/static_pages/terms_and_conditions.html.erb

5) Check it works:

$ ruby test/functional/static_pages_controller_test.rb
Loaded suite test/functional/static_pages_controller_test
Started
............
Finished in 0.156958 seconds.

12 tests, 24 assertions, 0 failures, 0 errors

Done! Now put the content in your pages, test, check in and deploy.

Categories: Rails

Tags: , , Leave a comment

Like This Post?

Subscribe for more...

16 Comments

  1. daeltar

    This line does not seem to be safe (I would ad some include? test):
    render :template => “static_pages/#{params[:id]}”

  2. rds

    map.page ‘:id’, :controller => ‘pages’, :action => ’show’, :requirements => { :id => /faq|about|privacy/ }

  3. Henrik N

    You could also route them as different actions of the same controller, and then use templates with the same name as the action.

    The controller doesn’t need to explicitly define the action if there is a template with the conventional name.

  4. Ben Hughes

    Be careful… you should sanitize that params[:id] before just rendering a file:

    render :template => “static_pages/#{params[:id]}”

    Nasty things may happen.

  5. Arthur Gunn

    Nice.
    I have a very similar method.

    # in routes.rb:
    map.pages ‘:page’, :controller => ‘pages’, :action => ’show’, :page => /faq|about|privacy|terms/

    #pages_controller.rb:
    class PagesController params[:page]
    end
    end

    By adding a requirement on the page param, we can avoid a lot of repetition.
    link to them like so:
    pages_path(‘about’), pages_path(‘faq’) etc.

  6. Olek

    Route definitions could’ve been DRYer, and I think you don’t need page in #show action.

  7. Arthur Gunn

    So the pages_controller was butchered there. The relevant bit is that pages_controller’s show method just has one line:

    render :action => params[:page]

    …hope that comes through

  8. Sander

    Even less code and easier to extend:
    # Routes.rb:
    map.connect ‘:page.html’, :controller => ‘main’, :action => ‘render_html’

    # main_controller.rb
    def render_html
    render :template => “main/#{params[:page]}” if template_exists?(“main/#{params[:page]}”)
    end

  9. Luke

    I wanted to share my approach to this problem. Thanks for the post!

    http://www.lukecowell.com/archives/2010/2/11/static_pages_in_rails/

  10. Mark Richman

    Note the functional test requires Shoulda for the should_route method.

  11. ö

    I find it DRYer to pass a catch-all *path parameter to the static_pages controller and check in the controller if the corresponding template exists, as described in http://snafu.diarrhea.ch/blog/article/4-serving-static-content-with-rails . This way you don’t need to add a custom route and restart Rails every time you add a page. It also allows for mod_rewrite-like functionality and subdirectories.

  12. Aleksey Gureiev

    Hi,

    Can I suggest a slightly shorter approach? Here’s what I have in mind:

    1) Create an empty StaticPages controller and write the tests. As you will see, I’m accessing pages through the same URLs, but not calling a :show action, but calling actions with the same name as the page (:faq, :about etc). This is a great way to customize each action if necessary and leave everything else untouched.

    http://gist.github.com/303124

    2) Create routes:

    http://gist.github.com/303127

    3) Create ,html.erb files for each of the pages in your app/views/static_pages directory.

    As you can see, I’m not adding any actions to the StaticPagesController. It is absolutely empty. When Rails see the action that is not in the controller, it first checks if the template is there and only if it’s not, throws an exception.

    Now to customize any of the pages (for example, to fetch some page-related data, like contacts from the database etc), you can add an action method to the controller, assign instance variables and start using them in that template only.

    Hope you like it.

  13. Aleksey Gureiev

    Evidently, it didn’t like my using of angle brackets in step 3. Here’s what I meant: “Create _page_.html.erb files …”

  14. Aleksey Gureiev

    @ö: Having inexplicit routes like that isn’t a very good thing. First of all, you will be running code checking for static pages whenever any missing resource is requested (images etc). Secondly, unless you have a CMS of a sort, stating the names of static pages is way better for readability. You don’t have too many of them in your average app.

  15. admin

    @ daeltar & @Ben Hughes – the line is not unsafe as the controller action would never be invoked unless the id is in the pre-approved routing. (I never used the default /:controller/:action/:id routes). I suppose if someone does use those default routes it might be worth looking into.

    @Aleksey Gureiev – I liked your approach it was a bit DRYer but still clear and easy to comprehend quickly.

    @ Luke – Thanks for the input

    @ Mark Richman – Yep, i’m using shoulda for testing.

  16. Fadhli Rahim

    Hi, Josh Susser from has_many :through blog had long ago blogged something very similiar. Here’s the link

    http://blog.hasmanythrough.com/2008/4/2/simple-pages

Feed

http://www.mendable.com / Quick and easy static pages in Rails