Ruby

2つのバージョン番号文字列について「以上」「以下」「より大きい」「より小さい」「同じ」の比較判定をする

課題

アプリケーションのバージョン番号について、以上(「以下」「より大きい」「より小さい」「同じ」)を判定したい。

例えば、 1.3.21.2.3 より大きい(一般的には新しい)、1.3.22.3.2 より小さい(一般的には古い)というな判定をしたい。

ふわっとした仕様

バージョニングルールはソフトウェアによって色んなパターンがあるようです。ここではバージョンにあれこれ考えを巡らせてみる - Qiita の「数字によるバージョン」を参考に次の内容で対応することにします。

  • 同じアプリケーションの2つのバージョン番号同士について「以上」「以下」「より大きい」「より小さい」を判定できる
  • バージョン番号は . で区切られた文字列
    • 数字以外の文字列は無視する
  • メジャー・マイナーの2階層、加えてビルドとリビジョンの4階層だけでなく、複数階層パターンに対応する
  • 比較対象のバージョニングルールは階層の数を除いて途中で変更されないこと

バージョン番号の例

  • 1
  • 0.3
  • 0.0.1
  • My Software 0.0.1

シュッと実装

比較メソッドを実装する際に、Ruby の Comaprable モジュールを使います。

Comparable モジュールの比較演算子を定義する事で、通常の数値比較のように直感的なインターフェースになります(テストケースを参照)。

require 'test/unit'

class VersionComparator
  include Comparable

  attr_reader :splited_version

  def initialize(version)
    @splited_version = version.gsub(/[^\d.]+/, '').split('.').map(&:to_i)
  end

  def <=>(other)
    @splited_version <=> other.splited_version
  end
end

class TestVersionComparator < Test::Unit::TestCase
  def test_ufo
    # test <
    assert_equal true, VersionComparator.new('0.0.1') < VersionComparator.new('0.0.2')
    assert_equal false, VersionComparator.new('0.0.5') < VersionComparator.new('0.0.2')
    assert_equal true, VersionComparator.new('0.0.5') < VersionComparator.new('0.1.2')
    assert_equal false, VersionComparator.new('0.0.5') < VersionComparator.new('0.0.5')
    assert_equal false, VersionComparator.new('0.5') < VersionComparator.new('0.0.5')
    assert_equal true, VersionComparator.new('0.0.5') < VersionComparator.new('0.5')
    assert_equal true, VersionComparator.new('My Software 0.0.1') < VersionComparator.new('My Software 0.0.2')

    # test <=
    assert_equal true, VersionComparator.new('0.0.1') <= VersionComparator.new('0.0.2')
    assert_equal false, VersionComparator.new('0.0.5') <= VersionComparator.new('0.0.2')
    assert_equal true, VersionComparator.new('0.0.5') <= VersionComparator.new('0.1.2')
    assert_equal true, VersionComparator.new('0.0.5') <= VersionComparator.new('0.0.5')
    assert_equal false, VersionComparator.new('0.5') <= VersionComparator.new('0.0.5')
    assert_equal true, VersionComparator.new('0.0.5') <= VersionComparator.new('0.5')
    assert_equal true, VersionComparator.new('My Software 0.0.1') <= VersionComparator.new('My Software 0.0.2')

    # test >
    assert_equal false, VersionComparator.new('0.0.1') > VersionComparator.new('0.0.2')
    assert_equal true, VersionComparator.new('0.0.5') > VersionComparator.new('0.0.2')
    assert_equal false, VersionComparator.new('0.0.5') > VersionComparator.new('0.1.2')
    assert_equal false, VersionComparator.new('0.0.5') > VersionComparator.new('0.0.5')
    assert_equal true, VersionComparator.new('0.5') > VersionComparator.new('0.0.5')
    assert_equal false, VersionComparator.new('0.0.5') > VersionComparator.new('0.5')
    assert_equal false, VersionComparator.new('My Software 0.0.1') > VersionComparator.new('My Software 0.0.2')

    # test >=
    assert_equal false, VersionComparator.new('0.0.1') >= VersionComparator.new('0.0.2')
    assert_equal true, VersionComparator.new('0.0.5') >= VersionComparator.new('0.0.2')
    assert_equal false, VersionComparator.new('0.0.5') >= VersionComparator.new('0.1.2')
    assert_equal true, VersionComparator.new('0.0.5') >= VersionComparator.new('0.0.5')
    assert_equal true, VersionComparator.new('0.5') >= VersionComparator.new('0.0.5')
    assert_equal false, VersionComparator.new('0.0.5') >= VersionComparator.new('0.5')
    assert_equal false, VersionComparator.new('My Software 0.0.1') >= VersionComparator.new('My Software 0.0.2')

    # test ==
    assert_equal true, VersionComparator.new('0.0.1') == VersionComparator.new('0.0.1')
    assert_equal true, VersionComparator.new('My Software 0.0.2') == VersionComparator.new('My Software 0.0.2')
  end
end