Ruby の throw と catch の実例
Ruby では大体のケースでは例外を begin-raise-rescue で実行箇所を移しているのでは。
なので throw と catch を使う機会はあんまりないように思う。
そんな中、実際に使われているところを見つけたので紹介。
throw と catch を上手く利用しているライブラリ
使われていたのは Rack ベースのアプリケーションで認証機能を提供している warden というライブラリ。
このライブラリはdevise の内部でも利用されている。
利用箇所
throw と catch が使われているのはそれぞれ下記のコード。
- throw 周辺のコード
def authenticate!(*args)
user, opts = _perform_authentication(*args)
throw(:warden, opts) unless user
user
end
- catch 周辺のコード
def call(env) # :nodoc:
return @app.call(env) if env['warden'] && env['warden'].manager != self
env['warden'] = Proxy.new(env, self)
result = catch(:warden) do
env['warden'].on_request
@app.call(env)
end
result ||= {}
case result
when Array
handle_chain_result(result.first, result, env)
when Hash
process_unauthenticated(env, result)
when Rack::Response
handle_chain_result(result.status, result, env)
end
end
コードの説明
throw は認証を行うコード中にあって、認証が失敗した場合に throw(:warden) が実行されている。
catch は rack ミドルウェアの内部で利用されているので、この warden よりも内側のミドルウェア・アプリケーションコード中で throw(:warden) が実行されたらここまでジャンプする。
なので、認証が失敗したら rack ミドルウェアまで戻ってきて認証失敗時の処理を行う。
認証が成功した場合には アプリケーションのコードが rack response を返すはずなので、そのレスポンスをそのまま外側のミドルウェアに渡す。
まとめ
ジャンプしたいコードが大きく離れている場合には非常に有効な手段だと思った。
特に今回の例のように、Rack ミドルウェアとアプリケーションコード間でのジャンプに使うとよさそう。