Saturday, June 14, 2008

Using rails routing for easy search engine friendly URL changes


In this entry I am going to show how to, in a very search engine friendly way, update a URL for a particular page in a rails website. The rails routing system provides named routes, which are a very nice way to change the URL for a particular page and have all of your links update without having to change code in a bunch of different places. That is a great start on moving a page, but it doesn't take care of any links from outside of your site that might be pointing at the old URL. The best way to handle that is to make it so that the old URL redirects to the new URL, your end users won't notice a difference but the search engines bots will take note and update their indexes.


First off if you are not familiar with routing and named routes in rails you might want to read up on it. The rails API docs have some pretty good material on routes located at http://api.rubyonrails.org/classes/ActionController/Routing.htm


Lets pretend we are moving "http://example.com/users/signup" to "http://example.com/signup". Our original named route might look something like this:


map.signup '/users/signup', :controller => 'users', :action => 'signup'


And the code for a link that pointed to it would look something like this:


<%= link_to 'Signup', signup_url %>

To change our URL we would simply change the named route to look like this:

map.signup '/signup', :controller => 'users', :action => 'signup'

All of the links that used the named route would automatically change to link to the new URL but the old URL would stop working. To change that we could add the following to the routes file

map.signup_old '/users/signup', :controller => 'users', :action => 'signup', :old_url => true

This would allow the old URL to work, but it would be best if we made it so that it redirected to the new URL so that search engines would switch over to the new URL faster. Notice that there is an additional parameter that is defined on the end of the second route, this makes it so that we can easily differentiate what URL a visitor arrived on the page with.

Now all we have to do is make a method we can use in a before filter that redirects the user to the same action, the redirect will automatically use the first route we defined (as long as we leave them in that order in the routes file). In the users controller we would add the following code:

before_filter :redirect_to_new_url

def redirect_to_new_url
if params[:old_url] == true
headers["Status"] = "301 Moved Permanently"
redirect_to({:action => action_name})
end
end
The lines in the route file would look like this:

map.signup '/signup', :controller => 'users', :action => 'signup'
map.signup_old '/users/signup', :controller => 'users', :action => 'signup', :old_url => true

Once you have this in place you only have to add a new route to move a URL in a search engine friendly way, and when you decide that all the external links and search engines are updated all you have to do is get rid of the route that points to the old URL.

Sunday, June 1, 2008

What DRY is really about


Lets start with what the acronym DRY stands for, Do not Repeat Yourself. That sounds like a pretty good idea, it makes sense, but what are we really trying to accomplish by keeping our code DRY? Ok, that was a rhetorical question but I know someone out there would probably respond by saying "Not repeating ourselves!", to which the audience would chuckle because everyone knows that isn't what DRY is about. DRY is really about making high quality maintainable code.




Lately I have been finding things in the code base that I am working with that indicates that some programmers have taken DRY way too literally and use it in places that reduce code readability and maintainability. For example I found something similar to the following ruby on rails code a while back.


if something == true
temp_var = {:action => "foo"}
else
temp_var = {:action => "bar"}
end
redirect_to {:controller => "main"}.merge(temp_var)

In this example of code, they are essentially avoiding a tiny bit of duplication, defining the controller. The thing is this code is more difficult to understand than the following.

if something == true
redirect_to :controller => "main", :action => "foo"
else
redirect_to :controller => "main", :action => "bar"
end

In the second example there is a slight bit of duplication, but it is also easier to figure out what the code is doing as you read through your if statement. That increased readability makes the code just a little easier to maintain. Additionally if you were thinking about changing this code there is a good chance that you would be changing the controller argument for only one of the two redirect calls, in which case the second code example is much simpler to edit. Of course there is another option, if the person writing this code originally had been aware of some additional functionality that the rails framework offers they would have used a named route which would get rid of all of the duplication while maintaining great code readability.

if something == true
redirect_to foo_url
else
redirect_to bar_url
end

Now that you know DRY isn't just about not repeating yourself, take a little extra time before removing duplication and think about whether the change gets you closer to the goal of having readable and maintainable code. If the change doesn't make your code easier to maintain consider leaving the duplication or even better do some research and find a solution that gets rid of the duplication without making things harder to read or maintain.