Some Background
On one of our projects at PLANET ARGON, we have a requirement for displaying the username instead of the user model id in the url. For example:
http://ourapplication.com/users/1 http://ourapplication.com/users/1/assets should be http://ourapplication.com/users/myusername http://ourapplication.com/users/myusername/assets
The Resource Hacks Plugin
Luckily for us, Jeremy Voorhis already created a plugin resource_hacks.
Installing the plugin, allows us to add a member_path to our route definitions. Let's look at some examples.
Without the member path:
map.resources :users do |users| users.resources :assets end
This gives us the default named routes associated with restful rails, such as users_url, assets_url(@user), etc.
Let's add the new member_path:
map.resources :users, :member_path => '/users/:permalink' do |users| users.resources :assets end
This definition gives us access to a params[:permalink] in our users controller. Some examples:
http://ourapplication.com/users/myusername => params[:permalink] would equal 'myusername' http://ourapplication.com/users/simple-user => params[:permalink] would equal 'simple-user' http://ourapplication.com/users/1 => params[:permalink] would equal '1'
Our Next Steps
So how do we use this :permalink attribute, first we need to create a new model attribute for our user called permalink with a corresponding permalink column on the users table. Second we need to update our users controller to look for the permalink parameter instead of id parameter. Let's get started.
Updating Our User Model
Along with adding a new 'permalink' column on the User model, we also need to update the #to_param like so:
def to_param
"#{username.gsub(/[^a-z0-9]+/i, '-')}" if self.username
end
The #to_param method is used by the named routes in restful rails. The default is to return the model id. What our definition says is to use the username instead of the model id, removing any unfriendly url characters and replacing them with a '-'. For example:
my.user.name => would become 'my-user-name'.
Updating Our User Controller
We need to update the way we find the user in the controller. Hopefully, we are using a before_filter that will find our user, so we only have to update the code in one place. For example:
class UsersController < ApplicationController before_filter :find_user private def find_user @user = User.find(params[:id]) end end
The initial step in updating the user find method, is to use the permalink parameter instead of the id parameter, so let's do that:
def find_user @user = User.find_by_permalink(params[:permalink]) end
Simple, right? But what if we didn't used restful named routes in our link_to definitions, like so:
link_to "show", :controller => :users, :action => :show, :id => @user.id
Which would render something like the following, if the user id is 1:
http://ourapplication.com/users/1
When our find_user method received this request, the permalink value would equal '1' and would in most cases not return a user. We can handle this with a little addition to our find_user method, like so:
def find_user @user = User.find_by_permalink(params[:permalink]) || User.find(params[:permalink]) end
With this update, if the User.find_by_permalink fails, then a call to User.find is made (hopefully with the model id).
Summarize
Using the resource_hacks plugin, we can get pretty urls with restful rails with a little bit of effort on our part.
NOTE: We would probably want to add some error checking in a real application :)