← Blog

file_exists gem:Ruby 3.2以降でFile.exists?とDir.exists?を使う

Ruby 3.2で File.exists? と Dir.exists? が削除されました。どちらも「s」の付かない File.exist? と Dir.exist? が推奨されるようになってから長い間非推奨でしたが、古いコードではまだあちこちで使われていました。Ruby 3.2にアップグレードすると、そうしたコードは次のエラーで止まってしまいます。

undefined method `exists?' for File:Class (NoMethodError)

Ruby 3.2のリリース直後に、この問題について File.exists? and Dir.exists? on Ruby 3.2 Workaround(英語)という記事を書きました。今回は、そこから生まれた小さなgem file_exists について、現在の状況をあらためて紹介します。

何をするgemか

このgemは、File.exists? と Dir.exists? を File.exist? と Dir.exist? のエイリアスとして追加し直します。やっていることはそれだけで、ライブラリ本体は数行しかありません。

unless Dir.respond_to?(:exists?)
  class << Dir
    alias_method :exists?, :exist?
  end
end

unless File.respond_to?(:exists?)
  class << File
    alias_method :exists?, :exist?
  end
end

バージョン0.2.0からは、メソッドが本当に存在しない場合にだけエイリアスを追加するようにしました。そのため、まだ exists? が残っている古いRubyでは何もしません。gemspec上はRuby 2.0以降に対応しています。依存gemはなく、ライセンスはMITです。

なぜこのgemがあるのか

正しい対処は、いつでも呼び出しを書き換えることです。ただ、問題の呼び出しが自分のコードではなく、依存しているgemの中にあることもあります。そのgemがもうメンテナンスされていなかったり、修正はマージ済みでもまだリリースされていなかったりするケースです。私自身も openssl-win-root でまさにこの状況に遭遇しました。こういうときは、上流の正式な修正を待つ間(あるいは自分で修正に取り組む間)も、新しいRubyでアプリを動かし続けたいものです。file_existsはその隙間を埋めるためのgemです。

RubyGemsによると、ダウンロード数は76万回を超えています。同じ問題に困っていたのは私だけではなかったようです。

使い方

Gemfileに追加します。

bundle add file_exists

または直接インストールします。

gem install file_exists

そして、File.exists? や Dir.exists? を呼ぶコードが読み込まれる前に、できるだけ早い段階でrequireしてください。

require 'file_exists'

File.exists?("Filename.txt") # => File.exist? と同じ結果
Dir.exists?("Dirname")       # => Dir.exist? と同じ結果

読み込む順番が重要です。ほかのgemが読み込み時に File.exists? を呼んでいる場合は、そのgemの require より前にエイリアスが定義されている必要があります。Railsアプリなら、config/boot.rb の require "bundler/setup" の直後がちょうどよい場所です。

gemを使わずにコードを直すべきとき

呼び出しが自分で管理しているコードの中にあるなら、このgemは使わずに書き換えてください。

File.exist? と Dir.exist? は、実際に使われているどのRubyバージョンでも動くので、安心して置き換えられます。残っている呼び出しは次のコマンドで探せます。

grep -rnE "(File|Dir)\.exists\?" --include="*.rb" .

RuboCopを使っている場合は、Lint/DeprecatedClassMethods というcopでもこれらの呼び出しを検出できます。

サードパーティのgemの場合は、まず新しいバージョンで修正されていないか確認しましょう。修正されていなければ、上流にissueかpull requestを出してください。file_existsは、その修正がリリースされるまでのつなぎとして使うもので、ずっと使い続けるものではありません。このgemはプロセス全体でコアクラスを書き換えますし、古い名前が不要になれば依存を一つ減らせます。

最後に、以前の記事の訂正です。そこで紹介した手書きのモンキーパッチは、もともと exist? にファイル名を渡していませんでした。現在は修正済みです。gemを入れずにコードをコピーしたい場合は、上の alias_method 版がおすすめです。gemに実際に入っているのもこのコードです。

リンク