Skip to content
Snippets Groups Projects
Commit c924de70 authored by Jack Nagel's avatar Jack Nagel
Browse files

Version: build-in devel version comparisons


The heuristic used by the default version comparison is simple. A
version string is scanned for strings of digits, split into an array of
these strings, and then an element-wise comparison is done.

This fails when presented with something like

  Version.new("1.0.0beta7") <=> Version.new("1.0.0")

because the first three digits match, and the fourth digit of the
receiver (7) is greater than the assumed fourth digit of the parameter
(0).

Fix this by defining an element-wise comparator on a new VersionElement
class. This allows us to correctly compare "alpha", "beta", and "rc"
style version strings, and keeps the logic out of the main version
comparison.

Signed-off-by: default avatarJack Nagel <jacknagel@gmail.com>
parent a119ee71
No related branches found
No related tags found
No related merge requests found
......@@ -23,6 +23,12 @@ class VersionComparisonTests < Test::Unit::TestCase
assert_version_comparison 'HEAD', '==', 'HEAD'
assert_version_comparison 'HEAD', '>', '1.2.3'
assert_version_comparison '1.2.3', '<', 'HEAD'
assert_version_comparison '3.2.0b4', '<', '3.2.0'
assert_version_comparison '1.0beta6', '<', '1.0b7'
assert_version_comparison '1.0b6', '<', '1.0beta7'
assert_version_comparison '1.1alpha4', '<', '1.1beta2'
assert_version_comparison '1.1beta2', '<', '1.1rc1'
assert_nil Version.new('1.0') <=> 'foo'
end
def test_macos_version_comparison
......
class VersionElement
include Comparable
attr_reader :elem
def initialize elem
elem = elem.to_s.downcase
@elem = case elem
when 'a', 'alpha' then 'alpha'
when 'b', 'beta' then 'beta'
when /\d+/ then elem.to_i
else elem
end
end
def <=>(other)
return unless other.is_a? VersionElement
return -1 if string? and other.numeric?
return 1 if numeric? and other.string?
return elem <=> other.elem
end
def to_s
@elem.to_s
end
def string?
@elem.is_a? String
end
def numeric?
@elem.is_a? Numeric
end
end
class Version
include Comparable
......@@ -11,30 +46,47 @@ class Version
@detected_from_url
end
def to_a
@array ||= @version.scan(/\d+|[a-zA-Z]+/).map { |e| VersionElement.new(e) }
end
def head?
@version == 'HEAD'
end
def nums
@version.scan(/\d+/).map { |d| d.to_i }
def devel?
alpha? or beta? or rc?
end
def alpha?
to_a.any? { |e| e.to_s == 'alpha' }
end
def beta?
to_a.any? { |e| e.to_s == 'beta' }
end
def rc?
to_a.any? { |e| e.to_s == 'rc' }
end
def <=>(other)
return nil unless other.is_a? Version
return 0 if self.head? and other.head?
return 1 if self.head? and not other.head?
return -1 if not self.head? and other.head?
return 1 if other.nil?
# Return nil if objects aren't comparable
return unless other.is_a? Version
# Versions are equal if both are HEAD
return 0 if head? and other.head?
# HEAD is greater than any numerical version
return 1 if head? and not other.head?
return -1 if not head? and other.head?
snums = self.nums
onums = other.nums
stuple, otuple = to_a, other.to_a
count = [snums.length, onums.length].max
max = [stuple.length, otuple.length].max
snums.fill(0, snums.length, count - snums.length)
onums.fill(0, onums.length, count - onums.length)
stuple.fill(VersionElement.new(0), stuple.length, max - stuple.length)
otuple.fill(VersionElement.new(0), otuple.length, max - otuple.length)
snums <=> onums
stuple <=> otuple
end
def to_s
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment