Friday, February 8, 2013

Ruby Version Check

As good a practice as it is to make Ruby code portable between interpreter versions, sometimes it is just not possible. And other times, a particular feature (such as little- and big-endian flags in Array#pack and String#unpack) makes requiring a minimum version a small burden to bear.

There are a few underfeatured or overengineered solutions to this problem already, but that feels "just right" for a task that should be so simple. What follows are two brief one-liners for enforcing a minimum Ruby version.

Method 1 : A simple to_i check

This method is useful if the required version is in the format of the 1.8.x and 1.9.x releases. That is, the version number must be at least three single-digit decimal numbers (i.e. 0-9) separated by decimal points (or periods, if you prefer). The trick is simply to concatenate the decimal numbers and convert them to an integer:


This can be used in a straightforward check as follows:

raise "Ruby 1.9.3 required" if RUBY_VERSION.split('.')[0,3].join.to_i < 193

Method 2 : Padded-decimal to_i check

This method is useful if the version components can contain multiple-digit decimal numbers. For simplicity, assume that the largest version component will be 999. The trick then is to format a string in which each version component is zero-padded to three places, then converted to an integer:

("%03d%03d%03d" % RUBY_VERSION.split('.')[0,3]).to_i 

The required version number must also be zero-padded, as the following check demonstrates:

raise "Ruby 1.9.3 required" if ("%03d%03d%03d" % RUBY_VERSION.split('.')[0,3]).to_i < 1009003 


Of course, to be absolutely safe, one must consider the possibility of versions such as 3.0 or even 3. In these cases, the output of split('.') will cause a failure, as there are not three components available.

The fix is to append enough zeroes to the array after splitting.

A two-component version number requires a single zero to be appended:

irb> ("%03d%03d%03d" % (("1.8").split('.')<<0)).to_i
=> 1008000 

While a one-component version number requires two zeroes to be appended:

irb> ("%03d%03d%03d" % (("1").split('.')<<0<<0)).to_i
=> 1000000

The ultimate, maximally-futureproof version-string-to-integer comparison is therefore:

("%03d%03d%03d" % (RUBY_VERSION.split('.')<<0<<0)).to_i

The accompanying version check for Ruby 1.9.3:

raise "Ruby 1.9.3 required" if ("%03d%03d%03d" % (RUBY_VERSION.split('.')<<0<<0)).to_i < 1009003 

A bit ugly, and a bit more than an 80-character line, but still simple enough to chuck under the shebang.