So I had this code in a Rails project using Devise for authentication:
before_filter :require_app_login def require_app_login logger.debug("---- AAA ----") authenticate_user! logger.debug("---- BBB ----") rescue Exception => e logger.debug("---- CCC ----") raise e end
In my log file, I was seeing non-authenticated users getting “AAA” but not “BBB” or “CCC”. I couldn’t figure out how Ruby could unwind the stack without printing one of those or the other. It turns out that Warden (which Devise uses) employs a little-used Ruby feature called
catch. It is not a synonym for
rescue! In fact it’s a lot like
GOTO. You use
catch like this:
catch :my_label do # run some code end
Anywhere in that block of code, including further down the stack, you can say
throw :my_label to cause the
catch to immediately return. The stack unwinds with no exception handling. Amazing!
Note that although your
rescues get skipped, if you add an
ensure block, that will still get called. I couldn’t find this documented anywhere, but that’s the behavior I observe with Ruby 1.9.3, and it makes sense. I guess the moral is that unless you use
ensure, you shouldn’t assume that you’ve covered all the ways for your code to unwind.