diff --git a/Library/Homebrew/test/test_versions.rb b/Library/Homebrew/test/test_versions.rb index 972ce130b4..9811db57ef 100644 --- a/Library/Homebrew/test/test_versions.rb +++ b/Library/Homebrew/test/test_versions.rb @@ -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 diff --git a/Library/Homebrew/version.rb b/Library/Homebrew/version.rb index 30e41392a2..01053012f4 100644 --- a/Library/Homebrew/version.rb +++ b/Library/Homebrew/version.rb @@ -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 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