2007-11-10 02:48:56 -05:00
|
|
|
#--
|
|
|
|
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
|
|
|
|
# All rights reserved.
|
|
|
|
# See LICENSE.txt for permissions.
|
|
|
|
#++
|
|
|
|
|
|
|
|
##
|
2009-06-09 17:38:59 -04:00
|
|
|
# The Version class processes string versions into comparable
|
|
|
|
# values. A version string should normally be a series of numbers
|
|
|
|
# separated by periods. Each part (digits separated by periods) is
|
|
|
|
# considered its own number, and these are used for sorting. So for
|
|
|
|
# instance, 3.10 sorts higher than 3.2 because ten is greater than
|
|
|
|
# two.
|
|
|
|
#
|
|
|
|
# If any part contains letters (currently only a-z are supported) then
|
|
|
|
# that version is considered prerelease. Versions with a prerelease
|
|
|
|
# part in the Nth part sort less than versions with N-1 parts. Prerelease
|
|
|
|
# parts are sorted alphabetically using the normal Ruby string sorting
|
|
|
|
# rules.
|
|
|
|
#
|
|
|
|
# Prereleases sort between real releases (newest to oldest):
|
|
|
|
#
|
|
|
|
# 1. 1.0
|
|
|
|
# 2. 1.0.b
|
|
|
|
# 3. 1.0.a
|
|
|
|
# 4. 0.9
|
2008-06-17 18:04:18 -04:00
|
|
|
|
2007-11-10 02:48:56 -05:00
|
|
|
class Gem::Version
|
|
|
|
|
2009-06-09 17:38:59 -04:00
|
|
|
class Part
|
|
|
|
include Comparable
|
|
|
|
|
|
|
|
attr_reader :value
|
|
|
|
|
|
|
|
def initialize(value)
|
|
|
|
@value = (value =~ /\A\d+\z/) ? value.to_i : value
|
|
|
|
end
|
|
|
|
|
|
|
|
def to_s
|
|
|
|
self.value.to_s
|
|
|
|
end
|
|
|
|
|
|
|
|
def inspect
|
|
|
|
@value
|
|
|
|
end
|
|
|
|
|
|
|
|
def alpha?
|
|
|
|
String === value
|
|
|
|
end
|
|
|
|
|
|
|
|
def numeric?
|
|
|
|
Fixnum === value
|
|
|
|
end
|
|
|
|
|
|
|
|
def <=>(other)
|
|
|
|
if self.numeric? && other.alpha? then
|
|
|
|
1
|
|
|
|
elsif self.alpha? && other.numeric? then
|
|
|
|
-1
|
|
|
|
else
|
|
|
|
self.value <=> other.value
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def succ
|
|
|
|
self.class.new(self.value.succ)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2007-11-10 02:48:56 -05:00
|
|
|
include Comparable
|
|
|
|
|
2009-06-09 17:38:59 -04:00
|
|
|
VERSION_PATTERN = '[0-9]+(\.[0-9a-z]+)*'
|
2007-11-10 02:48:56 -05:00
|
|
|
|
|
|
|
attr_reader :version
|
|
|
|
|
|
|
|
def self.correct?(version)
|
2009-06-09 17:38:59 -04:00
|
|
|
pattern = /\A\s*(#{VERSION_PATTERN})*\s*\z/
|
|
|
|
|
|
|
|
version.is_a? Integer or
|
|
|
|
version =~ pattern or
|
|
|
|
version.to_s =~ pattern
|
2007-11-10 02:48:56 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
##
|
|
|
|
# Factory method to create a Version object. Input may be a Version or a
|
|
|
|
# String. Intended to simplify client code.
|
|
|
|
#
|
|
|
|
# ver1 = Version.create('1.3.17') # -> (Version object)
|
|
|
|
# ver2 = Version.create(ver1) # -> (ver1)
|
|
|
|
# ver3 = Version.create(nil) # -> nil
|
2008-06-17 18:04:18 -04:00
|
|
|
|
2007-11-10 02:48:56 -05:00
|
|
|
def self.create(input)
|
|
|
|
if input.respond_to? :version then
|
|
|
|
input
|
|
|
|
elsif input.nil? then
|
|
|
|
nil
|
|
|
|
else
|
|
|
|
new input
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
##
|
2008-06-17 18:04:18 -04:00
|
|
|
# Constructs a Version from the +version+ string. A version string is a
|
2009-06-09 17:38:59 -04:00
|
|
|
# series of digits or ASCII letters separated by dots.
|
2008-06-17 18:04:18 -04:00
|
|
|
|
2007-11-10 02:48:56 -05:00
|
|
|
def initialize(version)
|
|
|
|
raise ArgumentError, "Malformed version number string #{version}" unless
|
|
|
|
self.class.correct?(version)
|
|
|
|
|
|
|
|
self.version = version
|
|
|
|
end
|
|
|
|
|
|
|
|
def inspect # :nodoc:
|
|
|
|
"#<#{self.class} #{@version.inspect}>"
|
|
|
|
end
|
|
|
|
|
2009-06-09 17:38:59 -04:00
|
|
|
##
|
2007-11-10 02:48:56 -05:00
|
|
|
# Dump only the raw version string, not the complete object
|
2009-06-09 17:38:59 -04:00
|
|
|
|
2007-11-10 02:48:56 -05:00
|
|
|
def marshal_dump
|
|
|
|
[@version]
|
|
|
|
end
|
|
|
|
|
2009-06-09 17:38:59 -04:00
|
|
|
##
|
2007-11-10 02:48:56 -05:00
|
|
|
# Load custom marshal format
|
2009-06-09 17:38:59 -04:00
|
|
|
|
2007-11-10 02:48:56 -05:00
|
|
|
def marshal_load(array)
|
|
|
|
self.version = array[0]
|
|
|
|
end
|
|
|
|
|
2009-06-09 17:38:59 -04:00
|
|
|
def parts
|
|
|
|
@parts ||= normalize
|
|
|
|
end
|
|
|
|
|
2008-06-17 18:04:18 -04:00
|
|
|
##
|
2007-11-10 02:48:56 -05:00
|
|
|
# Strip ignored trailing zeros.
|
2008-06-17 18:04:18 -04:00
|
|
|
|
2007-11-10 02:48:56 -05:00
|
|
|
def normalize
|
2009-06-09 17:38:59 -04:00
|
|
|
parts_arr = parse_parts_from_version_string
|
|
|
|
if parts_arr.length != 1
|
|
|
|
parts_arr.pop while parts_arr.last && parts_arr.last.value == 0
|
|
|
|
parts_arr = [Part.new(0)] if parts_arr.empty?
|
|
|
|
end
|
|
|
|
parts_arr
|
2007-11-10 02:48:56 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
##
|
|
|
|
# Returns the text representation of the version
|
2009-06-09 17:38:59 -04:00
|
|
|
|
2007-11-10 02:48:56 -05:00
|
|
|
def to_s
|
|
|
|
@version
|
|
|
|
end
|
|
|
|
|
|
|
|
def to_yaml_properties
|
|
|
|
['@version']
|
|
|
|
end
|
|
|
|
|
|
|
|
def version=(version)
|
|
|
|
@version = version.to_s.strip
|
|
|
|
normalize
|
|
|
|
end
|
|
|
|
|
2009-06-09 17:38:59 -04:00
|
|
|
##
|
|
|
|
# A version is considered a prerelease if any part contains a letter.
|
|
|
|
|
|
|
|
def prerelease?
|
|
|
|
parts.any? { |part| part.alpha? }
|
|
|
|
end
|
2009-12-08 02:19:09 -05:00
|
|
|
|
2009-06-09 17:38:59 -04:00
|
|
|
##
|
|
|
|
# The release for this version (e.g. 1.2.0.a -> 1.2.0)
|
|
|
|
# Non-prerelease versions return themselves
|
|
|
|
def release
|
|
|
|
return self unless prerelease?
|
|
|
|
rel_parts = parts.dup
|
|
|
|
rel_parts.pop while rel_parts.any? { |part| part.alpha? }
|
|
|
|
self.class.new(rel_parts.join('.'))
|
|
|
|
end
|
|
|
|
|
2007-11-10 02:48:56 -05:00
|
|
|
def yaml_initialize(tag, values)
|
|
|
|
self.version = values['version']
|
|
|
|
end
|
|
|
|
|
|
|
|
##
|
2008-06-17 18:04:18 -04:00
|
|
|
# Compares this version with +other+ returning -1, 0, or 1 if the other
|
|
|
|
# version is larger, the same, or smaller than this one.
|
|
|
|
|
2007-11-10 02:48:56 -05:00
|
|
|
def <=>(other)
|
2008-06-17 18:04:18 -04:00
|
|
|
return nil unless self.class === other
|
2007-11-10 02:48:56 -05:00
|
|
|
return 1 unless other
|
2009-06-09 17:38:59 -04:00
|
|
|
mine, theirs = balance(self.parts.dup, other.parts.dup)
|
|
|
|
mine <=> theirs
|
|
|
|
end
|
|
|
|
|
|
|
|
def balance(a, b)
|
|
|
|
a << Part.new(0) while a.size < b.size
|
|
|
|
b << Part.new(0) while b.size < a.size
|
|
|
|
[a, b]
|
2007-11-10 02:48:56 -05:00
|
|
|
end
|
|
|
|
|
2008-06-17 18:04:18 -04:00
|
|
|
##
|
|
|
|
# A Version is only eql? to another version if it has the same version
|
|
|
|
# string. "1.0" is not the same version as "1".
|
|
|
|
|
|
|
|
def eql?(other)
|
|
|
|
self.class === other and @version == other.version
|
|
|
|
end
|
2007-12-20 03:39:12 -05:00
|
|
|
|
|
|
|
def hash # :nodoc:
|
2008-06-17 18:04:18 -04:00
|
|
|
@version.hash
|
2007-11-10 02:48:56 -05:00
|
|
|
end
|
|
|
|
|
2009-06-09 17:38:59 -04:00
|
|
|
##
|
|
|
|
# Return a new version object where the next to the last revision number is
|
|
|
|
# one greater. (e.g. 5.3.1 => 5.4)
|
|
|
|
#
|
|
|
|
# Pre-release (alpha) parts are ignored. (e.g 5.3.1.b2 => 5.4)
|
|
|
|
|
2007-11-10 02:48:56 -05:00
|
|
|
def bump
|
2009-06-09 17:38:59 -04:00
|
|
|
parts = parse_parts_from_version_string
|
|
|
|
parts.pop while parts.any? { |part| part.alpha? }
|
|
|
|
parts.pop if parts.size > 1
|
|
|
|
parts[-1] = parts[-1].succ
|
|
|
|
self.class.new(parts.join("."))
|
2007-11-10 02:48:56 -05:00
|
|
|
end
|
|
|
|
|
2009-06-09 17:38:59 -04:00
|
|
|
def parse_parts_from_version_string # :nodoc:
|
|
|
|
@version.to_s.scan(/[0-9a-z]+/i).map { |s| Part.new(s) }
|
|
|
|
end
|
|
|
|
|
|
|
|
def pretty_print(q) # :nodoc:
|
|
|
|
q.text "Gem::Version.new(#{@version.inspect})"
|
2007-12-20 03:39:12 -05:00
|
|
|
end
|
|
|
|
|
2007-11-10 02:48:56 -05:00
|
|
|
#:stopdoc:
|
|
|
|
|
|
|
|
require 'rubygems/requirement'
|
|
|
|
|
2009-06-09 17:38:59 -04:00
|
|
|
##
|
2007-11-10 02:48:56 -05:00
|
|
|
# Gem::Requirement's original definition is nested in Version.
|
|
|
|
# Although an inappropriate place, current gems specs reference the nested
|
|
|
|
# class name explicitly. To remain compatible with old software loading
|
|
|
|
# gemspecs, we leave a copy of original definition in Version, but define an
|
|
|
|
# alias Gem::Requirement for use everywhere else.
|
|
|
|
|
|
|
|
Requirement = ::Gem::Requirement
|
|
|
|
|
|
|
|
# :startdoc:
|
|
|
|
|
|
|
|
end
|
|
|
|
|