Rackの基礎

こんにちは!! エンジニアとしてインターンしている村田です。

今回はズバリRackについて書いていきたいと思います。

Rackに関しては日本語の資料もあまり多くないため、特にRailsの初心者の方はよく分からないまま見過ごしていることも多いかもしれません。

私も初心者なので探すのに苦労しました。。

そこで自分もまだまだではありますが、これを読んで自分と同じような初心者の方々がRackのぼんやりとしたイメージをつかんでいただけるように書いていきたいと思います。

それでは早速ですがやっていきましょう!

Rackが誕生した背景

まず何故Rackが必要なのか? ここがわからないと、話が頭に入ってこないですよね。

これはPSGIというPythonのインターフェースの仕様に影響を受けました。

以前のPythonフレームワーク界は、様々フレームワークが開発されたものの、そのフレームワークが特定のWebサーバーに依存した状態でした。 ただし、これではサーバーの乗り換え時の負担が大きかったりして、開発者も頭を悩ませることが多かったようです。 そこで、ある特定のルールを定めて(インターフェース)、そのルールのもとフレームワークとWebサーバーの開発を進めることにしました。

これがPSGIの正体です。

RubyフレームワークRailsSinatraを筆頭に多様性に富みますし、WebサーバーもWebrickをはじめとして様々な独自のデプロイ方法を持っています。

すると、当然PSGIの代わりとなるシステムが必要になりまして、そうやって検討されていった結果現在のRackが誕生していくことになります。

Rackってなに??

さていよいよ本題にいきましょう。

Rackとは、rackupというコマンドを打つことで指定したファイルを参照しながらWebサーバーを立ち上げられるようにしているライブラリ(gem)です。 もう少し機能自体の説明をするならば、HTTPの送受信処理を担当するモジュールです。

指定したファイル(デフォルトではconfig.ru。ちなみにruはruckupの略。)には独自のRuby DSLで様々なミドルウェアやアプリケーションを参照するように書かれています。 これをもとにWebサーバーを立ち上げていくことになります。

ビジュアル的に説明をするとこんな感じになっています。

f:id:cluex-developers:20160916102713p:plain

このようにミドルウェアを介して、RailsなどのRackアプリケーションに対してWebサーバーから来たHTTPをパースしたりしています。

試しにbundle exec rake middlewareと打ってみましょう。 例えばこのような感じになって出てくると思います。

use Raven::Rack
use ActionDispatch::Static
use Rack::Lock
use #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x007fd239f96d58>
use Rack::Runtime
use Rack::MethodOverride
use ActionDispatch::RequestId
use Rails::Rack::Logger
use ActionDispatch::ShowExceptions
use ActionDispatch::DebugExceptions
use BetterErrors::Middleware
use ActionDispatch::RemoteIp
use ActionDispatch::Reloader
use ActionDispatch::Callbacks
use ActiveRecord::Migration::CheckPending
use ActiveRecord::ConnectionAdapters::ConnectionManagement
use ActiveRecord::QueryCache
use ActionDispatch::Cookies
use ActionDispatch::Session::CacheStore
use ActionDispatch::Flash
use ActionDispatch::ParamsParser
use Remotipart::Middleware
use Rack::Head
use Rack::ConditionalGet
use Rack::ETag
use Warden::Manager
use ExceptionNotification::Rack
use Bullet::Rack
use OmniAuth::Strategies::Facebook
run Rails::Application.routes

ここに書いてあるuseの対象がRackミドルウェアです。 そして最後に書いてあるrunの対象はRackアプリケーション(Rails)になります。 ちなみにuserunはRack DSLなのですが、今回は詳しく触れませんので興味がある方は調べてみてください。

ruckupの正体

こちらをご覧いただければお分かり頂けると思いますが、実はrackupコマンドがやっているのはRack::Server.startをしているだけなんですよね。

と言われてもRack::Serverとかよく知らないって方がこの記事を読んでくださっていると思いますので、説明します。

Railsで開発をしているとrails s、省略しないで書くと、rails serverというコマンドを打つことがあると思います。 このrails serverというコマンドも実は省略形でして、正確にはRails::Server.startというコマンドを打っています。

何か先ほどとても似ているコマンドを見ましたよね? そうRack::Server.startです。

もちろんこの二つは関係がないわけではなく、Rails::ServerはRack::Serverのサブクラスになっているのです。

継承する中で、ポートを3000番にするなど様々な違いを作っているようですが基本的な動きの部分は引き継いでいることがわかっていただけたと思います。

Rackミドルウェアの話

Rackミドルウェアはもちろん自分で追加することもできますが、そのためには簡単なルールを幾つか覚えなくてはなりません。

1つのHashオブジェクトを引数に取る「call」メソッドが実装されていること callメソッドは「ステータスコード」、「ヘッダーを表現したHash」、「eachに反応するオブジェクト」の3つの要素を持った配列を返すこと

上にある2つがそのルールになります。 また慣習的にcallメソッドの引数にはenvを用いることも覚えておくと得をするかもしれません。

詳しく話を始めるとDSLの説明やその他いろいろな話を書かないといけないので割愛しますが、Railsのようなアプリケーションも最後はcallで呼ばれるので

lamda do |env|
[200, {"Content-Type" => "text/html"}, ["Hello, world!"]]
end

簡単に言ってしまえばこのような形で動いていることになります。 これは先ほどの条件を満たしていますよね。

最後に

私の説明では物足りなかった部分も多かったと思います。 でもこれをきっかけにしてRackのイメージを掴めた方が一人でもいらっしゃればとても嬉しいです。

私もわからないことだらけなのでもっと勉強していこうと思いますが、最後にこの記事を作る上で参考にさせていただいた記事を載せておこうと思います。

RailsGuides

Rackとは何か

Rack解説

パーフェクトRubyonRails

それでは失礼いたします。