Shorts. by Jeff Kreeftmeijer

Ruby: Compare version numbers with pessimistic constraints

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