概要(歴史的な経緯など)
- 第23回 Rackとは何か(1)Rackの生まれた背景:Ruby Freaks Lounge|gihyo.jp … 技術評論社
- 第24回 Rackとは何か(2)Rackの使い方:Ruby Freaks Lounge|gihyo.jp … 技術評論社
- 第25回 Rackとは何か(3)ミドルウェアのすすめ:Ruby Freaks Lounge|gihyo.jp … 技術評論社
この3回のコードをまとめてくださってる(+最近のRackでも動くように変更してくださってる)のが
ちなみに元のコードで何がまずいかというと、一つは require のファイル指定。Ruby 1.9以降はパスで指定しないとNGになったから。もう一つはミドルウェアの neco_filter.rb で Content-Length ヘッダが書き替えられていないためにエラーが出る。
Unexpected error while processing request: Content-Length header was 97, but should be 103
Content-Length ヘッダは Rack::Lint で確認しています。
def verify_content_length(bytes) if @head_request assert("Response body was given for HEAD request, but should be empty") { bytes == 0 } elsif @content_length assert("Content-Length header was #{@content_length}, but should be #{bytes}") { @content_length == bytes.to_s } end end
簡潔なリファレンス(2011/5時点
簡潔な概要(2014/4の状況
エンドポイント、ミドルウエア、 Rack ハンドラという概念が Rack では大事だと分かります。
かなり初期バージョン(0.2か0.3)で少し詳しめの解説(2008/2時点
残念ながら古いので今のソースと合わない。
evalで評価してると書いてあるが、さすがに何年もそのまま放置しないよね。
config.ru の内容は Rack::Builder オブジェクトの中で評価される (instance_eval)。
「設定」を設計するための資料 - Joy Luck Crab
ruckupを使わずrubyスクリプトとして起動
もちろん可能なのだが、環境依存してしまうのでオススメしない。理由は以下(長い引用)。
単にRackアプリケーションを起動するためだけであれば,config.ru(rackupファイルと呼びます。拡張子のruはおそらくrackupの略でしょう)は必要ありません。…以下のようなコードをファイルの末尾に書き加えるとsimple_app.rb単体でアプリケーションを起動することができます。
if __FILE__ == $0 require 'rack/handler/webrick' Rack::Handler::WEBrick.run SimpleApp.new, :Port => 9292 endrackupから起動した場合と特にかわらない結果になったと思います。前回の構成でrackupコマンドを実行した場合,コマンドの内部でこのような処理をしているからです。このほうが「サーバを起動してアプリケーションを実行している」のがよくわかりますね。では,なぜ最初からこの形ではなく,rackupを使うやり方を紹介したのでしょうか。
一つは,この形式ではサーバ依存のコードが残ってしまっているからです。…残念なことに上記のコードでは「どのサーバによってどのように実行されるか」を明示的に記述してしまっているため,(些細なことではありますが)サーバを変更する度にアプリケーションのコードを修正する必要があります。これではRackのメリットが活かし切れていません。
…MongrelやWEBrickであればrackupコマンドが,ThinやPassengerであればサーバ側で,それぞれ「rackupファイルを読んでアプリケーションを初期化して,適切なハンドラで起動する」ところまではやってくれます。であれば,そちらに任せてしまえばそれぞれの差異を考慮する必要がないので,そうするべきでしょう。
もう一つは,rackupファイルでは,Rack::Builderを通してDSLでRackアプリケーションの構成を記述する仕組みが使えるということです。上記のコードのアプリケーションを起動する部分をRack::Builderを使うように書き換えると,以下のようになります。if __FILE__ == $0 require 'rack/builder' require 'rack/handler/webrick' app = Rack::Builder.new { run SimpleApp.new } Rack::Handler::WEBrick.run app, :Port => 9292 endRack::Builderのブロックの中身がrackupファイルと同じになっているのにお気付きでしょうか。rackupコマンドは,rackupファイルの中身をRack::Builderに渡しているわけです。この例ではあまり有難味はありませんが,後述するRack::URLMapやミドルウェアを使うようになると,Rack::Builderを使ってDSLで構成を記述する方がスマートです。
つまり,「基本的にはconfig.ruに色々書いて,アプリケーション側にはサーバ依存のコードは書かない」ものだと覚えてください。
第24回 Rackとは何か(2)Rackの使い方:Ruby Freaks Lounge|gihyo.jp … 技術評論社
中身を学ぶ
Rack 1.4.1。非常に良い。
ミドルウェアの作り方
何もしないミドルウェア
class Through def initialize(app) @app = app end def call(env) @app.call(env) end end
これを use Through
とすれば OK。
ミドルウェアの例(語尾がクマ語になるrack middlewareなど)
class KumaResponseFilter def initialize(app) @app = app end def call(env) res = @app.call env # sinatraに処理させる if res[1]["Content-Type"] =~ /^text\/.+/ res[2] = res[2].map{|body_part| body_part.gsub(/([。!?])/){|s| "クマ#{s}"} } res[1]["Content-Length"] = res[2].map{|body_part| body_part.bytesize }.inject{|a,b| a+b }.to_s end return res end end
rack middlewareを作ると、WebサーバーがWebアプリケーションフレームワーク(WAF)とやりとりするデータを書き換えたりできる。
rack::auth系の認証プラグインとか、rack::cache系のキャッシュ系が有名。
どちらもリクエストとレスポンスの間をいじる処理で、プラグインをsinatraやrailsで実装するよりもrackでやったほうが楽だと思った。
Rack::URLMapの上手い使い方
まず
Rack::URLMap
Rack::URLMapはRackの「機能」ではなくRack標準添付の「アプリケーション」…
:
このアプリケーションはパスとアプリケーションのマッピングを保持しておき,パスに応じてリクエストを登録してあるアプリケーションに振り分けてくれます。また,Rack::Builderではこのアプリケーションへのショートカットになっているmapという記法が用意されています。
:
Rack::URLMapでは複雑なURLの解決はできませんので,通常はアプリケーションやフレームワークの側で実装することになりますが,単機能の簡単なアプリケーションを複数立ち上げたいというような場合には手軽に設定できて便利です。
第24回 Rackとは何か(2)Rackの使い方:Ruby Freaks Lounge|gihyo.jp … 技術評論社
ということで普段は Rack アプリケーションで
map '/simple' { run SimpleApp.new }
てな感じで、指定された URL で使う app を振り分けます(Sinatra の get/put の類似)。
で。
あるSinatraアプリケーションが http://localhost:9292/ で動いている。config.ruは以下のような設定。
config.ru require './app' run Sinatra::Applicationこれを http://localhost.:9292/sub で動かしたい、そんな時は Rack::URLMap を使う。
config.ru require './app' run Rack::URLMap.new("/sub" => Sinatra::Application)
GitHubのRackのWiki
この中の
(tutorial) rackup howto · rack/rack Wiki
で興味ある内容を引用しておきます。
- .ru:
The config file is treated as if it is the body of
app = Rack::Builder.new { ... config ... }.to_app
これは上記第24回 Rackとは何か(2)Rackの使い方:Ruby Freaks Loungeの「rackupとRack::Builder」にも載っていました。
Also, the first line starting with #\ is treated as if it was options, allowing rackup arguments to be specified in the config file. For example:
#\ -w -p 8765 :
Would run with Ruby warnings enabled, and request port 8765 (which will be ignored unless the server supports the
:Port
option).
#\ の後に、 ruby のコマンドラインオプションが指定できるようです。
Automatic Middleware
rackup
will automatically use some middleware, depending on the environment you select, the-E
switch, withdevelopment
being the default:
development
: CommonLogger, ShowExceptions, Lintdeployment
: CommonLoggernone
: none
通常は development
モードでの起動。その際にはミドルウェアとして Rack::CommonLogger, Rack::ShowExceptions, Rack::Lint が自動的に使用されるようになっています。
ソースでは以下のようになっています。
module Rack class Server class << self def default_middleware_by_environment m = Hash.new {|h,k| h[k] = []} m["deployment"] = [ [Rack::ContentLength], [Rack::Chunked], logging_middleware, [Rack::TempfileReaper] ] m["development"] = [ [Rack::ContentLength], [Rack::Chunked], logging_middleware, [Rack::ShowExceptions], [Rack::Lint], [Rack::TempfileReaper] ] m end def middleware default_middleware_by_environment end end def middleware self.class.middleware end private def build_app(app) middleware[options[:environment]].reverse_each do |middleware| middleware = middleware.call(self) if middleware.respond_to?(:call) next unless middleware klass, *args = middleware app = klass.new(app, *args) end app end end end
RackでWeb Application Frameworkを作った例
おまけ:Rails on Rack
Rackの生ログをMongoDBに送る(2015/12/24追記)
ramolog gem という名前で公開されている。