Trusting in Exceptions To Do What They Do
Nobody likes to see exceptions when running an application; it means something went wrong and you should have been on top of it. But with that, often times I find people will write code to prevent exceptions rather than just allowing them to occur and then using it in their favor.
Take this bit of ruby code in a rails controller as an example:
1class ArticlesController < ApplicationController
2 def show
3 @article = Article.find(params[:id])
4 end
5end
That's pretty solid, but what happens if someone passes in an ID that does not exist? Well in rails' production mode, it would return a 404 error.
Makes sense! That article doesn't exist, so it can't be found. I have dabbled with a little SEO and usability and just a 404 isn't cutting it for me. I think we can redirect our user back on a right track. For one, we know the user is trying to read an article, so why not redirect the user to a list of all the articles and give them a little warning?
Solution One
First check that the article exists, and if not, redirect the user to the index page.
1def show
2 if Article.exists?(params[:id])
3 @article = Article.find(params[:id])
4 else
5 flash[:error] = 'That article does not exist'
6 redirect_to :action => 'index'
7 end
8end
While this works, this requires two queries to our database; one to check it's existence and one to load it. I think there is a better way, and its secret is to go with the flow of the code.
Solution Two
One thing ActiveRecord::Base#find does do is raise an exception when it cannot find anything, an ActiveRecord::RecordNotFound exception. We can use this bit of knowledge to re-write that method to something a little simpler by trusting it to throw an exception and then for us to rescue it.
1def show
2 @article = Article.find(params[:id])
3rescue ActiveRecord::RecordNotFound
4 flash[:error] = 'That article does not exist'
5 redirect_to :action => 'index'
6end
One call to the database and fewer lines of code (not a lot, but still). This time, that nasty little error has been used in our favor; but one mustn't get lazy when using rescue
statements.
Don't Get Lazy
Notice that I used the constant ActiveRecord::RecordNotFound
with the statement. Sure, I could have just written rescue
, but that hurts us in two ways. First, it becomes less readable without it. Having the RecordNotFound
is pretty obvious as to the reason we are executing that statement. Secondly, it can introduce unwanted side effects.
1def show
2 @article = Article.find(params[:id])
3 some_illegal_operation
4rescue
5 flash[:error] = 'That article does not exist'
6 redirect_to :action => 'index'
7end
Whenever that method is called, it will always redirect the user to the index page with that error message. Because we've rescued all errors, we're not getting any notice about how our some_illegal_operation
has caused us grief. This makes debugging and management harder.
The lesson being, don't be lazy in those rescues. Try to avoid a blanket rescue statement if you can.
Comments
comments powered by Disqus