ID free pretty permalink based URL’s in Rails
February 28, 2010 2 Comments
Slug based URLS
I love the way that rails allows you to make use of REST and create meaningful urls for your web applications. The only problem is that, although these urls are meaningful, having to use a model’s id in the URL can be pretty ugly.
Replacing the model’s id with a meaningful title can be useful to both users and for SEO purposes. A lot of approaches in rails make use of the fact that calling to_i on a string will start at the begining of a string and work forward until there are no numbers like so:
"1".to_i # => 1 "123four".to_i => 123
This has led to a number of rails plugins that allow you to make id and permalink based urls like the following:
acts_as_friendly_param by Chris Farms is one of the plugins that you can use to achieve this.
Rails Slugs Are Bad
Slugs are bad kids. Okay? (There’s actually nothing wrong with slugs that contain an ID and while I don’t have any objection to creating URL’s like the above for some things they just seem unnecessary).
There is however another approach that allows you to use a unique reference for each record in your models. If you store a unique permalink for each model then you can use that link in your models finder in order to find that record like so:
The only problem with this approach is you have to edit all of your code to use the different finder method. Enter the slugs_are_bad plugin. I created the plugin to override rails’ default finder methods. The plugin is based on a couple of other plugins acts_as_friendly_param by Chris Farms and permalink_fu by Technoweenie but was adapted to suit my needs a little more.
Using the plugin
The slugs_are_bad plugin forms a drop-in replacement to create pretty, id-free, urls. In most cases you don’t have to make any changes from the default rails scaffold apart from adding the slugs_are_bad plugin.
app/models/user.rb slugs_are_bad(:permalink_attribute, :generate_from) # ie. slugs_are_bad(:permalink, :title) # will automatically generate a slug-less permalink from the title attribute and store it in the model. Then in your view link_to 'User', User.new(:name => 'foo bar') # nothing new needed here # Generates /users/foo-bar instead of /users/1 Controllers # create the user as usual User.create!(:name => 'foo bar') # To find the model with its nothing else is required User.find(params[:id]) # nothing needed here (where id will be 'foo-bar') # you could also manually specify the permalink in this instance if wanted. User.create!(:name => 'foo-bar', :permalink => 'foo')
However, there are still a few things missing. The plugin is not quite as flexible as I would like it to be and also there are no tests for it at the moment. I have a set of tests that I use in a production site to ensure the plugin works for that project but I havent created any tests for the standalone plugin itself yet. I should also note that the plugin was been created for rails 2.3.x, I know that rails 2.3 is obsolete now :P but hey some people might find it useful.
If anyone wants to expand upon the plugin then fork away on github and ping me the changes.