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'
And in PageController…
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:

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

weepy

Apr 02

weepy said,

Great idea!

As an extension … how would you deal with subfolders of pages?

bryanl

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.

Matt Powell

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.

nap

Apr 02

nap said,

I too am a big fan of Comatose for just this sort of thing. Simple and easily integrated.

Steve Tooke

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.

Matt Jankowski

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.

Jeremy Weiskotten

Apr 03

Jeremy Weiskotten said,

Have you considered using template_exists? ? This way you can use the filesystem to validate the page.

def ensure_valid
  unless template_exists?("pages/#{current_page}")
    render :nothing => true, :status => 404 and return false
  end
end
Jeremy Weiskotten

Apr 03

Jeremy Weiskotten said,

Sorry, that should be:

template_exists?(“pages/#{current_page}.rhtml”)

Need to include the extension!

Matt Powell

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?

Ken Robertson

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.

Brian

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.

Brian Ketelsen

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

Jeremy Weiskotten

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