Help us understand the problem. What is going on with this article?

Bundler が Ruby 2.7 で変な件

Ruby 2.7 で Bundler がらみの不可解な現象に遭遇している。

「何やねん,早よ言え」という方は「総括」の節を見てくだされ。

前提

この記事で述べる実験は 2020-01-17 時点のもの。

Ruby 2.6.5 と Ruby 2.7.0 の二つについて実験を行った。

Windows と macOS と CentOS で実験した。
Windows は RubyInstaller for Windows でインストールしたもの。他は rbenv でインストール。

Ruby 2.6.5 も 2.7.0 も,デフォルトの bundler gem は 2.1.2 だが,

gem update bundler

して,bundler 2.1.4 をインストールした。
したがって,どの OS,どの Ruby でも,2.1.2 と 2.1.4 が入った状態。

実験 1

まず,OS と Ruby バージョンに関わらず,端末で

bundle -v

とやったら

Bundler version 2.1.4

と表示された(当然)。

次に,

gem list bundler

とやったときの結果が意外だった。

macOS と CentOS の Ruby 2.7.0 でだけ

bundler (2.1.4)

と表示され,その他の OS,Ruby バージョンの組み合わせでは

bundler (2.1.4, default: 2.1.2)

と表示されたのだ。後者のほうがしかるべき表示のような気がする。
Ruby のバージョンだけで違いが生じるなら「Ruby 2.7 で仕様が変わったのかな」と思うところだが,Windows の場合,バージョンによる違いはない。

追記 2020-01-24

Ruby 2.7 で,CentOS および macOS でのみ 2.1.2 が見えなかったのは,rbenv のプラグイン rbenv-communal-gems のせいかもしれない。確証はないけど。

実験 2

以下のスクリプトを実行した。

gem "bundler"
require "bundler"

p Bundler::VERSION

すると,すべての組み合わせにおいて,「"2.1.4"」が表示された。
これは期待通り。
インストールされている最新版が require されるはずだから。

実験 3

前の実験をちょっと改変して,

require "bundler"

p Bundler::VERSION

としてみた。
すると今度は,どの OS でも,Ruby 2.6.5 では「"2.1.4"」と表示されるが,Ruby 2.7.0 では「"2.1.2"」と表示される。
え? 最新版じゃない?

これは何か Ruby 2.7 で仕様が変わったとした思えない。うーむ。

実験 4

まず,Gemfile に以下のようにだけ書く。

Gemfile
source "https://rubygems.org"

(なんか gem を指定してもいいのだが,何も書かなくても実験は可能)

そして,コマンドライン上で

bundle check

する。
そうすると,Gemfile.lock が出来る。
Ruby 2.6.5 でやっても,Ruby 2.7.0 でやっても,その末尾には

BUNDLED WITH
   2.1.4

と書かれる。
これは納得できる話だ。実験 1 で,bundle コマンドは最新版である 2.1.4 が使われることを確認しているから。

しかし,この状態で以下のスクリプトを動かすとどうなるか。

require "bundler"
Bundler.require

Ruby 2.6.5 では何事もなく終わるが,Ruby 2.7.0 ではドバドバっとメッセージが出る。
先頭はこれ。

Warning: the running version of Bundler (2.1.2) is older than the version that created the lockfile (2.1.4). We suggest you to upgrade to the version that created the lockfile by running `gem install bundler:2.1.4`.

これはエラーではなく警告。
「Gemfile.lock に書いとる(Bundler の)バージョンは 2.1.4 なのに,今走っとる Bundler は 2.1.2。古いやんけ」
と怒っているようだ。

親切にも
gem install bundler:2.1.4 ってやってアップグレードしたらよろし」
て suggest してくれてるのだが,もう入っとるっちゅーに。

末尾はこれ。

/Users/hogehoge/.rbenv/versions/2.7.0/lib/ruby/2.7.0/rubygems.rb:275:in `find_spec_for_exe': Could not find 'bundler' (2.1.4) required by your /Users/hogehoge/temp/xxx/Gemfile.lock. (Gem::GemNotFoundException)
To update to the latest version installed on your system, run `bundle update --bundler`.
To install the missing version, run `gem install bundler:2.1.4`

こっちは警告じゃなくてエラー。
「Gemfile.lock で要求されている bundler 2.1.4 が,み,見つからない」(がくっ)
という dying message を残して死亡している。

これはどう解釈できるのか?
実験 3 の結果と整合的,という気はする。
Ruby 2.7.0 では単純に bundler を require すると,なぜか最新版ではなく 2.1.2 が読み込まれるのだった。

実験 5

実験 4 と同じファイルを使い,

bundle exec ruby hoge.rb

のような形でスクリプトを動かすと正常だった。
bundle exec は,(よく分からんけど)Gemfile.lock の内容を見て使う gem のバージョンを決定してからスクリプトを動かすらしいので,これで正常化するのは納得できる気がする。

ruby コマンドでなく,Rake タスクを実行したいときは要注意。
いままで

rake hoge

とやっていたのを

bundle exec rake hoge

とするためには,Gemfile に gem 'rake' を入れてやらないといけない。

総括

一言でまとめると,どうやら Ruby 2.7 では bundler のバージョンを指定する仕組みを使わない限り,最新版の bundler が require されることはない,ということのようだ。

それによって各種の問題が起こる,と。

対策

結局どうすればいいのか。
おそらく以下の二つしかないと思う。

  • bundle exec を付けて実行
  • require "bundler" の前に gem "bundler" を書く

前者の場合,Rake タスクなら Gemfile を書き換える必要がある。確かめてはいないが Thor タスクでも同様だろう。
後者の場合も当然スクリプトを書き換える必要がある。

既存のコードを大量に書き換えなければならないことになりそう。
え〜っ

解決(追記 2020-04-23)

何だかよく判らないのですが,以下の記事
【Rails6】rails new で bundler: failed to load command: spring が出た。問題はbundler2.1.4かRuby2.7にあるようだが..... - Qiita
に書かれている

$ gem update --system 3.0.8 && gem update --system

を試したところ直りました。

scivola
主に Ruby 使ってます。 二十年来のコンパイラー恐怖症が Rust で治癒するか?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away