In several of my controllers, I have code looking something like this:
@user
.cars.find_by_id(params[:car_id])
.try(:wheels).find_by_id(params[:wheel_id])
.try(:screews).where(id: ids)
This can of course raise the exception: NoMethodError: undefined method `where' for nil:NilClass. I would like to be able to give my user more information about what went wrong, in this case, that the user had no cars with the specified id.
Changing find_by_id to find would cause the above code to raise an ActiveRecord::RecordNotFound exception, which I could handle, but in that case, how do I find out if the cars or the wheels were missing? The RecordNotFound exception doesn't really contain that much information. Is there any good way to handle this type of thing well? Preferably, something that is general enough that I could easily reuse the code in my other controllers.
I would say that your code smells and you should refactor it.
I'm not saying that you need to worship the Law of Demeter (DHH has a pretty good post about this) or other CS concepts but rather that catching the exceptions in this case would not make the situation better but is like building a safety net around a tower with a spotty foundation so that the inhabitants can jump out the window when it crumbles.
Lets go through it row by row:
.find_by_id
is a so called dynamic finder method which relies on method missing, they where extracted from the core in Rails 4. Use.find
instead, it is forward compatible and does not have the overhead of the method missing metamagic.We don't need to scope our query from
@user
since we already have a unique id for cars.If we want to fetch both the
Car
and theWheel
we should be using a join query to make an effective SQL query.But if what we really need are the screws we can do this instead:
So to reiterate: