Daniel Doubrovkine bio photo

Daniel Doubrovkine

aka dB., @awscloud, former CTO @artsy, +@vestris, NYC

Email Twitter LinkedIn Github Strava
Creative Commons License

Time and again, and again, developers fail to compare gem or ruby version numbers correctly. It’s an easy mistake to make.

> RUBY_VERSION
 => "2.6.6"
> RUBY_VERSION > '2.7.7'
 => false # ok
> RUBY_VERSION > '2.6.5'
 => true # ok
> RUBY_VERSION > '2.10.0'
 => true # ouch

Using to_f is another common mistake.

> RUBY_VERSION
 => "2.10.0"
> RUBY_VERSION.to_f < 2.7
 => false # ouch
> RUBY_VERSION.to_f
 => 2.1

There’s a great post that comes up on top of Google that recommends using Gem::Version.

> Gem::Version.new(RUBY_VERSION)
 => #<Gem::Version "2.6.6">
> Gem::Version.new(RUBY_VERSION) > Gem::Version.new('2.7.7')
 => false # ok
> Gem::Version.new(RUBY_VERSION) > Gem::Version.new('2.6.5')
 => true # ok
 > Gem::Version.new(RUBY_VERSION) > Gem::Version.new('2.10.0')
 => false # ok

This works correctly, including for pre-release versions.

> Gem::Version.new(RUBY_VERSION) > Gem::Version.new('2.6.6.pre1')
 => true

However, especially in this last case it may not be what you want. In fact, it’s almost never what you want. Consider the following example with RUBY_VERSION = 2.6.6.pre1.

if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.6.6')
  # this code works with any 2.6.6, including 2.6.6.pre1
else
  # this code works with 2.6.5 or older
end

This doesn’t work because “2.6.6.pre1” is not newer than “2.6.6”.

> Gem::Version.new('2.6.6.pre1') >= Gem::Version.new('2.6.6')
 => false
 

The desired effect can be achieved easily.

if Gem::Version.new(RUBY_VERSION).release >= Gem::Version.new('2.6.6')
  # this code works with any 2.6.6, including 2.6.6.pre1
else
  # this code works with 2.6.5 or older
end

Another common pattern that I have observed is to check versions at runtime.

def do_something
  if Gem::Version.new(RUBY_VERSION).release >= Gem::Version.new('2.6.6')
    # this code works with any 2.6.6, including 2.6.6.pre1
  else
    # this code works with 2.6.5 or older
  end
end

Avoid evaluating this if multiple times by declaring different methods depending on the Ruby version.

if Gem::Version.new(RUBY_VERSION).release >= Gem::Version.new('2.6.6')
  def do_something
    # this code works with any 2.6.6, including 2.6.6.pre1
  end
else
  def do_something
    # this code works with 2.6.5 or older
  end
end

We can also hide this implementation detail in some cases using the with-version gem. Consider contributing to support more if-then-else scenarios.

with_minimum_ruby '2.6.6' do
  # this code works with 2.6.6 or newer, including 2.6.6.pre1
end