この章では、Rackを使ってWebアプリケーションを実装してみます。Rackの基本的な仕組みを理解し、シンプルなアプリケーションから始めて、徐々にRackアプリケーションとはなんなのか、理解を深めてみましょう。
RubyGemsを使ってRackをインストールします。 ここではRackアプリケーションのためのコア機能を提供するrack gemと、Rackアプリケーションを起動するrackupコマンドを提供するrackup gemをインストールします。
$ gem install rack rackup
gemをインストールしたらrackupコマンドが実行できることを確認します。
$ rackup --help
Usage: rackup [ruby options] [rack options] [rackup config]
...
これでRackアプリケーションを書く準備は完了です。
まずは、最もシンプルなRackアプリケーションを作成してみましょう。
- 新しいファイル
app.ru
を作成し、以下のコードを記述します。class App def call(env) [ 200, {}, ["hello"], ] end end run App.new
call
メソッドは、Rackアプリケーションの環境情報とHTTPリクエストの情報が格納されたHashenv
を受け取り、ステータスコード、ヘッダー、ボディの配列を返します。
call
メソッドは必ず[status, headers, body]
の形式でレスポンスを返す必要があります。body
は通常、文字列を含む配列を返します。- .ru という拡張子は rackup config ファイルの拡張子で、
run
などのRack独自のメソッドが定義されたRubyのDSLで記述されます。- 例として app.ru という名前を指定しましたが、拡張子以外の部分の名前は任意です。rackupコマンドはデフォルトだと config.ru という設定ファイルを探すので、config.ru という名前にすると rackup コマンドを実行するだけでアプリケーションが起動します。
ターミナルで以下のコマンドを実行します。
$ rackup app.ru
ブラウザやcurlコマンド等で http://localhost:9292
にアクセスして、hello
と表示されれば成功です。
Rackアプリケーションが受け取る env
には、リクエストに関するさまざまな情報が含まれています。これを確認してみましょう。
call
メソッド内にbinding.irb
を挿入します。class App def call(env) binding.irb # ... end end
ターミナルでRackアプリケーションを実行して http://localhost:9292
にアクセスすると、ターミナル上でirbセッションが開始されます。env
を調べてみましょう。
$ rackup app.ru
[2024-10-20 20:43:59] INFO WEBrick 1.8.1
[2024-10-20 20:43:59] INFO ruby 3.3.4 (2024-07-09) [arm64-darwin23]
[2024-10-20 20:43:59] INFO WEBrick::HTTPServer#start: pid=68048 port=9292
From: /Users/hogelog/repos/hogelog/kaigionrails/01-app/app.ru @ line 3 :
1: class App
2: def call(env)
=> 3: binding.irb
4: [
5: 200,
6: {"content-type" => "text/plain"},
7: ["hello"],
8: ]
irb(#<App:0x0000000101952048>):001> env
=>
{"GATEWAY_INTERFACE"=>"CGI/1.1",
"PATH_INFO"=>"/",
"QUERY_STRING"=>"",
"REMOTE_ADDR"=>"::1",
"REMOTE_HOST"=>"::1",
"REQUEST_METHOD"=>"GET",
"REQUEST_URI"=>"http://localhost:9292/",
...
env
にはリクエストに関する情報が含まれており、リクエストメソッドやパス、ヘッダー、クエリパラメータなどが確認できます。様々なパスやメソッドでアクセスして、env
の中身が変わることを確認してみましょう。
次はHTTPレスポンスヘッダーを設定する方法を確認してみましょう。
call
メソッドで返すヘッダーに"content-type" => "text/plain"
を追加します。class App def call(env) [ 200, {"content-type" => "text/plain"}, ["hello"], ] end end
- レスポンスヘッダーはハッシュで表現します。
- HTTP/1.xではレスポンスヘッダーのキーの大文字小文字は無視されますが、Rackアプリケーションでは小文字で統一するよう定められています。
これまでと同様rackupコマンドで起動し、ブラウザの開発者ツールやcurlコマンドでレスポンスヘッダーを確認してみましょう。
$ curl -i http://localhost:9292/
HTTP/1.1 200 OK
content-type: text/plain
Content-Length: 5
hello
またcontent-type以外にも任意のヘッダーを設定可能なので、他のヘッダーも設定してみてください。
次に、リクエストのパスやメソッドに応じて、異なるレスポンスを返すようにしてみましょう。
call
メソッド内でリクエストの情報を取得します。class App def call(env) method = env["REQUEST_METHOD"] path = env["PATH_INFO"] # メソッド、パスに応じた処理を実装 # ... end end run App.new
- メソッド、パスに応じた処理を実装してください。
GET /
リクエストが来たらIt works!
を返すよう実装。GET /hello/foobar
のようなリクエストにはHello foobar!
を返すよう実装。
env
からリクエストメソッドREQUEST_METHOD
やリクエストパスPATH_INFO
を取得できます。それらの値に応じて処理を分岐してみましょう。
これまでと同様rackupコマンドで起動し、ブラウザの開発者ツールやcurlコマンドでレスポンスを確認してみましょう。
$ curl http://localhost:9292/
It works!
$ curl http://localhost:9292/hello/hogelog
Hello hogelog!
適切に実装できていれば、上記したようなレスポンスが返ってくるはずです。
先ほどまではenvに含まれた値をそのまま扱って処理を実装していました。ここではRackが提供する便利なクラスやメソッドを使ってコードを簡潔に記述してみましょう。
Rack::Request
とRack::Response
を使用して、以下のようにリクエストとレスポンスを扱ってみます。require "rack/request" require "rack/response" class App def call(env) request = Rack::Request.new(env) case [request.request_method, request.path_info] in ["GET", "/"] Rack::Response.new("It works!", 200).finish # ... end end end run App.new
- 上記実装に加え、パスに応じた処理を実装してください。
GET /hello/foobar
のようなリクエストにはHello foobar!
を返すよう実装。GET /
,GET /hello/foobar
以外のリクエストには404ステータスコードでNot Found
を返すよう実装。
Rack::Request
はリクエストに対する便利なメソッドを提供します。Rack::Request.new
はcall(env)
で渡される env を引数として受け取ります。
Rack::Response
はレスポンスの組み立てをわかりやすくします。Rack::Response.new
は body, status, headers を引数として受け取ります。Rack::Response#finish
メソッドで[status, headers, body]
形式のレスポンスを返します
これまでと同様rackupコマンドで起動し、ブラウザの開発者ツールやcurlコマンドでレスポンスを確認してみましょう。
$ curl http://localhost:9292/
It works!
$ curl http://localhost:9292/hello/hogelog
Hello hogelog!
$ curl -i http://localhost:9292/foobar
HTTP/1.1 404 Not Found
content-type: text/plain
Content-Length: 9
Not Found
正しく実装できていれば、以上のようにリクエストに応じたレスポンスが返ってくるはずです。
次に、Rubyの軽量なWebフレームワークであるSinatraを使って、同様の機能を実装してみましょう。
sinatra.ru
というファイルを作成し、Sinatraのベースクラスを継承したApp
クラスを以下のように定義します。require "sinatra/base" class App < Sinatra::Base get "/" do "It works!" end get "/hello/:name" do "Hello #{params[:name]}" end end run App.new
- Sinatraは
Sinatra::Base
を継承したクラス内でDSLを使ってルートを定義します。- "GET /foo" のリクエストに対応する処理は
get '/foo' do ... end
のように記述します。 - リクエストのクエリやURLパラメタなどは
params
ハッシュから取得できます。
- "GET /foo" のリクエストに対応する処理は
sinatra gemをインストールした上で、これまでのようにrackupコマンドでアプリケーションを起動しましょう。
$ gem install sinatra
...
$ rackup sinatra.ru
...
起動したアプリケーションに対しブラウザやcurlコマンドで動作を確認してみましょう。
$ curl http://localhost:9292/
It works!
$ curl http://localhost:9292/hello/hogelog
Hello hogelog!
Sinatraを使うことでRackアプリケーションを非常に簡潔に記述できることがわかります。
最後に、Railsを使って、同じ機能を実装してみましょう。Railsは起動するときなども通常rackupコマンドを使わず、意識することも少ないかもしれませんが、RailsアプリケーションがRackアプリケーションの形で提供されています。
ここではフルセットのRailsアプリケーションではなく、1ファイルのシンプルな形でRailsアプリケーション定義をしてrackupコマンドで起動してみます。
rails.ru
というファイルを作成し、ここにRailsアプリケーションを定義していきます。- 最小限のRails機能をロードするために、
action_controller/railtie
をロードします。require "action_controller/railtie"
- 最低限のRailsアプリケーション、コントローラ定義します。
require "action_controller/railtie" class App < Rails::Application config.secret_key_base = "secret_key_base" config.logger = Logger.new($stdout) Rails.logger = config.logger routes.draw do root "apps#index" resources :apps, only: :show, path: "hello" end end class AppsController < ActionController::Base def index render plain: "It works!" end def show render plain: "Hello #{params[:id]}!" end end run Rails.application
- 最後に、
run Rails.application
でアプリケーションを起動します。run Rails.application
rails.ru 全体
require "action_controller/railtie"
class App < Rails::Application
config.secret_key_base = "secret_key_base"
config.logger = Logger.new($stdout)
Rails.logger = config.logger
routes.draw do
root "apps#index"
resources :apps, only: :show, path: "hello"
end
end
class AppsController < ActionController::Base
def index
render plain: "It works!"
end
def show
render plain: "Hello #{params[:id]}!"
end
end
run Rails.application
rails gemをインストールし、rackupコマンドでアプリケーションを起動します。
$ gem install rails
...
$ rackup rails.ru
[2024-10-21 00:57:36] INFO WEBrick 1.8.1
[2024-10-21 00:57:36] INFO ruby 3.3.4 (2024-07-09) [arm64-darwin23]
[2024-10-21 00:57:36] INFO WEBrick::HTTPServer#start: pid=74850 port=9292
ブラウザやcurlなどでアクセスし、以下のように正しくレスポンスを返せていることを確認してみましょう。
$ curl http://localhost:9292/
It works!
$ curl http://localhost:9292/hello/hogelog
Hello hogelog!
このRackアプリケーションはRailsの機能のごく一部しか利用していませんが、RailsアプリケーションがRackアプリケーションを定義するものであることがわかります。
この章では、Rackアプリケーションの基本的な構造から始めて、環境変数の確認、レスポンスヘッダーの設定、ルーティングの実装、Rackライブラリの活用、SinatraやRailsを使った実装までを学びました。これらの基礎を押さえることで、さまざまなフレームワークやライブラリを使ったアプリケーションの構築に役立てることができます。
これまでの内容を通じて、以下のポイントを理解できたと思います。
- Rackの基本構造:
call
メソッドで[status, headers, body]
を返す。 - Rackアプリケーション引数
env
の利用: リクエストに関する情報を取得できる。 - Rackのライブラリの活用:
Rack::Request
やRack::Response
といった便利なクラスの提供。 - SinatraやRailsとの組み合わせ: SinatraやRailsといったフレームワークとRackの関係性。
次の章では、Rackミドルウェアの作成や、ミドルウェアを使った機能拡張について学んでいきます。