前回は,Sinatraバージョン1.0の概要を公式ドキュメントを手がかりとして,Sinatraを紹介しました。そして最後に,「Sinatraの先には,まだ地図がない」と言及しました。
今回は,「実世界のSinatra」と題して,実際にSinatraを利用して開発していくうえでの,筆者自身のロードマップを示していきます。
Sinatraとはいったい何か
いきなりですが,Sinatraとはいったい何なのでしょう。
これは根本的な問いになりますが,Sinatraで開発を進める前に,ここをしっかり考えることが重要であると筆者は考えます。
素直に考えるならば,Sinatraはもちろん,広義のWebアプリケーションフレームワークの一つである,と答えられるでしょう。アプリケーションフレームワークのそもそもの定義が,「共通部分を再利用可能にし,開発を助けるもの」であるならば,Sinatraもこの例に漏れません。
しかし,Sinatraのアーキテクチャは,現在の多くのWebアプリケーションフレームワークが採用しているModel-View-Controllerの三層構造ではありません。SinatraにはRailsのActiveRecordに相当するようなModel層はありませんし,View層は,提供されているテンプレートエンジンを自由に選択できますが,全く使わないことも可能です。
このように,それまでのWebアプリケーションフレームワークの感覚でSinatraを扱うと,明らかにその性質が異なることが分かります。
それではSinatraとは,一体どういう特性をもったフレームワークなのでしょうか。
公式のREADMEなどのドキュメントでは,次のように表現されています。
Sinatra is a DSL for quickly creating web applications in Ruby with minimal effort
"Sinatraは,Rubyで最小限の労力で素早くWebアプリケーションを作るためのDSL"
つまり,SinatraはDSL(ドメイン固有言語)である,という定義です。
前回の終わりにも触れましたが,get, post, put, deleteといったAPIが考案されたのが,そもそものSinatraの始まりです。HTTPメソッドに対応したメソッドでWebアプリケーションを記述する,その直感的なインターフェースの在り方そのものが,SinatraをSinatraとして成り立たせているアイデンティティでしょう。
これまで,フレームワーク同士の差異は,機能の有無や,アーキテクチャの優劣で語られることがほとんどで,あまり「どのように記述するか」ということが関心ごとにはなりませんでした。しかし,Sinatraはそこを一番重要視したのです。
このように考察すると,バージョン1.0までの多くの変更が機能追加ではなく,内部実装やAPIの洗練,パフォーマンスや外部拡張性の向上であったことの意味が分かるのではないでしょうか。つまり,Sinatraはそのアイデンティティを誕生時からある程度完成させていた一方,フレームワークとしての実装の洗練は,まだまだ発展途上だということです。
Sinatraができること,できないこと
このことを踏まえた上で,実際の開発において問題につきあたった場合,それがSinatraそのものの限界なのか,フレームワーク的未熟なのかを切り分ける必要があります。
例えば,beforeメソッドはリクエスト時に共通となる前処理を定義するメソッドですが,現状ではすべてのリクエストに適用されるため,適用範囲を限定できないという問題があります。そのため,特定のアクションのみに認証をかけたいといった場合には,beforeブロック内でswitchを用いて処理分けをするか,それぞれのアクションに認証をかけるといった対処法がとられることがほとんどです。
なお,次期バージョンでは,このbeforeメソッドの第一引数にパスを指定できるようにし,この問題に対処するといった方針がとられるようです。
以下は、パスの指定にワイルドカードを用い、/user以下の階層に対し、共通の前処理(例えばログイン認証処理など)を適用する例です。
リスト1 次期バージョンで予定されているbeforeメソッド
before '/user/*' do
# do something
end
この場合でもパスが基準になってしまうため,例えば認証を行うアクションが複数の場合,パスに正規表現を用いて複数のURLにマッチさせねばならず,アプリケーションの規模が大きくなるにつれ管理が複雑化する恐れがあります。これは一見,フレームワークとしての機能の実装の未熟さに思えるかもしれません。そうであれば,開発チームにパッチを送る,MLに相談する,拡張モジュールを作成するなどの方法があり,いずれにしろ問題解決は可能です。
しかし,この問題の根本には,パスを基準にして処理を分けるSinatraのアイディアそのものがあり,設計から根本的に見直さなければスマートな解決策は得られません。Sinatraのコミッタになり議論するという方法はありますが,短期的には問題は解決されないため,現実には多少難があっても,妥協的な対応方法をとるべきでしょう。
Sinatraには現状,beforeメソッドの問題と同様の構図で,「今は解決不可能で,今後も解決不能(かもしれない)」問題が多く存在します。
その中でも最大の問題は,開発スコープの規模の問題です。前回,Sinatra::Baseクラスを継承してアプリケーションを作成する"Modular"スタイルの開発手法を紹介しましたが,この"Modular"スタイルを用いても,ある規模以上の開発スコープにSinatraは対応できません。
Sinatraはアーキテクチャ上ではRackアプリケーションですが,Rails3.0等と違い,単一のSinatra::Baseクラス(またはSinatra::Applicationクラス)にRackアプリケーションを実装していくスタイルのため,一つのアプリケーションに多くのアクションが存在し,多くの人数が関わらなければならない開発規模の場合は,開発を管理することが難しくなります。

