2
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?

More than 1 year has passed since last update.

Rack 3.0 で Rack::Server が消えた

Last updated at Posted at 2022-09-17

とある Ruby のプロジェクトで依存 gem のバージョンを上げたらエラーが出たのでその原因を探った。

起こったこと

ある種のウェブアプリ機能を持った gem を開発し,長らく多数のプロジェクトで使っていた。

この gem は rack という gem を利用している1。rack への依存はとくにバージョンを指定していない。

ある日,bundle update で gem たちのバージョンを上げたところ,rack もバージョン 2.2.4 から 3.0.0 に上がった。
メジャーバージョンが上がったので何かしら非互換性があるかとは予想したが,起動しようとしたら案の定

cannot load such file -- rack/server (LoadError)

が出て死んだ。

rack/server って何だっけ?

最小再現コード

今回発生したのと同じ問題を最小再現コードで示すと以下のようになる。

まず Gemfile。rack と webrick のみを使う。webrick はウェブサーバー。

Gemfile
source "https://rubygems.org"

gem "rack"
gem "webrick"

次に,ウェブアプリ。現在時刻を text/plain(つまり HTML じゃなくてただのテキスト)で返すだけの簡単なもの。

require "bundler"
Bundler.require

class App
  def call(env) # 引数は使ってない

    # レスポンスボディー(配列の形にする)
    body = ["Now #{ Time.now }"] 

    # ステータス,ヘッダー,ボディーの三つ組を返す
    [200, {"Conent-Type" => "text/plain"}, body]
  end
end

Rack::Server.new(app: App.new).start

最終行で Rack::Server を動かしている。
Rack::Server を生成するとき,app として「call に反応するオブジェクト」を渡してやる。

この単純なスクリプトを動かし,ブラウザーで http://localhost:8080 にアクセスすると,画面に

Now 2022-09-17 18:00:57 +0900

みたいなものが表示される。

ところが,2022 年 9 月 6 日に rack 3.0.0 がリリースされ,このバージョンを使うと

Rack::Server.new(app: App.new).start

のところで件の LoadError が出るようになった。

原因と対策

rack のアップデートに伴う現象なので,もちろん

Gemfile(抜粋)
gem "rack", "< 3"

のようにバージョンを 3 未満に抑えてやればいちおう解決するわけだが,ここはきちんと原因を調べて最新の rack で動くようにしておきたい。

rack の CHANGELOG を見に行くと,[3.0.0.beta1] - 2022-08-08 の「Removed」のところに,「Rack::Server 等を別 gem に分離した」というようなことが書いてある。

その別 gem というのは,調べたところ rackup であるらしい(CHANGELOG に具体的な gem 名も書いておいてほしかった)。
ではこれを使おう。

rackup gem を使う

まずは Gemfile を以下のように変更してみた。

Gemfile
source "https://rubygems.org"

gem "rackup"
gem "webrick"

rackup は rack に依存しているので Gemfile に rack を書く必要はないはず。

これで bundle install する。

rack gem はバージョン 3 未満で rackup というコマンドを持っていた。rackup gem も同名のコマンドを持っているので,こいつをシステムにインストールするとコマンドを上書きしてしまう。一抹の不安がよぎるが,まあ今後,全プロジェクトで rack 2 系から 3 系に移行していくので,もし問題が生じてもどうにかできるかな。

さて,この状態で件のスクリプトを動かすと,今度は

uninitialized constant Rack::Server (NameError)

が出た。
おぅ! rackup gem には Rack::Server が無いんかいっ。

調べたところ,Rack::Server を単純に Rackup::Server に書き換えればよいことが分かった。
予想よりずっと簡単な修正で解決した。めでたしめでたし。

修正後のスクリプトも載せておく:

require "bundler"
Bundler.require

class App
  def call(env)
    body = ["Now #{ Time.now }"]
    [200, {"Conent-Type" => "text/plain"}, body]
  end
end

Rackup::Server.new(app: App.new).start

追記

Gemfile に rackup と rack を両方書いた状態で Rack::Server を使うと NameError が出ずにアプリが動くが,

Rack::Server is deprecated and replaced by Rackup::Server

と警告が出る。

まとめ

今まで Rack::Server を使っていたプロジェクトは,これからは rackup gem の Rackup::Server を使うべし。

  1. rack を一言で言えばウェブアプリとウェブサーバーの仲立ちをするライブラリー。ウェブサーバーの差異を吸収する働きがあり,ウェブサーバーの取り替えが簡単になる。

2
1
0

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
2
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?