Improved dynamic human-readable RESTful routing

Earlier in the week I posted about making dynamic RESTful routes in Rails. At the end of the article, I mentioned that the method I was using might not work in a production environment, and after searching Agile Web Development with Rails for the word “restart,” I confirmed my suspicions. Generating routes on the fly from data runs afoul of caching, requiring the server to be restarted to pick up changes to the routing. That rules it out as a reliable way to generate pretty URLs.

To recap, I’ve already used map.resources to generate the usual http://example.com/sections/1 style of REST URL. I want to be able to route a URL like http://example.com/about to the show method of my Sections controller, as well, to improve human readability (and SEO) of the URL.

Fortunately, I’d already thought about how to do this. The solution is to capture the highly variable URL in a route and process it in the Sections controller to figure out where to send it. The following is now the very last route in my routes.rb file:

  map.section_dispatch ':section',
                       :controller => 'sections',
                       :action => 'show'

This route’s location at the very end of routes.rb is no accident. It’s extremely promiscuous, and it will latch onto any URL of the form http://example.com/foo. If it came earlier in the routing, it would prevent many other routes from being triggered, including the http://example.com/sections REST route that calls the index action to display a list of all the sections.

The route captures whatever comes after the domain part of the URL in the :section parameter and sends it along to the show action in the Sections controller, which is where the really interesting stuff happens:

  def show
    section = params[:section]
    if section.nil?
      section = 'home'
    end
    @section = Section.find_by_name(section)
    unless @section
      render :file => "/404.html",
             :status => :not_found
    end
  end

Remember I said the route is promiscuous? Here’s real proof: even a nil :section parameter gets picked up by the route, which means the root URL for the site (http://example.com) is sent to the show action. The check for section.nil? assigns the Home section as the desired target; Home is where I keep any bits of content that appear on the front page of the site.

The show action then looks for a Section in the database whose name matches whatever the route picked up from the URL, assigning the Section it finds to the instance variable @section for use by the Section view. However, if the name in the URL doesn’t match any Section names, a render call sends the browser a 404 Not Found error and renders an appropriate page so the site visitor knows she’s typed in a bad URL. Eventually, I’ll replace the static 404.html file with a new controller and view that can generate a more useful 404 page with a search box and helpful links, but that’s a project for another day.

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*