The project is in a healthy, maintained state
Backward compatibility for Ruby 3 stdlib


>= 0
 Project Readme


This gem provides a compatibility layer to Ruby 3 projects that still have legacy code written against Ruby 2's stdlib. Only use this if you upgrade to Ruby 3 but still depend on some third-party code that breaks on Ruby 3.

The gem will fix only some (but the most common) incompatibilities.


Add this line to your application's Gemfile:

gem 'ruby3-backward-compatibility'

And then execute:

$ bundle install

Or install it yourself as:

$ gem install ruby3-backward-compatibility


You need to require the specific backports yourself, see below.

You can also require all included backports using

require 'ruby3-backward-compatibility/compatibility/all'

Note however that this will not patch anything that has not been required yet. You can always require single patches as shown below.

List of backports

Ruby 3 keyword arguments

One breaking change in Ruby 3 is that methods defined using keyword arguments have to be called with keyword arguments, not with option hashes. For example

class Ruby3Class
  def some_method(foo:)
    puts foo
end 'bar') # works{ foo: 'bar' }) # raises an ArgumentError

To fix this for arbitrary methods, you can use the callable_with_hash method included in this gem, like this:

require 'ruby3_backward_compatibility'

class Ruby3Class
  # reopen the class

  extend Ruby3BackwardCompatibility::CallableWithHash

  callable_with_hash :some_method
end 'bar') # still works{ foo: 'bar' }) # now works as well

This will wrap the given method and convert a hash given as the last argument into keywords, similar to how it worked in Ruby 2.

Note: In case the method is also defined in a prepended module, you need to put the extend Ruby3BackwardCompatibility::CallableWithHash above the prepend.


Dir.exists? has been removed in favor of Dir.exist?.

To add Dir.exists? as an alias for Dir.exist?, use

require 'ruby3_backward_compatibility/compatibility/dir'

ERB used to have the signature, safe_level, trim_mode, eoutvar = '_erbout')

but this was changed to, safe_level: nil, trim_mode: nil, eoutvar: '_erbout')

To allow both styles, use

require 'ruby3_backward_compatibility/compatibility/erb'


File.exists? has been removed in favor of File.exist?.

To add File.exists? as an alias for File.exist?, use

require 'ruby3_backward_compatibility/compatibility/file'


Fixnum has been an alias for Integer for a while, but was removed.

To add back Fixnum as an alias for Integer, use

require 'ruby3_backward_compatibility/compatibility/fixnum'


I18n has a few methods (translate, localize, etc.) that requires to be called with keywords.

To allow calling it with option hashes, too, use

require 'ruby3_backward_compatibility/compatibility/i18n'


The methods Object#taint and Object#untaint were no-ops for a while but started to raise deprecation warnings.

The method Object#=~ always returned nil if called on anything else than a String. It was removed in Ruby 3.2.

To add back Object#taint and Object#untaint as no-ops and Object#=~ on arbitrary receivers, use

require 'ruby3_backward_compatibility/compatibility/object'

Regexp (Ruby 3.2+)

It was possible to instantiate a Regexp with'foo', Regexp::IGNORECASE, 'n'). The last argument used to indicate the encoding in Ruby 1.8. Up to Ruby 3.2 an argument of "n" or "N" indicated a regexp that ignores encoding (same as the Regexp::NOENCODING flag).

In Ruby 3 this raises an ArgumentError.

To bring back the old behavior, use

require 'ruby3_backward_compatibility/compatibility/regexp'

YAML (Psych)

Psych version 4 (default for Ruby 3.1) has two changes:

  • Psych.load has been renamed to Psych.unsafe_load

  • The signature of Psych.safe_load has been changed from

    Psych.safe_load(yaml, permitted_classes = [], permitted_symbols = [], aliases = false, filename = nil)


    Psych.safe_load(yaml, permitted_classes: [], permitted_symbols: [], aliases: false, filename: nil)

To allow both styles of calling Psych.safe_load, use

require 'ruby3_backward_compatibility/compatibility/psych'

Attention: There has been a very good reason why Psych renamed the .load method: You may never use .load on any external strings. It is possible to create valid YAML strings that lead to the execution of arbitrary code, so calling YAML.load on user input is a major security vulnerability. For that reason, this library does not change the safe behavior of YAML.load.


String#encode require keyword arguments. To allow it to be called with an options hash, use

require 'ruby3_backward_compatibility/compatibility/string'


The URI module allows other libraries to register their own URI schemes. In the past, it was possible to do something like

module URI
  @schemes['MYSCHEME'] = MySchemeImplementation

In Ruby 3, you have to use

URI.register_scheme 'MYSCHEME', MySchemeImplementation

To add back the old behavior, use

require 'ruby3_backward_compatibility/compatibility/uri'

Things we cannot fix

In Ruby 2, it was possible to use without giving a block. In this case, the Proc took the block from the current context. A common usecase worked like this:

def call_me(block =

block = proc do
  puts "called"

call_me(block) # works
call_me(&block) # also works
call_me { puts "called" } # also works

This is no longer possible in Ruby 3 and we are not aware of a way to make a generic port for this.


After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and the created tag, and push the .gem file to


The gem is available as open source under the terms of the MIT License.