Static pages for the enterprise
Posted by Matt Jankowski
Apr 02
HasManyThroughJoshSusser writes about how to handle simple static pages.
This is a fairly simple problem that many sites face. You want to keep use of your layout (and any conditionals in it – like a login/logout area, for example), but it feels like overkill to build a bunch of empty actions just to render static content. Not to mention that it’s a disgusting violation of RESTful action naming (which, personally speaking, would keep me up at night).
In Josh’s example, he already has a home_controller with an index action for his homepage – and he takes advantage of that existing controller by adding a #show action to it, and adding a route which will send his static content page requests to that action:
1 2 |
map.home ':page', :controller => 'home', :action => 'show', :page => /about|contact/ |
This works well, but I don’t really like the overloading of the home_controller #show action there. You’re not “showing a home” ... unless it’s a real estate app and all your static pages are actually For Sale listings … !!!
Here’s an enterprisey static page solution I recently used.
In your routes…1 2 |
map.site 'site/:name', :controller => 'page', :action => 'show' |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
class PageController < ApplicationController verify :params => :name, :only => :show, :redirect_to => :root_url before_filter :ensure_valid, :only => :show def show render :template => "pages/#{current_page}" end protected def current_page params[:name].to_s.downcase end def ensure_valid unless %w(about copyright guidelines help privacy terms).include? current_page render :nothing => true, :status => 404 and return false end end end |
In this approach, I do a few things to help myself sleep at night:
- I’m staying RESTful with action naming – that’s maybe one hour each night
- I’m keeping the logic about what’s a valid page or not in the controller (which is the next best spot to the filesystem) – although I like Josh’s suggestion to use a PAGES constant in the controller as well.
- I’m avoiding the “implicit action” behavior that rails gives you when you have files on disk which match requested paths. I’ve never liked this, so I don’t want to reward bad behavior by taking advantage of it.
We’ve also done the “create an empty SiteController and throw static content in app/views/site/*” approach – and we’ve also tried using Comatose – with mixed results.
What does everyone else do?

Comments on this post
Apr 02
weepy said,
Great idea!
As an extension … how would you deal with subfolders of pages?
Apr 02
bryanl said,
We use a modified version of comatose. I don’t like pushing software to change phone numbers. That combined with serving the cached pages directly from nginx, solves all of our problems.
Apr 02
Matt Powell said,
You say “I’ve never liked this”, but then you make a bit of an ontological leap to calling it “bad behavior”. You’re kind of assuming that anyone who needs to add static content to the site is going to be a Rails programmer. Surely it’s not a stretch to imagine a situation where a company needs to add a static page to their site in a hurry, and don’t want to have to pay a software engineer to open up a controller and change a validation filter?
What if you added some criteria about what sort of files get turned into static pages automatically? What if you only allowed files in a certain directory, and limited FTP access accordingly? Used Liquid instead of ERB? Rendered the pages inside a fairly restricted template? There are all sorts of things you can do to avoid this kind of coupling.
Apr 02
nap said,
I too am a big fan of Comatose for just this sort of thing. Simple and easily integrated.
Apr 03
Steve Tooke said,
I generally don’t have any static pages at all as such. I don’t like having to go through a deployment process to update content, especially when a lot of the time I don’t have to maintain the content! Giving the users access to deploy is recipe for disaster.
Generally I’ll have a pages model and controller and store all of the HTML/Markdown in the DB and use Page Caching to get the performance.
I do think that some pages could benefit from having helpers available though, so I might look at having the controller check for a template and render that if it exists.
Apr 03
Matt Jankowski said,
@weepy – you could probably do ‘site/args’ (there’s an asterisk before args there, textile wont let me) for the route, and then change #current_page to turn the params[:args] Array into a String if you needed nested paths there.
@Matt Powell – I’ve never liked it because I consider it bad behavior, and my number one policy is to not reward bad behavior. But yes, you’re correct about the “ease of addition” argument, it would be nice to be able to have the files on disk be the list of valid fils rather than requiring code changes.
Apr 03
Jeremy Weiskotten said,
Have you considered using template_exists? ? This way you can use the filesystem to validate the page.
Apr 03
Jeremy Weiskotten said,
Sorry, that should be:
template_exists?(“pages/#{current_page}.rhtml”)
Need to include the extension!
Apr 03
Matt Powell said,
@Matt Jankowski: Ah, right. The way the sentence was structured kind of made me think the opposite ;)
Out of interest, why do you consider it inherently bad?
Apr 03
Ken Robertson said,
@Jeremy: Wouldn’t that impose another hit on the filesystem? In a way, I think part of the benefit of this was hinted before of avoiding the checking for if a file exists in the web server config. IE, seen nginx configs that do:
if ( -f $request_filename ) with another if ( -f $request_filename.html ) or ( -f $request_filename/index.html )
It is a number of extra filesystem hits that aren’t needed. Read an article recently about how removing those kind of checks can improve functionality as having your mongrel serve is is faster than going through several filesystem hits.
Apr 03
Brian said,
We used a content management system that stores the meat of the pages, then a combination of method_missing and controller_missing with a good default route to ensure that any URL that didn’t have a rails controller/action would attempt a retrieval from the content management system and render the content using the layout specified in the content itself. It’s super agile for the Enterprisey Busines People, who can add new pages without talking to anyone in IT. The key to the system is setting up good routes.
Apr 03
Brian Ketelsen said,
I wrote up the content thing here: http://blog.p.latyp.us/2007/05/context-sensitive-help-in-rails.html on my blog
Apr 05
Jeremy Weiskotten said,
@Ken, it’s pretty trivial to cache the fact that a file exists. That way you only have to hit the filesystem once per page to determine if it’s a valid request, but you can still leverage Rails. We actually do this on my project in a couple of places—not to serve static content necessarily.
Sorry, comments are closed for this article.
© 2000 - 2009 by thoughtbot, inc.
written by a bushel of tiny robots
Come “ride the toad” on Hoptoad, the app error app.
Thunder Thimble: Brand monitoring for social media.
Widgetfinger: Simple content management for simple websites.
Tee-Bot, funny shirts your friends won't understand!
Umbrella Today: “It’s like totally the simplest weather report ever, Julie.”
Thoughtbot
thoughtbot is a technology consulting firm that provides web application development and design services. We focus on building modern systems, embracing good ideas and delivering elegant solutions.
Interested in learning Rails?
Sign up for our beginning or advanced training.
Archives