Rescuing Ruby 9
Now, before anyone goes crazy, this article has nothing to do with saving ruby from something, i just couldnt think of a better title. We will be talking about rescue and some cool tricks that can be done with it.
As most of you vets know, every thing in Ruby is an object. What most noobs dont realize is how powerful that can be and how concise your code can become. Of course, the flip side of that problem is what i call, newbie-come-expert. It’s basically the stage in a noob’s study of a language where they think they know enough to write really cool software. What actually happens is they end up with code like this:
@objects.map {|e| [e.id, e.title]}.sort {|a, b| a.last <=> b.last}.map {|e| e.join(" ")}.join("\n")
Lovely.
Dont worry noobs, everyone does it. I’m still trying to undo it from some of my older code.
Anyway, my point is, sometimes Ruby’s dynamicism (say that 3 times fast) can lead to messy code so use this tip wisely.
On to the actual tip.
So, like I said before, EVERYTHING in Ruby is an object, even the remains of a rescue clause or an inline rescue which is what we will be playing with here.
How many of you have done this:
@person = Person.find(some_id)
only to get the following error:
ActiveRecord::RecordNotFound: Couldn't find Person with ID=22
It’s pretty annoying, mainly because this type of thing could easily happen in a live app. A user goes to find something that isnt there. One way around it is to have a rescue at the end of the method, but what if you have multiple finds that could throw errors? What if you still need to be able to proceed with whatever you are doing, even if the object isnt found?
This is where inline rescue’s can help.
Now you can pull all of your finds together and end them with a rescue.
Example: Let’s say you had the following code:@person = Person.find(params[:id]) @employee = Employee.find(params[:employee_id])
Now, if either of these finds fail, you will get an error. Instead of rescuing the entire method, you can resuce each statement and make the return value nil and check for nil like you normally would.
@person = Person.find(params[:id]) rescue nil @employee = Employee.find(params[:employee_id]) rescue nil
Now when you cant find the ID, it will rescue and just set the variables to nil and move on.
You can also set the rescues to another default datatype instead of nil.
Now, you may be saying, “Wait, why would a user need to enter an id”. Well, one example is an order id, or a tracking number. Anyway, I have run across this a couple of times and thought i’d write something about it.
until next time.
—jake
Trackbacks
Use the following link to trackback from your own site:
http://blog.flvorful.com/articles/trackback/2
What kind of users do you have that are searching by id keys? ;)
@person = Person.find(params[:id]) should really be
@person = Person.find(:first, params[:id])
If there is no record found, it will properly return nil, not throw an exception.
Good tip. Though with ActiveRecord I tend to use find_by_id when I dont want it to throw an exception.
Why does this feel like the Ruby equivalent of VB’s “on error resume next”? :)
Personally I can’t see how you’d want to carry on when objects aren’t found, but if I ever do I’ll keep your advice in mind.
Thanks for the post.
Another approach is to use
find_by_idinstead offind:If a person with this id isn’t found, no exception will be raised, and
@personwill be set to nil.I prefer find_by_id.
I have found using an inline rescue is a nice way to avoid implementing method_missing, polymorphism, or some complex conditional logic. So, for example you have a simple ruby script with behavior rapped up in some methods where your last statement is:
send ARGV[1]
Now you want to extend this to execute some behavior in a module, class, or object instance, so:
send ARGV[1] rescue MyModule.send ARGV[1] unless ARGV[1].nil?
It is short and to the point with the right hand condition keeping all in check.Inline rescue is sweet chaining with no method missing magic!
send ARGV[1] rescue MyModule.send ARGV[1] unless ARGV[1].nil?
Sorry for the multi comment. I thought it had been lost and I did not submit, now I know it was due to moderator approval. Thanks
yes, most of you are correct. find_by_id would be much cleaner. I’m sorry, i should have used better examples. I was just trying to illustrate a use of inline rescues. I dont really use it for that, but i also didnt want to dig through lines of source code to find a realy example (sorry there’s just too many lines and not enough time :) )
—jake