summaryrefslogtreecommitdiff
path: root/lib/rubygems/version.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rubygems/version.rb')
-rw-r--r--lib/rubygems/version.rb110
1 files changed, 64 insertions, 46 deletions
diff --git a/lib/rubygems/version.rb b/lib/rubygems/version.rb
index 20bbff4fdd..e174d8ad95 100644
--- a/lib/rubygems/version.rb
+++ b/lib/rubygems/version.rb
@@ -1,4 +1,7 @@
# frozen_string_literal: true
+
+require_relative "deprecate"
+
##
# The Version class processes string versions into comparable
# values. A version string should normally be a series of numbers
@@ -128,7 +131,7 @@
#
# == Preventing Version Catastrophe:
#
-# From: http://blog.zenspider.com/2008/10/rubygems-howto-preventing-cata.html
+# From: https://www.zenspider.com/ruby/2008/10/rubygems-how-to-preventing-catastrophe.html
#
# Let's say you're depending on the fnord gem version 2.y.z. If you
# specify your dependency as ">= 2.0.0" then, you're good, right? What
@@ -150,31 +153,27 @@
# a zero to give a sensible result.
class Gem::Version
- autoload :Requirement, File.expand_path('requirement', __dir__)
-
include Comparable
- VERSION_PATTERN = '[0-9]+(?>\.[0-9a-zA-Z]+)*(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?'.freeze # :nodoc:
- ANCHORED_VERSION_PATTERN = /\A\s*(#{VERSION_PATTERN})?\s*\z/.freeze # :nodoc:
+ VERSION_PATTERN = '[0-9]+(?>\.[0-9a-zA-Z]+)*(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?' # :nodoc:
+ ANCHORED_VERSION_PATTERN = /\A\s*(#{VERSION_PATTERN})?\s*\z/ # :nodoc:
##
# A string representation of this Version.
def version
- @version.dup
+ @version
end
- alias to_s version
+ alias_method :to_s, :version
##
# True if the +version+ string matches RubyGems' requirements.
def self.correct?(version)
- unless Gem::Deprecate.skip
- warn "nil versions are discouraged and will be deprecated in Rubygems 4" if version.nil?
- end
+ nil_versions_are_discouraged! if version.nil?
- !!(version.to_s =~ ANCHORED_VERSION_PATTERN)
+ ANCHORED_VERSION_PATTERN.match?(version.to_s)
end
##
@@ -189,6 +188,8 @@ class Gem::Version
if self === input # check yourself before you wreck yourself
input
elsif input.nil?
+ nil_versions_are_discouraged!
+
nil
else
new input
@@ -200,11 +201,19 @@ class Gem::Version
@@release = {}
def self.new(version) # :nodoc:
- return super unless Gem::Version == self
+ return super unless self == Gem::Version
@@all[version] ||= super
end
+ def self.nil_versions_are_discouraged!
+ unless Gem::Deprecate.skip
+ warn "nil versions are discouraged and will be deprecated in Rubygems 4"
+ end
+ end
+
+ private_class_method :nil_versions_are_discouraged!
+
##
# Constructs a Version from the +version+ string. A version string is a
# series of digits or ASCII letters separated by dots.
@@ -215,9 +224,17 @@ class Gem::Version
end
# If version is an empty string convert it to 0
- version = 0 if version.is_a?(String) && version =~ /\A\s*\Z/
+ version = 0 if version.is_a?(String) && /\A\s*\Z/.match?(version)
- @version = version.to_s.strip.gsub("-",".pre.")
+ @version = version.to_s
+
+ # optimization to avoid allocation when given an integer, since we know
+ # it's to_s won't have any spaces or dashes
+ unless version.is_a?(Integer)
+ @version = @version.strip
+ @version.gsub!("-",".pre.")
+ end
+ @version = -@version
@segments = nil
end
@@ -243,7 +260,7 @@ class Gem::Version
# same precision. Version "1.0" is not the same as version "1".
def eql?(other)
- self.class === other and @version == other._version
+ self.class === other && @version == other.version
end
def hash # :nodoc:
@@ -263,7 +280,7 @@ class Gem::Version
# string for backwards (RubyGems 1.3.5 and earlier) compatibility.
def marshal_dump
- [version]
+ [@version]
end
##
@@ -275,7 +292,7 @@ class Gem::Version
end
def yaml_initialize(tag, map) # :nodoc:
- @version = map['version']
+ @version = -map["version"]
@segments = nil
@hash = nil
end
@@ -285,7 +302,7 @@ class Gem::Version
end
def encode_with(coder) # :nodoc:
- coder.add 'version', @version
+ coder.add "version", @version
end
##
@@ -293,7 +310,7 @@ class Gem::Version
def prerelease?
unless instance_variable_defined? :@prerelease
- @prerelease = !!(@version =~ /[a-zA-Z]/)
+ @prerelease = /[a-zA-Z]/.match?(version)
end
@prerelease
end
@@ -308,12 +325,12 @@ class Gem::Version
def release
@@release[self] ||= if prerelease?
- segments = self.segments
- segments.pop while segments.any? {|s| String === s }
- self.class.new segments.join('.')
- else
- self
- end
+ segments = self.segments
+ segments.pop while segments.any? {|s| String === s }
+ self.class.new segments.join(".")
+ else
+ self
+ end
end
def segments # :nodoc:
@@ -339,11 +356,13 @@ class Gem::Version
# Compares this version with +other+ returning -1, 0, or 1 if the
# other version is larger, the same, or smaller than this
# one. Attempts to compare to something that's not a
- # <tt>Gem::Version</tt> return +nil+.
+ # <tt>Gem::Version</tt> or a valid version String return +nil+.
def <=>(other)
+ return self <=> self.class.new(other) if (String === other) && self.class.correct?(other)
+
return unless Gem::Version === other
- return 0 if @version == other._version || canonical_segments == other.canonical_segments
+ return 0 if @version == other.version || canonical_segments == other.canonical_segments
lhsegments = canonical_segments
rhsegments = other.canonical_segments
@@ -355,7 +374,8 @@ class Gem::Version
i = 0
while i <= limit
- lhs, rhs = lhsegments[i] || 0, rhsegments[i] || 0
+ lhs = lhsegments[i] || 0
+ rhs = rhsegments[i] || 0
i += 1
next if lhs == rhs
@@ -365,42 +385,40 @@ class Gem::Version
return lhs <=> rhs
end
- return 0
+ 0
end
+ # remove trailing zeros segments before first letter or at the end of the version
def canonical_segments
- @canonical_segments ||=
- _split_segments.map! do |segments|
- segments.reverse_each.drop_while {|s| s == 0 }.reverse
- end.reduce(&:concat)
+ @canonical_segments ||= begin
+ # remove trailing 0 segments, using dot or letter as anchor
+ # may leave a trailing dot which will be ignored by partition_segments
+ canonical_version = @version.sub(/(?<=[a-zA-Z.])[.0]+\z/, "")
+ # remove 0 segments before the first letter in a prerelease version
+ canonical_version.sub!(/(?<=\.|\A)[0.]+(?=[a-zA-Z])/, "") if prerelease?
+ partition_segments(canonical_version)
+ end
end
def freeze
prerelease?
+ _segments
canonical_segments
super
end
protected
- def _version
- @version
- end
-
def _segments
# segments is lazy so it can pick up version values that come from
# old marshaled versions, which don't go through marshal_load.
# since this version object is cached in @@all, its @segments should be frozen
-
- @segments ||= @version.scan(/[0-9]+|[a-z]+/i).map do |s|
- /^\d+$/ =~ s ? s.to_i : s
- end.freeze
+ @segments ||= partition_segments(@version)
end
- def _split_segments
- string_start = _segments.index {|s| s.is_a?(String) }
- string_segments = segments
- numeric_segments = string_segments.slice!(0, string_start || string_segments.size)
- return numeric_segments, string_segments
+ def partition_segments(ver)
+ ver.scan(/\d+|[a-z]+/i).map! do |s|
+ /\A\d/.match?(s) ? s.to_i : -s
+ end.freeze
end
end