見出し画像

Ruby 3.3.1 で `require 'nkf'` するとエラー

medical-expenses-manager の Ruby 3.3 の CI が毎回落ちていて、何でだろうと思っていた。
手元の 3.3.0 だと再現しないな。。。と思っていたが、つい最近 3.3.1 が出たのだった。3.3.1 に更新したところ、自分の環境でも落ちるようになった。

$ rails c
Loading development environment (Rails 7.1.3.2)
irb(main):001> require 'nkf'
/home/owner/.rbenv/versions/3.3.1/lib/ruby/3.3.0/bundled_gems.rb:130:in `<': comparison of String with nil failed (ArgumentError)

    msg = " #{RUBY_VERSION < SINCE[gem] ? "will no longer be" : "is not"} part of the default gems since Ruby #{SINCE[gem]}."
                             ^^^^^^^^^^

最初は nkf の GitHub に issue が無いかとか、 Bundler か RubyGems のバージョンの差異なんじゃないかとか、色々調べていたが、エラーが発生しているソースを読んでみると、どうやら bootsnap を読み込んでいるときに起きる問題らしいということがわかった。

  def self.warning?(name, specs: nil)
    # name can be a feature name or a file path with String or Pathname
    feature = File.path(name)
    # bootsnap expand `require "csv"` to `require "#{LIBDIR}/csv.rb"`
    name = feature.delete_prefix(LIBDIR).chomp(".rb").tr("/", "-")
    name.sub!(LIBEXT, "")

このエラーが起きている箇所というのが、Gemfile に nkf が無い状態で `require 'nkf'` したときに「nkf が Ruby 3.4 からは default gem じゃなくなるよ」という警告文を表示するロジックなのだが、 bootsnap ではこの `require 'nkf'` を書き換えているので、このロジックでもそれに追従しているということらしい。
そこまでわかったので、 Ruby の issue tracker と bootsnap の issue に 'nkf' が含まれるものが無いか探したが見つからなかったので、minimal repro を作って報告しようと思った。

Rails を使わない場合に bootsnap を有効化するにはどうするんだろうとか、1ファイルに gem の指定まで含めるのってどう書くんだっけとか、いろいろ調べて以下のスクリプトができた。

require 'bundler/inline'

gemfile do
  source 'https://rubygems.org'
  gem 'bootsnap', require: false
end

require 'bootsnap'
env = ENV['RAILS_ENV'] || "development"
Bootsnap.setup(
  cache_dir:            'tmp/cache',          # Path to your cache
  ignore_directories:   ['node_modules'],     # Directory names to skip.
  development_mode:     env == 'development', # Current working environment, e.g. RACK_ENV, RAILS_ENV, etc
  load_path_cache:      true,                 # Optimize the LOAD_PATH with a cache
  compile_cache_iseq:   true,                 # Compile Ruby code into ISeq cache, breaks coverage reporting.
  compile_cache_yaml:   true,                 # Compile YAML into a cache
  compile_cache_json:   true,                 # Compile JSON into a cache
  readonly:             true,                 # Use the caches but don't update them on miss or stale entries.
)

require 'nkf'

非 Rails アプリで bootsnap を使う方法は↓を見た。

これを docker コンテナで実行する。

$ docker run --rm -it -v "$PWD":/tmp -w /tmp ruby:3.3.1 ruby script.rb
/usr/local/lib/ruby/3.3.0/bundled_gems.rb:130:in `<': comparison of String with nil failed (ArgumentError)                                                               
    msg = " #{RUBY_VERSION < SINCE[gem] ? "will no longer be" : "is not"} part of the default gems since Ruby #{SINCE[gem]}."
                             ^^^^^^^^^^                                                                                                                                          from /usr/local/lib/ruby/3.3.0/bundled_gems.rb:130:in `build_message'                                                                                                    from /usr/local/lib/ruby/3.3.0/bundled_gems.rb:126:in `warning?'
        from /usr/local/lib/ruby/3.3.0/bundled_gems.rb:71:in `block (2 levels) in replace_require'
        from /usr/local/bundle/gems/bootsnap-1.18.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in `require'
        from script.rb:21:in `<main>'

3.3.0 だとエラーにならない。

$ docker run --rm -it -v "$PWD":/tmp -w /tmp ruby:3.3.0 ruby script.rb
$

いざ報告しようと思ったのだが、これは bootsnap に報告すべきなのか、Ruby 本体に報告すべきなのかがわからなかった。よく考えれば Ruby が bootsnap のための対応を入れているのだから Ruby に報告するのが筋なのだが、 bugs.ruby-lang.org のアカウント無いし、報告の仕方がよくわからん。。。と思って気が引けていた。
でも報告する前に、類似の報告がないか、 改めて '3.3.1' でクエリしたところ、バッチリ同じ issue が既に報告されていて、解決済みだった。

Ruby 3.3.1 がリリース されたのが 2024-04-23 で、この issue が報告されたのが 2024-04-24 だから次の日には報告されてたのか。でその次の日には修正されている。

というわけで2時間くらい調査したがすでに解決済みの問題だったので報告には至らなかった。まあこの辺りのコードについて勉強になったので収穫はあったということで。。。


この記事が気に入ったらサポートをしてみませんか?