To compare version numbers using pessimistic or approximate constraints (using ~>
) in Ruby, you could use RubyGems' Gem::Version
or do hacky things with Gem::Dependency
.
Instead, I decided to write a simple and understandable Version
class that does everything I need:
require 'version'
Version.new('1').matches?('~>', '1') # => true
Version.new('1.2').matches?('~>', '1.0') # => true
Version.new('1.2.3').matches?('~>', '1.0.0') # => false
Using Ruby's Comparable
module and a method that figures out the next incompatible version, implementing the =
, >
, <
and ~>
operators could be done in 40 lines of Ruby, without depending on RubyGems:
class Version
include Comparable
attr_reader :major, :minor, :patch
def initialize(number)
@major, @minor, @patch = number.split('.').map(&:to_i)
end
def to_a
[major, minor, patch].compact
end
def <=>(version)
(major.to_i <=> version.major.to_i).nonzero? ||
(minor.to_i <=> version.minor.to_i).nonzero? ||
patch.to_i <=> version.patch.to_i
end
def matches?(operator, number)
version = Version.new(number)
self == version
return self == version if operator == '='
return self > version if operator == '>'
return self < version if operator == '<'
return version <= self && version.next > self if operator == '~>'
end
def next
next_splits = to_a
if next_splits.length == 1
next_splits[0] += 1
else
next_splits[-2] += 1
next_splits[-1] = 0
end
Version.new(next_splits.join('.'))
end
end