11
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

この記事はソニックガーデン 若手プログラマ Advent Calendar 2024の22日目の記事です。

普段業務でRuby on Railsを使用しているのですが、ある日いつも通りRailsガイドを読んでいると「Rack」という単語に目が止まりました。

「…うん、Rackって何?」となりました。

RailsガイドになんでRackとやらの説明が載っているんだ?と困惑し、さらにページを読み進めるとそもそも「ミドルウェア」という単語が理解できていないことに気がつきました。お恥ずかしいかぎりです。

とはいえいつまでも恥ずかしがっていられないので、これを機に調べました。

アドカレ二本目の記事は、Railsを使うなら押さえておきたいRackについて調べたことを共有したいと思います。

Rackとは

Rackは、簡単にいうとwebアプリケーションサーバーとフレームワーク間を共通化してくれるライブラリです。

例えばRailsというフレームワークではPumaやUnicornサーバーなどを利用することが多いですよね。ここは魔法で連動していると思っていましたが、その魔法の正体はRackという仲介役なんですね。

RailsはRackというインターフェースに則っているので、複数のサーバーで起動することができるのです。

Rackの基本を理解する

Rackはシンプルなインターフェースを定義しています。

def call(env)
 [status, headers, body]
end
  • callメソッドを定義する
  • 引数にはenvを受け取る
  • callメソッドは、ステータスコード・ヘッダー(中身はハッシュオブジェクト)、レスポンスボディを戻り値として返す

Rackアプリケーションを動かしてみよう

Rackアプリケーションを作成してみます。

まずはディレクトリを作成し、rack gemをインストールしましょう

mkdir rack-sample
cd rack-sample

gem install rack

Rackの規約に準ずるAppクラスを定義します。ファイル名はapp.rbです。

class App
  def call(env)
    status = 200
    headers = { 'content-type' => 'text/html' }
    body = ['Hello, World!']
    [status, headers, body]
  end
end

次に、エントリーポイント用のファイルを作成しましょう。ファイル名はconfig.ruです。

require 'rack'
require_relative 'app'

run App.new

これで準備は完了です。

rackupコマンドでサーバー立ち上げされます。

rackup 

CleanShot 2024-12-21 at 15.34.32@2x.png

callメソッドに記述した戻り値が画面に表示されています!

改めてRackの構造をまとめると

  • rackupコマンドを叩くと、config.ruに記述したrunメソッドが呼ばれる
  • runメソッドは、callメソッドの戻り値をレスポンスとしてもらう

Rackミドルウェアって何?

Rackはミドルウェアとして紹介されることがあります。

ミドルウェアとは「アプリケーションサーバーとアプリケーションの間に処理を追加する」機構のことを指します。

Rackミドルウェアも、Rackアプリケーション同様に実装することができます。

用意するものは、initializeメソッドとcallメソッドの二つです。

Appクラスとは別に、SimpleMiddlewareクラスを作成してみます。

class SimpleMiddleware
  def initialize(app)
    @app = app
  end

  def call(env)
    status, headers, body = @app.call(env)
    return [status, headers, body]
  end
end

initializeメソッドでは何をしているのでしょうか。引数のappとは何なのか、確かめるためにputsで確認してみます

class SimpleMiddleware
  def initialize(app)
	puts "*" * 50 # 表示のため
    puts "* #{self.class} initialize(app = #{app.class})"
    puts "*" * 50 # 表示のため
    @app = app
  end

  def call(env)
    status, headers, body = @app.call(env)
    puts "*" * 50 # 表示のため
    puts "* #{self.class} call(body = #{body})"
    puts "*" * 50 # 表示のため
    return [status, headers, body]
  end
end

config.ruも書き直します。

require 'rack'
require_relative 'app'
require_relative 'simple_middleware'

use SimpleMiddleware
run App.new

rackupしてサーバーを立ち上げます

rackup
rack-practice % rackup
**************************************************
* SimpleMiddleware initialize(app = App)
**************************************************
# 略
**************************************************
* SimpleMiddleware call(body = ["Hello, World!"])
**************************************************

これによると、initializeメソッドでappとして受け取っていたのはAppクラスということがわかります。

そしてbodyとして受け取っているものは、Appクラスのcallメソッドを呼び出した内容なんですね。

今回のミドルウェア実装では以下のことが分かりました。

  • Rackアプリケーションには、中心(今回はApp.rb)と周辺(今回はSimpleMiddleware.rb)という概念が存在する
  • ミドルウェアはinitializeで、アプリケーションの中心にあたるオブジェクトを受け取り、自分自身のcallメソッドが呼ばれた時に、initializeで受け取ったオブジェクトのcallメソッドを呼び出し、処理を行う

今回扱ったことはかなり基本中の基本ですので、まだまだ奥が深そうです。

終わりに

Railsでコードを書く以上、避けては通れないRackについて調べてみました。正直完全に理解できたとは言い難いですが、仕組みについて一定知識を得ることができたと思います。読者の皆さんにとっても、理解の一助けになると嬉しいです。

参考文献

  • すがわら まさのり他、パーフェクト Ruby on Rails【増補改訂版】、技術評論社、2022、p129~p137

ソニックガーデン 若手プログラマ Advent Calendar 2024」23日目は@ynitamiです。お楽しみに!

11
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?