Ruby Freaks Lounge

第41回 Sinatra 1.0の世界にようこそ

この記事を読むのに必要な時間:およそ 6 分

2つのSinatra

Sinatraに関してよく話題にされる一つに,⁠小規模な開発では使いやすそうだけど,中規模以上になると管理できなくなるのではないか」という問題があります。サンプルコードで見られるトップレベルの記述方法の印象が強いせいか,こうした感想が持たれやすいようです。

この問いに答えるため,実はSinatra0.9x系から,もう一つの記述方法が用意されています。それまでの方法"Classic"スタイルに対し,この新しい書き方は"Modular"スタイルと呼ばれています。

それでは,この2つのHelloWorldサンプルコードを見てみましょう。

リスト1 "Classic"スタイル

require 'rubygems'
require 'sinatra'
get '/' do
  'Hello world!'
end

これぞまさしくSinatra,"Classic"スタイルの呼び名にふさわしいコードです。個人的にSinatraの魅力の本質は,このコードに凝縮されていると思っています。

リスト2 "Modular"スタイル

require 'rubygems'
require 'sinatra/base'
class MyApp < Sinatra::Base
  get '/' do
    'Hello world!'
  end
end
MyApp.run! :host => 'localhost', :port => 9090

これが"Modular"スタイルのSinatraコードです。一見して,クラスベースのコードになっていることが分かります。

RubyKaigi2009におけるAaron Quintさんの講演の中で,繰り返し"Classy"(イケてる)なSinatraとして紹介されていたので,記憶されている方もいるでしょう。

この"Modular"スタイルのSinatraでもっとも注目すべき点は,

require 'sinatra/base'

と,Sinatra::Baseクラスのみがロードされ,継承されている点です。

Sinatra::Baseクラスはその名の通りSinatraのコアとなる基底クラスです。それ単体での利用は推奨されていません。

require 'sinatra'

とした"Classic"スタイルの場合,Sinatra::BaseクラスはSinatra::Applicationクラスに継承されます。

そして,Sinatra::Applicationクラス内で,Sinatra::Baseクラス内のいくつかの--Sinatraの代表的なAPI※2がトップレベルに押し上げられます。

トップレベル側から見れば,Sinatra::Baseクラスに処理を委譲するかたちになります。これにより,不必要なAPIへのアクセスがトップレベルにおいてできなくなり,各メソッド間でのインスタンス変数などの共有も行える,おなじみのSinatraの使用感が実現されます。

しかし,トップレベルのみでは,中規模以上の開発ではやはりグローバル領域の汚染が問題となります。"Modular"スタイルではシンプルに,Sinatra::Baseクラスの直接継承によって名前空間を分け,これを解決しています。"Modular"スタイルのSinatraアプリケーションはRackのミドルウェアとなるため,config.ruファイル内でuseキーワードで複数を指定することもできます。

こうして,"Modular"スタイルの開発手法を用いる事で,アプリケーション単位で開発スコープの分割が可能になり,ある程度の開発規模でも,安心してSinataで開発することが可能になります。

※2
get, put, post, delete, head, template, layout, before, after, error, not_found, configure, set, mime_type, enable, disable, use, development?, test?, production?, helpers, settings

拡張モジュールの作成

もう一つ,Sinatraを使う上で「Railsのようなプラグインの機能はないのか」ということがよく話題にのぼります。

Sinatraはとてもシンプルがゆえに,実務においては不足する機能が多く,意識せずに開発を進めると,思わぬ車輪の再発明をしがちです。

もちろん,シンプルなコアに必要な機能だけを足していく,Sinatraならではの開発スタイルは魅力的です。しかし,よくある認証系やヘルパーを,開発者がそれぞれの方法で持っているという状況は,あまり良いことではないと言えるでしょう。

Rackのミドルウェア開発というのは,この問いの答えの一つです。しかし,リクエストとレスポンスの言わばフィルタ的処理に限定されてしまうため,アプリケーションの動作に密に影響を与えるには,力不足の感は否めません。

そこで,Sinatraでは0.9xを通じて,開発者間のモジュールの共有化を推進するための方法が模索されてきました。現在はルールが定められるとともに,拡張用APIが提供されています。詳しくは公式ドキュメントの"Writing Extensions"に記載されていますので,その引用をメインに解説してみましょう。

拡張モジュール作成のルール

拡張モジュール作成には,以下のルールが定められています。ルールと言っても,"should"と表現されていることから,作成上のガイドラインと捉えると良いでしょう。

  1. Sinatra::Baseクラスをあらゆる手段(オープンクラス,include,extendなど)において,直接的変更をしないこと。
  2. 「require 'sinatra'」ではなく,⁠require 'sinatra/base'⁠⁠ を行うこと。
  3. 用意されたAPIをできるだけ用いること。つまりSinatra.registerSinatra.helpersをつかうこと。
  4. Sinatra::モジュール名の階層で命名すること。

ルール1のSinatra::Baseクラスへの直接的変更を避ける,という点はSinatraのコアの動作に関わる部分ですので,最も重要です。

次に,ルール3で登場した,"Sinatra.register"と"Sinatra.helpers"という2つのAPIを見ていきましょう。

著者プロフィール

吉川毅(よしかわつよし)

Tokyu.rb,Asakusa.rb,はたまたJimbocho.rbなど,首都圏のRubyコミュニティにふらふらと出没する,浮気な文系プログラマ。お仕事では主にPHPを使ったtoC向けのサービス開発に従事。最近はソーシャルゲームを開発したりしている。Twitterほか,Web上でのidは"tsuyoshikawa"。実は回文になっている。

http://twitter.com/tsuyoshikawa