Rails 2.0 will ship with a new ActionController class method called:
1 | rescue_from Exception, :with => :method_name |
…which will rescue all exceptions in actions, thrown when the application runs in production mode with the given method.
As I read that I thought: Why only in production mode? It seems the Rails team designed this tool to rescue exceptions that were not wanted to be thrown, but why?
In our newest application I had the problem that we wanted to be able to display e.g. a login box on several places all over the app.
We created a login box partial which displayed a form that posted the login to the login action at the user controller. But what if the login fails? We ended up with something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | def login unless request.post? flash[:notice] = :login_failed redirect_to_last_page return end user = User.authenticate(params[:nickname], params[:password]) unless user flash[:notice] = :login_failed redirect_to_last_page return end #doing the login stuff in session... end |
Mhm… two times the exact same unless block just with a different condition? Not very DRY, isn’t it?
Ok, we’re nearly there where this post started. Why not just throw a UserErrors::LoginFailed Error? This would look like this:
1 2 3 4 5 6 | def login raise UserError::LoginError unless request.post? user = User.authenticate(params[:nickname], params[:password]) raise UserError::LoginError(user) unless user #doing the login stuff in session... end |
Quite cool. Now we outsourced the things that should happen on a login failure to this private method in the user controller:
1 2 3 4 5 | def login_error(e) flash[:notice] = :login_failed redirect_to_last_page return end |
And at to get this method work when a UserError::LoginFailed method is thrown we added this as private methods to the application controller:
1 2 3 4 5 6 7 8 9 10 | alias old_rescue_action rescue_action def rescue_action(e) key = @@rescue_exceptions.keys.find {|el| e.is_a?(el)} return old_rescue_action(e) unless key send(@@rescue_exceptions[key][:with], e) end def self.rescue_from_all(e, params) @@rescue_exceptions[e] = params end |
This let us now do this:
1 | rescue_from_all UserError::LoginFailed, :with => :login_error |
at the top of the user controller.
That’s all. If now a UserError::LoginFailed exception is raised in the controller it gets handled by the login_error method. Not quite a big deal but I like it.
If I find some time this week I will put this to a small plugin, so that you can give it a try even easier.
If you have any questions, or want do discuss if this is nifty or just crap, you’re welcome to leave your comment.
Yours,
Thorben
FEtMab-Team









