Ruby Freaks Lounge

第42回実世界のSinatra

前回は、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アプリケーションを実装していくスタイルのため、一つのアプリケーションに多くのアクションが存在し、多くの人数が関わらなければならない開発規模の場合は、開発を管理することが難しくなります。

Sinatraを利用する局面

それでは、現状のフレームワークとしてのSinatraが持つポテンシャルとして、どういったものの開発利用が考えられるでしょうか。

画面遷移数が少ないWebアプリケーション

一番利用が考えられるのが、画面遷移数が少ないWebアプリケーションです。個人的に作成して公開する、TwitterのAPIを利用したサービスなどがこのパターンで、もっともSinatraが力を発揮できる開発規模でしょう。

あくまで個人が中心の開発であれば、"Modular"スタイルを用いるまでもなく、ささっとトップレベルに書き捨ててしまうのが筆者の好みです。

商用利用であれば、企業のキャンペーンに利用されるような、画面遷移がせいぜい10くらいまでで、フォーム利用が少しあるくらいの規模のWebサイトも、Sinatraでの開発に適していると言えるでしょう。

このアプリケーションの例としては、筆者が講師をさせていただいた「第3回東京RubyKaigi」で、O/RマッパーにDataMapperを用いた簡単なTwitterクローンアプリケーションを作成するワークショップを行いましたので、参考にしてください。(コードはこちらから)

また、Sinatraで作られたScantyはO/RマッパーにSequelを用いた例なので、Sequelを好まれる方には参考になるでしょう。

ツールのWebインターフェース

Webインターフェースの例としては、Integrityがあります。

gitでのコミットとともにソースコードをビルドし、テストを実行するこのツールの管理画面の開発にSinatraは利用されています。

製品の主軸となる機能、Integrityの場合はサーバですが、その結果を閲覧したり、処理を実行するためのランチャーであったり、あくまでサポートの機能として、小規模なアプリケーションを付属させたい場合、Sinatraは適していると言えるでしょう。

WebAPI

GoogleやTwitterの例を出すまでもなく、HTMLをレスポンスとして返さないWebアプリケーション、つまりWebAPIは一般的になりました。ソーシャルアプリ/ゲーム開発など、実際の現場でも案件が多くなってきた印象を受けますが、SinatraはこうしたWebAPIを短期間に開発するのに非常に適しています。

Sinatra自身のAPI設計との相性が良く、今後主軸になる可能性が一番ある利用法とも言えるかもしれません。

Sinatraを利用する規模感

どの利用法にせよ、テンプレートを除いたSinatraアプリケーション本体のソースコードが、100~500行くらいで1ファイルに収まる程度の規模までが適しているというのが、筆者の感覚値です。

それ以上で、ファイル構成を考えざるを得ないくらいの規模になったときは、Sinatraの利用が妥当であるかどうか、一度考え直してみるタイミングかもしれません。

開発上のTips

次に、筆者の経験上から、いくつかの開発時のTipsを紹介します。

開発環境

Sinatra本体の開発は、Herokuの資金的サポート[1]を受けています。それもあって、Herokuを利用しての開発はかなりスムーズです。

Herokuでは、既にSinatraと、Sinatra周辺でよく利用されるであろうライブラリの多くは既にサーバにインストールされています。また、.gemsファイルに明記することで任意のSinatraのバージョンやライブラリを利用することも可能です。

商用で利用するのは、EC2のパフォーマンス的に、またクラウドという特殊性からもなかなか難しいと思いますが、個人利用や開発環境での利用は積極的に検討するべきでしょう。

ファイル構成

SinatraはRackアプリケーションですので、Rackの標準的なファイル構成に従うのがよいでしょう。特にPassengerで動かす場合などにスムーズです。

lib/の直下に{アプリケーション名}/でディレクトリを配置させるやり方は、"Modular"スタイルを適用させる場合に有効です。その場合、以下に注意点を列挙します。

  • config.ruのファイル名はなるべくそのままが良い。
  • {アプリケーション名}.rbファイル単体でテスト可能なように保つ。そのためconfig.ruなどに設定などをあまり書かない方が良い。
  • {アプリケーション名}.rbファイル内で関連ファイルをロードするようにし、アプリケーションの記述は別ファイル(ここではapp.rb)に分離する。
  • ヘルパーメソッドは膨らみがちなので、別ファイル(ここではhelper.rb)に分離し、helpersメソッドで読み込む。
  • {アプリケーション名}/ディレクトリ配下にアプリケーション関連のファイルを配置する。
  • {アプリケーション名}でモジュールを定義し、名前空間として用いれば良い。
リスト2 Sinatraアプリケーションの標準的なファイル構成例
+-- config.ru
|
+-- view/
|
+-- public/
|
+-- test/
|
+-- lib/
        |
        +-- {アプリケーション名}.rb
        |
        +-- {アプリケーション名}/
               |
               +-- app.rb
               |
               +-- helper.rb

書き捨てのアプリケーションの場合は、テンプレートも含めすべてを1ファイル内に記述するスタイルのほうが良い場合もあるためこの限りではありませんが、config.ruとアプリケーションは分けたほうがテストしやすいので、おすすめです。

コードの整理法

SinatraはMVCフレームワークでないと言及しましたが、コードの整理の仕方においては、Railsからの影響を色濃く受けていると言えるでしょう。

Sinatraで開発するとき、いわゆるビジネスロジックは、選択したO/Rマッパー内に定義するのが良いでしょう。しかし、それ以外の多くの共通処理はヘルパーに集中することになります。

先述の通り、helpersメソッドはモジュールを引数に取れますので、これを利用し、ヘルパーはモジュール内にしっかりと実装するのが良いでしょう。そして、Sinatraでのもう一つ記述が集中しやすい箇所があります。それはconfigureブロック内です。

筆者の場合、開発環境でHerokuを使っていたのですが、本番環境はスケールアウトするためにデータベースのレプリケーションや、キャッシュの設定をしており、環境での差異に悩まされた経験があります。

このとき、configureブロックの第一引数に環境を指定できることと、configureブロックが複数定義可能なことを知っていると、かなり設定をすっきりさせられます。

リスト3 configureブロックの評価順
configure do
  # set something...               ---(1)
end

configure :test, :development do
  # set something...               ---(2)
end

configure :production do
  # set something...               ---(3)
end

configure do
  # set something...               ---(4)
end

上記のリストのようにconfigureを複数定義し、環境がproduction(本番)の場合、(1)→(3)→(4)の順でconfigureブロック内が評価されます。設定の途中の値だけ可変にしたい、ということも実現できますので、是非利用してみてください。

ドキュメント

最後に、ここで紹介しきれなかった多くのことについて、特に有益な公式内のドキュメントを紹介することで替えさせていただきます。

ドキュメントのタイトル(和訳含む)内容
日本語のREADME日本語で訳されたREADME。基本的なSinatraの使い方はここで分かるはずです。
よくある質問MLで多い質問はここに記載されるので、困ったらとりあえず見ましょう。随時更新されています。
設定項目setメソッドで利用出来る組込みの設定の説明があります。⁠ログをどうやって止めるの?」等は、ここを確認しましょう。
テストの書き方Rack::Testを利用したテストの書き方が紹介されています。Test::Unitの他にも、RSpecを用いた例もあります。
事例紹介Applicationsには多くのgithubレポジトリがリンクされています。書き方を学ぶにはここが一番でしょう。

まとめ

前回のまとめを繰り返すことになりますが、Sinatraの先にはまだ地図がありません。

今回は現実世界でSinatraを使うことをテーマにしましたので、いくつかネガティブな内容もあったかもしれませんが、それらは"あくまで現状で"という保留付きだということを、ここで強調させていただきたいです。よく話題とされる開発規模の問題も、今後素晴らしいアイディアによって乗り越られる可能性は充分あります。現時点でのSinatraを過大評価することはできませんが、矮小化して語ってしまうことも非常にもったいないと筆者は思います。

恐縮ながら、この記事をきっかけにきちんとSinatraが評価され、未来に活かされれば良いなあと思っています。

2回という短い連載でしたが、皆様のSinatraを使った開発が、楽しく、自由なものになることに少しでも貢献できれば、筆者としてこれ以上の幸福はありません。ご精読ありがとうございました。

おすすめ記事

記事・ニュース一覧