Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save jwilger/3af40e63a0244d8aa005 to your computer and use it in GitHub Desktop.
Save jwilger/3af40e63a0244d8aa005 to your computer and use it in GitHub Desktop.
Avoiding Multiple Render Errors by Using Throw/Catch

Avoiding Multiple Render Errors by Using Throw/Catch

When creating Rails controllers, I often dislike the use of filters such as #before_filter due to the fact that they can get a bit unweildy. I'd much rather be able to look at the definition of an action's method and see right in the first few lines if there are is anything that might run which would cause the action to behave differently.

The nice thing about before-filters, of course, is that you can cancel the running of the action method form within the filter, which is handy for redirects or changing what is rendered in cases such as failed authorization, etc. If you use inline methods to, say, check authorization, and that method does a render or a redirect, you need some way to ensure that the remainder of the action does not execute, so that you avoid both performing unauthorized actions and getting a double-render error from Rails.

What follows is a way to do that by using an application-wide around-filter and using Ruby's throw/catch mechanism to pop back up the stack.

class ApplicationController < ActionController::Base
around_filter :catch_renders_and_redirects
private
def catch_renders_and_redirects
catch(:action_rendered) do
yield
end
end
end
class MyController < ApplicationController
def index
check_authorization!(:see_the_index)
check_for_awesomeness!(param[:level])
@foo = something_we_dont_want_to_run_if_checks_above_failed
render text: 'Yay!'
end
private
def check_authorization!(permission)
unless Authorizor.authorized?(to: permission, as: current_user)
render template: 'authorization/failed', status: 403
throw :action_rendered
end
end
def check_for_awesomeness!(description)
unless description == 'awesome'
redirect_to '/not_awesome_enough'
throw :action_rendered
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment