I really like “pretty” or human-friendly URLs. One way to get them with Rails is to use Rick Olson’s PermalinkFu plugin. PermalinkFu takes care of making a field in your model URL-safe. It escapes non-ASCII characters, removes illegal characters (like
/), turns spaces into dashes, and then takes the whole string to lower case.
PermalinkFu is great, but we found it didn’t meet our needs for Better Together.
There are two problems with PermalinkFu. First, it re-creates the permalink every time you save the record, making it not very permanent. Second, the permalinks are not globally unique, so they need to be scoped in some way. This isn’t a problem with blogging engines (with a URL scheme like
/2007/05/15/slug how much uniqueness do you need?), but we needed something that would create globally unique permalinks which we could use as lookup keys.
So we wrote the UrlKey plugin to address those needs.
UrlKey takes the same approach to URL-ifying a string as PermalinkFu, but it also ensures that the key is globally unique by appending a number before saving it. It also only sets the key when you create a new record, so if the name changes, the key will not.
First, install the plugin:
script/plugin install svn://rubyforge.org/var/svn/slantwise/url_key/trunk
Then create a migration for the model you want to give a unique, human-friendly identifier. By default, UrlKey will look for a column called
url_key, but you can call it whatever you want.
class AddUrlKey < ActiveRecord::Migration
add_column :games, :url_key, :string
add_index :games, :url_key
remove_column :games, :url_key
(This code is from a fun little app we’re working on. Keep your eyes peeled: it launches at RailsConf.)
You’re going to want an index on the key column because it will be used in lookup queries.
To your model, add the
has_url_key class method and create a
class Game < ActiveRecord::Base
to_param is used by Rails in URL generation. Usually it returns the ID of the object. By changing it to return the
url_key, we’ll generate friendly URLs automatically.
But since we’re overriding
to_param, we need to make a change to the controller to look up the
Game by its
@game = Game.find_by_url_key(params[:id]) || Game.find(params[:id])
GamesController will first look for the
Game instance by key, and then by ID. That way any old URLs will still work, and you can use IDs if you want to.
UrlKey may not be for everyone.
The uniqueness check will run a query for each iteration until it finds a unique name, so if you have a lot of data this will suck.
Right now, there’s intentionally no facility for re-naming a URL key. It might be a nice feature if combined with a 302 redirect from the old URL.
There are several other plugins that accomplish similar tasks.
PermalinkFu (noted above) is probably the most widely-used.
Others automate the common technique of using the ID in the URL, followed by a slug (for example:
/articles/849-my-title-here). Obie Fernandez explains this approach. Two plugins to accomplish this are ActsAsSluggable and Friendly Param. I think these URLs look ugly, but they are easy to create with Rails.