Rack をシンプルに動かしてみる

 前回の記事で Roda というフレームワークのことを書きましたが、その時に久々に Rack について意識したので、今回は改めて Rack の基本的なことを再確認してみます。

 ちなみに前回の記事はこちらです。

blog.akanumahiroaki.com

Rack とは

 Rack の簡単な説明としては、 Ruby をサポートする Web サーバと、 Ruby の Web フレームワークの間のインターフェースを提供するためのものです。 Web フレームワークを開発する場合、開発者はそれぞれのサーバに対するハンドラを書かないといけませんが、 Rack ベースの構成にしておけば、 Rack をサポートするサーバのハンドラは自前で用意しなくてもよくなるので、効率的にフレームワークを開発することができるようになります(と言っても私自身はフレームワーク開発したことはまだありませんが。。)。

rack.github.io

 Rack の GitHub リポジトリはこちらです。

github.com

 前回の記事で書いた Roda や、有名どころでは Ruby on Rails や Sinatra も Rack ベースのフレームワークになります。

Rack のインストール

 まずはとにかく動かしてみたいということで、ごく簡単に Rack を動かしてみたいと思います。ベースになる環境としては Ruby と gem だけ使えるようにしてあって、 Rails や Rack はまだインストールしていない状態です。

$ ruby -v
ruby 2.6.2p47 (2019-03-13 revision 67232) [x86_64-linux]
$ 
$ gem -v
3.0.3
$ 
$ rails -v
bash: rails: command not found
$ 
$ rackup
bash: rackup: command not found

 Rack は gem でインストールできるようになっています。今回は Gemfile に記述して Bundler で管理してみます。下記のような Gemfile を用意します。

# frozen_string_literal: true

source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

gem "rack"

 そして bundle install します。

$ bundle install
Fetching gem metadata from https://rubygems.org/..............
Resolving dependencies...
Using bundler 1.17.2
Fetching rack 2.0.7
Installing rack 2.0.7
Bundle complete! 1 Gemfile dependency, 2 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.

 Rack の gem がインストールされ、下記のように情報が確認できます。

$ bundle info rack
  * rack (2.0.7)
        Summary: a modular Ruby webserver interface
        Homepage: https://rack.github.io/
        Path: /home/ec2-user/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/rack-2.0.7

 rackup コマンドも使えるようになっています。

$ rackup --version
Rack 1.3 (Release: 2.0.7)

Rack を簡単に動かしてみる

 公式サイトでも紹介されている、ごくシンプルな例で Rack を動かしてみたいと思います。まずは下記のように my_rack_app.rb というファイルを用意します。

# my_rack_app.rb

require 'rack'

app = Proc.new do |env|
  ['200', { 'Content-Type' => 'text/html' }, ['A barebones rack app.']]
end

Rack::Handler::WEBrick.run app

 Rack で動くアプリケーションの条件としては、 call メソッドを持つ Ruby オブジェクトを用意することで、 call メソッドのレスポンスとして、 HTTP レスポンスコード、 HTTP レスポンスヘッダのハッシュ、レスポンスボディの3つを含む Array を返す必要があります。 Ruby の Proc は call が呼ばれることで中身が評価されるので、上記の例では Proc の内容としてレスポンスで返すべき Array を固定値で定義しています。

 Rack アプリケーションを動かすにはサーバのハンドラにアプリケーションのオプジェクトを渡します。 Rack には Ruby に標準で組み込まれている WEBrick のハンドラが含まれているので、その run メソッドに先ほどの Proc のオブジェクトを渡します。

 このファイルを Ruby スクリプトとして実行すると、下記のように WEBrick のサーバが起動します。デフォルトでは 8080 番ポートで起動します。

$ ruby my_rack_app.rb 
[2019-04-06 08:28:55] INFO  WEBrick 1.4.2
[2019-04-06 08:28:55] INFO  ruby 2.6.2 (2019-03-13) [x86_64-linux]
[2019-04-06 08:28:55] INFO  WEBrick::HTTPServer#start: pid=19549 port=8080

 curl でリクエストを投げると下記のように、 Proc で定義したレスポンスボディの内容がレスポンスとして返ってきます。

$ curl localhost:8080
A barebones rack app.

 サーバのログとしては下記のような出力があります。

127.0.0.1 - - [06/Apr/2019:08:30:57 UTC] "GET / HTTP/1.1" 200 21
- -> /

 この例ではどのパスにリクエストをしても同じ内容のレスポンスになります。

$ curl localhost:8080/
A barebones rack app. 
$ curl localhost:8080/hello
A barebones rack app.
$ curl localhost:8080/hello/world
A barebones rack app.
127.0.0.1 - - [06/Apr/2019:08:33:00 UTC] "GET / HTTP/1.1" 200 21
- -> /
127.0.0.1 - - [06/Apr/2019:08:33:03 UTC] "GET /hello HTTP/1.1" 200 21
- -> /hello
127.0.0.1 - - [06/Apr/2019:08:33:08 UTC] "GET /hello/world HTTP/1.1" 200 21
- -> /hello/world

rackup コマンドで動かす

 先ほどは Ruby スクリプトを動かす形で Rack アプリを実行しましたが、もう一つの方法として、 rackup コマンドでアプリケーションを動かす方法があります。そのためにはまず下記のような内容で config.ru ファイルを用意します。

# config.ru

run Proc.new { |env| ['200', { 'Content-Type' => 'text/html' }, ['get rack\'d'] ] }

 Ruby スクリプトで動かした場合と違い、 rackup コマンドで実行する場合はその環境を自動的に認識してくれます。設定ファイルの名前は config.ru ではなくても良いのですが、 rackup コマンドで引数として設定ファイルを指定しない場合はデフォルトで config.ru が使用されます。また、別名にする場合も拡張子は *.ru としておくと、 rackup の設定ファイルとして扱われるので、特別な理由がない場合はこのルールに乗っておくのが良いと思います。

 また、サーバとしては Thin や Puma がインストールされている場合は WEBrick よりも優先してそちらが使われます。インストールされていない場合は WEBrick が使われます。

 config.ru が置かれているディレクトリで rackup コマンドを実行すると、下記のようにサーバが起動します。 rackup の場合はデフォルトのポートとして 9292 番ポートが使用されます。

$ rackup
[2019-04-06 08:41:40] INFO  WEBrick 1.4.2
[2019-04-06 08:41:40] INFO  ruby 2.6.2 (2019-03-13) [x86_64-linux]
[2019-04-06 08:41:40] INFO  WEBrick::HTTPServer#start: pid=20113 port=9292

 curl でリクエストを投げると下記のようにレスポンスが返ります。

$ curl localhost:9292
get rack'd

 サーバのログとしては下記のような出力になります。

127.0.0.1 - - [06/Apr/2019:08:44:05 +0000] "GET / HTTP/1.1" 200 - 0.0003

 ちなみに Ruby on Rails でも config.ru が用意されていて、下記のような内容になっています。 Rails の挙動についてはまた別途追ってみたいと思います。

# This file is used by Rack-based servers to start the application.

require_relative 'config/environment'

run Rails.application

まとめ

 今まで Rack を直接触って動かすということはしてなかったのですが、 Rack ベースのフレームワークを使う上でも Rack がどういうものかわかっていた方がイメージがわきやすいと思うので、シンプルに動かしてみるというのは良いと思います。今回はとりあえず動かしてみた程度でしたが、もう少し中身の詳細も追ってみて、 Rails 等での挙動についても見て行きたいと思います。