Find or find_by_id
By Renzo Borgatti on January 27th, 2009
Tagged with: rails, finders, exceptions, programming, ruby
If you are a Rails dev, you probably know that there are two "finder" behaviors in Rails. There is User.find(1) that raises an ActiveRecord::RecordNotFound if the record with the given ID was not found. There are also generated finders like User.find_by_id(1) that return nil instead. Inconsistency? I don't think so. Let's take the typical controller update method:
1 def update
2 @user = User.find(params[:id])
3 respond_to do |format|
4 if @user.update_attributes(params[:user])
5 flash[:notice] = 'User was successfully updated.'
6 format.html { redirect_to(@user) }
7 else
8 format.html { render :action => "edit" }
9 end
10 end
11 endThe find method will raise RecordNotFound if for any reason the searched user cannot be found. Here is the same thing but checking if the returned user instance is null:
1 def update
2 @user = User.find_by_id(params[:id])
3 if @user
4 respond_to do |format|
5 if @user.update_attributes(params[:user])
6 flash[:notice] = 'User was successfully updated.'
7 format.html { redirect_to(@user) }
8 else
9 format.html { render :action => "edit" }
10 end
11 end
12 end
13 endI think the two options both have their place based on the following consideration:
- Probability that the record will be found or not. Maybe the object is not a User but something more volatile like an Object which deleted from the database when it's "consumed".
- There is a general catch mechanism by Rails when an exception raises up to the last possible moment. If you decide not to catch something you know a courtesy page can be presented and the error will be written in the log
- If you decide to catch the exception you need to know what to check and maybe you need to look at the documentation.
- Finally, there are cases where you really need to do something different if the record was not found than leave the error to rise. In this case you have the two options: find with catch or find_by_id with null checking.
Find should be used without catch in 90% of the cases. Because most of the time the ID you're searching for is just the result of a previous request on an object that is still there almost for sure. You can accept the 0.01% of cases where the find call rises an error that you can later track in the error log. If find had the same behavior of find_by_id (let's say for consistency reason), you'll be forced to check for null. If you let it go you end up with "nil when you didn't expect it" which is not as clear as a RecordNotFound.
If you need to catch that the record doesn't exist, then you have two options: catch the RecordNotFound or use find_by_id and check for null. I'm lazy and I prefer the second one: I don't need to check the documentation to remember what to catch.
Happy coding coders!

Robb Shecter
Sun September 27, 2009 at 19:52
But this doesn't mean that the API is consistent.
I just got bitten by this inconsistency: there's nothing in the invocation Object.find vs. Object.find_by_id to denote that an exception will be thrown.
Compare this to Object.save! and Object.save. That sets up a great convention that .find unfortunately doesn't adhere to.