Home > Articles > Programming > Ruby

  • Print
  • + Share This
This chapter is from the book

This chapter is from the book

Default Parameters and the url_for Method

The URL generation techniques you're likely to use—link_to, redirect_to, and friends—are actually wrappers around a lower-level method called url_for. It's worth looking at url_for on its own terms, because you learn something about how Rails generates URLs. (And you might want to call url_for on its own at some point.)

The url_for method's job is to generate a URL from your specifications, married to the rules in the route it finds to be a match. This method abhors a vacuum: In generating a URL, it likes to fill in as many fields as possible. To that end, if it can't find a value for a particular field from the information in the hash you've given it, it looks for a value in the current request parameters.

In other words, in the face of missing values for URL segments, url_for defaults to the current values for :controller, :action, and, where appropriate, other parameters required by the route.

This means that you can economize on repeating information, if you're staying inside the same controller. For example, inside a show view for a template belonging to the auctions controller, you could create a link to the edit action like this:

<%= link_to "Edit auction", :action => "edit", :id => @auction.id %>

Assuming that this view is only ever rendered by actions in the auctions controller, the current controller at the time of the rendering will always be auctions. Because there's no :controller specified in the URL hash, the generator will fall back on auctions, and based on the default route (:controller/:action/:id), it will come up with this (for auction 5):

<a href="http://localhost:3000/auctions/edit/5">Edit auction</a>

The same is true of the action. If you don't supply an :action key, then the current action will be interpolated. Keep in mind, though, that it's pretty common for one action to render a template that belongs to another. So it's less likely that you'll want to let the URL generator fall back on the current action than on the current controller.

What Happened to :id?

Note that in that last example, we defaulted on :controller but we had to provide a value for :id. That's because of the way defaults work in the url_for method. What happens is that the route generator marches along the template segments, from left to right—in the default case like this:

:controller/:action/:id

And it fills in the fields based on the parameters from the current request until it hits one where you've provided a value:

:controller/:action/:id
  default!          provided!

When it hits one that you've provided, it checks to see whether what you've provided is the default it would have used anyway. Since we're using a show template as our example, and the link is to an edit action, we're not using the default value for :action.

Once it hits a non-default value, url_for stops using defaults entirely. It figures that once you've branched away from the defaults, you want to keep branching. So the nondefault field and all fields to its right cease to fall back on the current request for default values.

That's why there's a specific value for :id, even though it may well be the same as the params[:id] value left over from the previous request.

Pop quiz: What would happen if you switched the default route to this?

map.connect ':controller/:id/:action'

And then you did this in the show.rhtml template:

<%= link_to "Edit this auction", :action => "edit" %>

Answer: Since :id is no longer to the right of :action, but to its left, the generator would happily fill in both :controller and :id from their values in the current request. It would then use "edit" in the :action field, since we've hard-coded that. There's nothing to the right of :action, so at that point everything's done.

So if this is the show view for auction 5, we'd get the same hyperlink as before—almost. Since the default route changed, so would the ordering of the URL fields:

<a href="http://localhost:3000/auctions/5/edit">Edit this auction</a>

There's no advantage to actually doing this. The point, rather, is to get a feel for how the routing system works by seeing what happens when you tweak it.

  • + Share This
  • 🔖 Save To Your Account