diff options
Diffstat (limited to 'lib/rubygems/version.rb')
-rw-r--r-- | lib/rubygems/version.rb | 110 |
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 |