The file_exists Gem: File.exists? and Dir.exists? on Ruby 3.2+
Ruby 3.2 removed File.exists? and Dir.exists?. Both had been deprecated for years in favour of File.exist? and Dir.exist? (without the “s”), but plenty of older code still used them. After upgrading, that code fails with:
undefined method `exists?' for File:Class (NoMethodError)
I wrote about this right after Ruby 3.2 came out in File.exists? and Dir.exists? on Ruby 3.2 Workaround. This post is a current overview of the small gem that came out of it: file_exists.
What it does
The gem adds File.exists? and Dir.exists? back as aliases of File.exist? and Dir.exist?. That is all. The whole library is a few lines:
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
Since version 0.2.0 the alias is only added if the method is actually missing, so the gem does nothing on older Rubies that still have exists?. The gemspec allows Ruby 2.0 and newer. It has no dependencies and is MIT licensed.
Why it exists
The right fix is always to rename the call. But sometimes the call is not in your code: it is inside a gem you depend on, and that gem is unmaintained, or the fix is merged but not released. I ran into exactly that with openssl-win-root. In that situation you want your app to keep running on the new Ruby while you wait for (or work on) the proper fix upstream. That is the gap file_exists fills.
According to RubyGems it has been downloaded more than 760,000 times, so I was clearly not the only one with this problem.
How to use it
Add it to your Gemfile:
bundle add file_exists
Or install it directly:
gem install file_exists
Then require it as early as possible, before any code that calls File.exists? or Dir.exists? is loaded:
require 'file_exists'
File.exists?("Filename.txt") # => same result as File.exist?
Dir.exists?("Dirname") # => same result as Dir.exist?
Order matters. If another gem calls File.exists? while it is being loaded, the alias has to be in place before that require. In a Rails app, a good spot is config/boot.rb, right after require "bundler/setup".
When you should fix your code instead
If the call is in code you control, don’t use this gem. Just rename it:
File.exists?becomesFile.exist?Dir.exists?becomesDir.exist?
File.exist? and Dir.exist? work on every Ruby version you are likely to run, so the change is safe. To find the remaining calls:
grep -rnE "(File|Dir)\.exists\?" --include="*.rb" .
If you use RuboCop, the Lint/DeprecatedClassMethods cop flags these calls as well.
For third-party gems, check first whether a newer version already fixes it. If not, open an issue or a pull request upstream. Use file_exists as a bridge until that fix is released, not as a permanent part of your stack. It changes core classes for the whole process, and once nothing needs the old names anymore, it is one dependency you can remove.
A correction to my old post: the hand-written monkeypatch shown there originally called exist? without passing the filename along. I have fixed it now. If you want to copy something instead of installing the gem, the alias_method version above is the better choice: it is what the gem actually ships.