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: Jack Nagel <jacknagel@gmail.com>
This commit is contained in:
parent
a119ee718d
commit
c924de7086
@ -23,6 +23,12 @@ class VersionComparisonTests < Test::Unit::TestCase
|
|||||||
assert_version_comparison 'HEAD', '==', 'HEAD'
|
assert_version_comparison 'HEAD', '==', 'HEAD'
|
||||||
assert_version_comparison 'HEAD', '>', '1.2.3'
|
assert_version_comparison 'HEAD', '>', '1.2.3'
|
||||||
assert_version_comparison '1.2.3', '<', 'HEAD'
|
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
|
end
|
||||||
|
|
||||||
def test_macos_version_comparison
|
def test_macos_version_comparison
|
||||||
|
@ -1,3 +1,38 @@
|
|||||||
|
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
|
class Version
|
||||||
include Comparable
|
include Comparable
|
||||||
|
|
||||||
@ -11,30 +46,47 @@ class Version
|
|||||||
@detected_from_url
|
@detected_from_url
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def to_a
|
||||||
|
@array ||= @version.scan(/\d+|[a-zA-Z]+/).map { |e| VersionElement.new(e) }
|
||||||
|
end
|
||||||
|
|
||||||
def head?
|
def head?
|
||||||
@version == 'HEAD'
|
@version == 'HEAD'
|
||||||
end
|
end
|
||||||
|
|
||||||
def nums
|
def devel?
|
||||||
@version.scan(/\d+/).map { |d| d.to_i }
|
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
|
end
|
||||||
|
|
||||||
def <=>(other)
|
def <=>(other)
|
||||||
return nil unless other.is_a? Version
|
# Return nil if objects aren't comparable
|
||||||
return 0 if self.head? and other.head?
|
return unless other.is_a? Version
|
||||||
return 1 if self.head? and not other.head?
|
# Versions are equal if both are HEAD
|
||||||
return -1 if not self.head? and other.head?
|
return 0 if head? and other.head?
|
||||||
return 1 if other.nil?
|
# 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
|
stuple, otuple = to_a, other.to_a
|
||||||
onums = other.nums
|
|
||||||
|
|
||||||
count = [snums.length, onums.length].max
|
max = [stuple.length, otuple.length].max
|
||||||
|
|
||||||
snums.fill(0, snums.length, count - snums.length)
|
stuple.fill(VersionElement.new(0), stuple.length, max - stuple.length)
|
||||||
onums.fill(0, onums.length, count - onums.length)
|
otuple.fill(VersionElement.new(0), otuple.length, max - otuple.length)
|
||||||
|
|
||||||
snums <=> onums
|
stuple <=> otuple
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_s
|
def to_s
|
||||||
|
Loading…
x
Reference in New Issue
Block a user