Sinatra の error ハンドラが動いてくれなくて戸惑いました。

Sinatra は Ruby の Web アプリケーションフレームワークの1つです。

ハンドラを定義し、Web アプリケーションの動作を定義していきます。

リクエストは条件に合致するハンドラによって受け取られ、対応する処理をしてレスポンスを返します。

中には補助的なハンドラもあります。

beforeルートハンドラ実行前に行う処理を定義する。リクエストを変更できる。
afterルートハンドラ実行後に行う処理を定義する。レスポンスを変更できる。
error例外発生時の処理を定義する。

今回は、この中の error ハンドラに関する話です。

独自の例外を定義して、その処理を error ハンドラに集約し、例外の種類によって処理の振り分けをしようと考えました。

しかし、定義して動作させてみたのですが error ハンドラが呼び出されず、どうしてもエラー情報を表示するレポート画面が出てしまいます。

Sinatra のドキュメントを見ても書いて無いんです。仕方ないのでソースを読むことにしました。

まず最初にわかったのは、エラー情報のレポート画面を出力しているのは Sinatra::ShowExceptions という Rack Middleware だということです。

その Rack Middleware を組み込むかどうかを判断しているのは、:show_exceptions という組み込み変数ということもわかりました。これを false にしたところ error ハンドラが動いてくれるようになりました。

ちなみにこの :show_exceptions の標準は、Sinatra の動作モードが開発中(:development)であるときのみ有効なもので、最終的には利用しなくなる Rack Middleware です。以下がその定義です。

set :show_exceptions, Proc.new { development? }

しかし、現在はまだ開発中です。定義した error ハンドラ以外の例外が発生したときにはエラー情報のレポート画面を出して欲しいのです。

Sinatra 本体のソースコードを読み進めたところ良い設定方法を発見しました。 該当コードは以下の部分です。boom というのがハンドルした例外です。これを raise するか判断している条件があります。

    def handle_exception!(boom)
      ...

      if server_error?
        dump_errors! boom if settings.dump_errors?
        raise boom if settings.show_exceptions? and settings.show_exceptions 
!= :after_handler
      end

そこに :after_handler というシンボルがあります。

これを利用し :show_exception を以下のように定義します。

set :show_exception, :after_handler

すると error ハンドラが先に処理されて、処理しきれなかった例外だけが Sinatra::ShowExceptions に流れるようになります。

以下が標準だと嬉しいのかなと思いました。

set :show_exceptions, Proc.new { development? ? :after_handler : false }

ちなみにこの内容は、以下のページに加筆しました。

こういうことがあるので、情報をまとめておきたいんです。