URLs
In terms of viewing our sites, our users currently only have access to a page that displays a random story. To address this issue, we’ll add a new action that displays a single story, along with all of its details, before we implement the voting actions themselves. The story page will serve as a reference point for any given story on the site, as it will contain a range
of information voting actions, voting history, and so on about the story. Before we dive into the creation of our story page, let’s take a quick but important detour. The development of a story page such as this provides the perfect opportunity for a discussion about clean URLs.
Rails translates URLs of a certain format into actions that are invoked on a controller class. The translation, known as routing, is performed by the Rails Routing module.
Consider a URL that has the following format:
http://domain.com/story/show/1
Since most web applications operate on a single domain, we can ignore the domain part of the URL for now. That leaves us with a path that contains several components, separated by forward slashes:
/story/show/1
By default, the Routing module operates as follows:
? The first part of the URL is mapped to the corresponding controller. In this example, /story would route to our StoryController controller.
? The second part of the URL routes to the action name. In this case, the show action would be invoked.
? The third part is used to populate the id entry in the params hash. In our example, the value of params[:id] would be set to 1.
The end result of this routing is that the show action of the StoryController class would be called, and params[:id] would receive a value of 1. This is all well and good, and such a URL structure is certainly neater than many of the more complicated URLs out there on the Web. But an id of 1 isn’t exactly meaningful to our users—they’re more likely to remember the title of a story. Even if the title was slightly modified (with special characters removed, escaped, or replaced), it would still make for a more usable URL.
As I mentioned, converting a title to a URL like this will require a little work on our behalf (setting all characters to lowercase, and replacing spaces with dashes, in this case). But from a usability perspective, it’s quite obvious that the URL relates to the story, so this idea is definitely worth implementing.
To do so, we’ll modify the configuration of the Rails Routing module to support clean URLs like the one above. The routing configuration is stored in the file config/routes.rb, and (once all of the comments are stripped out) looks like this:
ActionController::Routing::Routes.draw do |map|
map.connect ‘:controller/service.wsdl’, :action => ‘wsdl’
map.connect ‘:controller/:action/:id’
end
The two calls to map.connect are Rails routes rules that specify how a URL should be mapped. Routes are read from top to bottom; the route with the lowest priority is the one at the very bottom of the list. In almost all cases, this will be the default route, which, as we learned above, is responsible for mapping a controller name, an action name, and an id:
map.connect ‘:controller/:action/:id’
To implement clean URLs for stories, we’ll insert the following line between the two existing routes:
map.story ‘story/show/:permalink’,
:controller => ‘story’,
:action => ‘show’
This newly added route will match any URLs of the following form:
/story/show/my-shiny-weblog
In this route, we’ve explicitly named the controller and the specific action that should handle a URL in this format. As a result, a URL of this form will always be handled by the show action of our StoryController. The permalink (an abbreviation
of permanent link) that appears at the end of the route is the part of the URL that will be placed in the arams[:permalink] hash area.
The syntax for matching the URL is absolutely identical to that used in the default route: any string prefixed with a colon (with the exception of the :controller and :action reserved words) ends up as a value in the params hash, thus becoming
available to both the controller and its views.
But why are we using map.story instead of the default, map.connect? Well, because we can—what we’re defining here is a named route. A named route is a custom alias for matching an incoming request’s URL. Requests with a URL that matches the defined pattern are directed to a specific page within our application (in this instance, the show action for our StoryController). Assigning the name story to this route makes sense, because we’re directing requests that relate to Story objects. Named routes are available from both controllers and views, and provide an easy way to direct requests to common pages within our application.
Now, all we need is a permalink value. Check out next articles.