Padrino Frameworkのルーティングの仕組みが変わります
はじめに
タイトルのとおり、Padrino 0.13.0からはコアのルーティング実装が新しいものに替わります。
この記事では、0.13.0から追加された新機能や変更について解説します。
ルーティングの実装については、Padrino Frameworkのルーターを開発した話をご覧ください。
新機能
プリコンパイル機能
新しいルーターでは、目的のルートを高速に探索するために、一度全てのルートを正規表現にコンパイルします。
この機能は、その処理を予めサーバ起動時に行っておくことで、初回リクエスト時のオーバーヘッドを減らすことを目的とするものです。
以下に示すコードをアプリケーション直下、あるいはconfig/apps.rb
のconfigure_apps
ブロック内に追加する事で、この機能が有効になります。
set :precompile_routes, true
Sinatraとの互換性の修正
*
を含むルートにおいて、*
に相当する値を取得できない問題の修正
*
に相当する値を取得する方法がありませんでしたが、ブロックパラメータ、もしくはparams[:splat]
から値を取得することができます。
get "/a/*" do |b|
"#{b}, #{params.inspect}"
end
get "/b/*" do
params.inspect
end
# 0.12.4
get "/a/123" #=> ArgumentError
get "/b/123" #=> {}
# 0.13.0
get "/a/123" #=> "123, {"splat"=>["123"]}"
get "/b/123" #=> {"splat"=>["2"]}
*
を含むルートのパスの展開ができない問題の修正
url
やurl_for
メソッドを使ったパスの展開ができるようになりました。
get :splatter, "/a/*" do
url(:splatter, splat: "123")
end
get :splatter2, "/b/*/*" do |c, d|
url(:splatter2, splat: [c, d])
end
# 0.12.4
get "/a/123" #=> "/a?splat=123"
# 0.13.0
get "/a/123" #=> "/a/123"
get "/b/123/456" #=> "/c/123/456"
*
を含むルートに対するリクエストにおける、PATH_INFOの末尾スラッシュの有無による挙動の修正
Sinatraの挙動に合わせることにしました。
この機能に依存したルーティングは修正が必要になります。
get("/asdf/*"){ "splat" }
# 0.12.4
get "/asdf/" #=> "splat"
get "/asdf" #=> "splat"
# 0.13.0
get "/asdf/" #=> "splat"
get "/asdf" #=> 404
正規表現ライクなクラスを用いたルートのサポート
なんのこっちゃという感じですが、要はダックタイピングのサポートです。
ただしSinatraのそれとは少し仕様が異なるので注意が必要です。
Sinatraでは、以下に示すようなクラスを用いたルーティングが可能です。
class RegexpLookAlike
class MatchData
def captures
["this", "is", "a", "test"]
end
end
def match(string)
::RegexpLookAlike::MatchData.new if string == "/this/is/a/test/"
end
def keys
["one", "two", "three", "four"]
end
end
get RegexpLookAlike.new do
"this is a test"
end
Padrinoでは、代わりに以下に示すようなクラスが必要になります。
class RegexpLookAlike
NAMES = ["this", "is", "a", "test"]
class MatchData
def captures
["this", "is", "a", "test"]
end
def names
NAMES
end
end
def names
NAMES
end
def to_s
"/this/is/a/test/"
end
def match(string)
::RegexpLookAlike::MatchData.new if string == "/this/is/a/test/"
end
end
get RegexpLookAlike do
"this is a test"
end
見てわかるとおり、MatchData#names
、RegexpLookAlike#names
、RegexpLookAlike#to_s
が新たに必要となります。
プリコンパイル機能に関する説明で触れたように、一度ルートを正規表現にコンパイルする必要があるため、
to_s
はそのための正規表現、あるいは文字列を返す必要があります。
mustermannなどと組み合わせて作っていくと良いかもしれません。
pass
with ブロックが動かない問題の修正
passing自体は有名ですが、ブロックを渡すことでそれを評価した値をレスポンスボディとして扱うことができる、というような機能もあります。
get "/" do
pass { "passed" }
"index"
end
get "/" #=> "passed"
ただし、ブロック付きpassを実行したことで次のルートにマッチし、そのルートが正しくレスポンスを返した場合にはpassに渡されたブロックは無視されます。
get "/" do
pass { "passed" }
"index"
end
get "/" do
"index2"
end
get "/" #=> "index2"
get "/" do
pass { "passed" }
"index"
end
get "/" do
pass
"index2"
end
get "/" #=> "passed"
ルートブロック内でrequest
インスタンスが変更された際のルーティングの挙動の修正
ルートブロック内でrequest
インスタンスを変更すると、その変更を考慮したルーティングが行われるようになるというものです。
pass
と組み合わせて使うことが想定されます。
get "/foo" do
request.path_info = "/bar"
pass
end
get "/bar" do
"bar"
end
get "/foo" #=> "bar"
変更点
(.:format)
-> (.:format)?
:provides
オプションを使った際に、以前までのPadrinoはパスに対して(.:format)
というキャプチャ用パラメータを追加していましたが、
0.13.0からは(.:format)?
という指定に変更されます。
これは、Sinatra 2.0のOptional Groupの挙動を模倣したものであり、Padrinoはこの部分において、Sinatra 2.0を先取りしているといえます。
といっても、:provides
オプションを介して使っている人はあまり意識する必要はないでしょう。
ブロックパラメータの数に関するサポートの追加
SinatraやPadrinoはルートに渡すブロックの仮引数の有無で、実引数を渡すか否かを決定します。
今回のアップデートでは、仮引数が存在していた場合に、仮引数の数と実引数の数が一致しなければPadrinoが用意した例外を発生させるように変更されました。
また、これに伴い0.13.0以前のものとブロックパラメータの扱いに関する挙動が変わっているため、解説します。
以下に示すコードは、0.12.4では拡張子が省略されているケースで正常に動作しているように見えますが、拡張子が渡されるとArgumentErrorが発生します。
0.13.0では、拡張子の有無にかかわらずPadrino::Routing::BlockArityError
がアプリケーションを起動するタイミングで発生するようになりました。
get "/:param1/:param2", provides: :xml do |param1, param2|
"#{param1}, #{param2}"
end
# 0.12.4
get "/123/456" #=> "123, 456"
get "/123/456.xml" #=> ArgumentError
# 0.13.0
get "/123/456" #=> Padrino::Routing::BlockArityError
get "/123/456.xml" #=> Padrino::Routing::BlockArityError
実際には以下に示すように、仮引数を三つ用意しておくべきです。
get "/:param1/:param2", provides: :xml do |param1, param2, format|
"#{param1}, #{param2}, #{format}"
end
# 0.12.4
get "/123/456" #=> ArgumentError
get "/123/456.xml" #=> "123, 456, xml"
# 0.13.0
get "/123/456" #=> "123, 456, "
get "/123/456.xml" #=> "123, 456, xml"
おわりに
0.13.0を正式にリリースする前に、何度かbetaリリースが行われることになると思います。(0.13.0.rc1 など)。
是非、Padrinoを利用している方はbetaリリース期間中に自分の環境で新しいバージョンをテストしていただき、不具合等があれば報告していただければと思います。
よろしくお願いします。
この記事は、0.13.0のリリースまで随時更新していきます。