Skip to content

Instantly share code, notes, and snippets.

@s-shin
Created February 24, 2017 10:55
Show Gist options
  • Save s-shin/58f930eb0f006eb4e0a25ec00e7fbead to your computer and use it in GitHub Desktop.
Save s-shin/58f930eb0f006eb4e0a25ec00e7fbead to your computer and use it in GitHub Desktop.
Simple class to handle SemVer (http://semver.org/).
class SemVer
include Comparable
attr_accessor :major, :minor, :patch, :prerelease, :build_metadata
@@re = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9A-Za-z-][0-9A-Za-z-]*))*))?(?:\+((?:0|[1-9A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9A-Za-z-][0-9A-Za-z-]*))*))?$/
def initialize(major, minor, patch, prerelease = "", build_metadata = "")
@major = major.to_i
@minor = minor.to_i
@patch = patch.to_i
@prerelease = prerelease.to_s
@build_metadata = build_metadata.to_s
end
def valid?
!(@@re !~ to_s)
end
def prerelease?
@prerelease && !@prerelease.empty?
end
def self.parse(str)
m = str.match(@@re)
return nil unless m
SemVer.new(*m[1..-1])
end
def to_s
s = "#{@major}.#{@minor}.#{@patch}"
s += "-#{@prerelease}" if prerelease?
s += "+#{@build_metadata}" if @build_metadata && !@build_metadata.empty?
s
end
def compare_major(other)
return 1 if @major > other.major
return -1 if @major < other.major
return 0
end
def compare_minor(other)
return 1 if @minor > other.minor
return -1 if @minor < other.minor
return 0
end
def compare_patch(other)
return 1 if @patch > other.patch
return -1 if @patch < other.patch
return 0
end
def compare_major_minor_patch(other)
c = compare_major(other)
return c if c != 0
c = compare_minor(other)
return c if c != 0
c = compare_patch(other)
return c if c != 0
return 0
end
def compare_prerelease(other)
return 1 if !prerelease? && other.prerelease?
return -1 if prerelease? && !other.prerelease?
ids = @prerelease.split('.')
other_ids = other.prerelease.split('.')
ids_size = ids.size
other_ids_size = other_ids.size
min_size = ids_size < other_ids_size ? ids_size : other_ids_size
for i in 0..min_size do
next if ids[i] == other_ids[i]
as_i = nil
begin
as_i = Integer(ids_[i])
rescue
end
other_as_i = nil
begin
other_as_i = Integer(other_ids[i])
rescue
end
return 1 if !as_i.nil? && other_as_i.nil?
return -1 if as_i.nil? && !other_as_i.nil?
return as_i <=> other_as_i if !as_i.nil? && !other_as_i.nil?
return ids[i] <=> other_ids[i]
end
return 1 if ids_size > other_ids_size
return -1 if ids_size < other_ids_size
return 0
end
def <=>(other)
c = compare_major_minor_patch(other)
return c if c != 0
c = compare_prerelease(other)
return c if c != 0
return 0
end
end
require 'test/unit'
$:.unshift File.dirname(__FILE__) + '/.'
require 'semver'
class TestSemVer < Test::Unit::TestCase
def test_parse
s = '0.1.2'
v = SemVer.parse(s)
assert_true(v.valid?)
assert_equal(0, v.major)
assert_equal(1, v.minor)
assert_equal(2, v.patch)
assert_equal(s, v.to_s)
s = '10.0.0-alpha.1.2+123'
v = SemVer.parse(s)
assert_true(v.valid?)
assert_equal(10, v.major)
assert_equal(0, v.minor)
assert_equal(0, v.patch)
assert_equal('alpha.1.2', v.prerelease)
assert_equal('123', v.build_metadata)
assert_equal(s, v.to_s)
[
'00.1.2',
'0.1.2-$12',
'0.1.2-alpha.12~3',
].each do |s|
assert_nil(SemVer.parse(s))
end
end
def test_compare
[
['0.1.2', :==, '0.1.2'],
['0.1.2', :>=, '0.1.2'],
['0.1.2', :<, '0.1.3'],
['0.1.2', :<, '1.0.0'],
['1.0.0', :>, '0.100.100'],
['1.0.0-alpha', :<, '1.0.0-alpha.1'],
['1.0.0-alpha.1', :<, '1.0.0-alpha.beta'],
['1.0.0-alpha.1', :<, '1.0.0-beta'],
['1.0.0-beta', :<, '1.0.0-beta.2'],
['1.0.0-beta.2', :<, '1.0.0-beta.11'],
['1.0.0-beta.11', :<, '1.0.0-rc.1'],
['1.0.0-rc.1', :<, '1.0.0'],
].each do |c|
assert_operator(SemVer.parse(c[0]), c[1], SemVer.parse(c[2]))
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment