Sinatraのverbメソッドとインスタンスメソッドを関連づけるSinatra Extensionを書いた
Sinatraが提供するDSLは非常に簡潔でわかりやすいが、実体はクラスメソッドの実行文のため、モジュールなどに切り分けて後からinclude
するなどといったことが困難である。
この問題は、数十個・数百個のルートを一つのアプリケーション上に定義することになったときに、混沌としたルーティングコードが出来上がることにつながってくる。
Padrinoが用意するcontroller
や、sinatra-contrib
の中のSinatra::Namespace
は、その問題を解決するに十分な機能だと言えるが、今回紹介するSinatra Extensionもまた、それに対する新たな解決策となる(かもしれない)。
考え方
Sinatraでは、get
やpost
などのverbメソッド(と自分は呼んでいる)に渡されたブロックを一旦、Sinatra::Base
のインスタンスメソッドとして定義する。そしてすぐにそれをinstance_method
を用いてUnboundMethod
として取り出し、自身の条件に当てはまるリクエストを受け取る度に、都度生成されるアプリケーションオブジェクトをレシーバとしてbind
後、実行するようになっている。
内部的にブロックをメソッドに変換して管理しているということは、同様に正規の方法で定義されたインスタンスメソッドもルーティングに対して使えるだろうということで、今回のアイデアを思いついた。
ちなみにこの「ブロックをメソッドとして定義する」というプロセスを踏んでいることで、sinatraの代表的な機能の一つとも言えるルート間でのインスタンス変数の共有(や、その他たくさんの便利機能)を実現している。
使い方
register
キーワードを使ってSinatraにSinatra::Bind
を登録することで、新たにon
というルート定義用のメソッドが追加される。
class App < Sinatra::Base
register Sinatra::Bind
def index
"Hello World"
end
on "/", to: :index
end
リクエストメソッドの指定
:type
オプションに指定することでそのように定義される。
class App < Sinatra::Base
register Sinatra::Bind
def index
"Hello World"
end
on "/", to: :index, type: :post
end
before
・after
フィルター
:type
オプションに:before
・:after
を指定することで、フィルターとして定義される。
おわりに
面白い試みではあると思う反面、ぶっちゃけ邪道っぽい。
おわり。