# frozen_string_literal: true### 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. If a prerelease part contains both# letters and numbers, it will be broken into multiple parts to# provide expected sort behavior (1.0.a10 becomes 1.0.a.10, and is# greater than 1.0.a9).## Prereleases sort between real releases (newest to oldest):## 1. 1.0# 2. 1.0.b1# 3. 1.0.a.2# 4. 0.9## If you want to specify a version restriction that includes both prereleases# and regular releases of the 1.x series this is the best way:## s.add_dependency 'example', '>= 1.0.0.a', '< 2.0.0'## == How Software Changes## Users expect to be able to specify a version constraint that gives them# some reasonable expectation that new versions of a library will work with# their software if the version constraint is true, and not work with their# software if the version constraint is false. In other words, the perfect# system will accept all compatible versions of the library and reject all# incompatible versions.## Libraries change in 3 ways (well, more than 3, but stay focused here!).## 1. The change may be an implementation detail only and have no effect on# the client software.# 2. The change may add new features, but do so in a way that client software# written to an earlier version is still compatible.# 3. The change may change the public interface of the library in such a way# that old software is no longer compatible.## Some examples are appropriate at this point. Suppose I have a Stack class# that supports a <tt>push</tt> and a <tt>pop</tt> method.## === Examples of Category 1 changes:## * Switch from an array based implementation to a linked-list based# implementation.# * Provide an automatic (and transparent) backing store for large stacks.## === Examples of Category 2 changes might be:## * Add a <tt>depth</tt> method to return the current depth of the stack.# * Add a <tt>top</tt> method that returns the current top of stack (without# changing the stack).# * Change <tt>push</tt> so that it returns the item pushed (previously it# had no usable return value).## === Examples of Category 3 changes might be:## * Changes <tt>pop</tt> so that it no longer returns a value (you must use# <tt>top</tt> to get the top of the stack).# * Rename the methods to <tt>push_item</tt> and <tt>pop_item</tt>.## == RubyGems Rational Versioning## * Versions shall be represented by three non-negative integers, separated# by periods (e.g. 3.1.4). The first integers is the "major" version# number, the second integer is the "minor" version number, and the third# integer is the "build" number.## * A category 1 change (implementation detail) will increment the build# number.## * A category 2 change (backwards compatible) will increment the minor# version number and reset the build number.## * A category 3 change (incompatible) will increment the major build number# and reset the minor and build numbers.## * Any "public" release of a gem should have a different version. Normally# that means incrementing the build number. This means a developer can# generate builds all day long, but as soon as they make a public release,# the version must be updated.## === Examples## Let's work through a project lifecycle using our Stack example from above.## Version 0.0.1:: The initial Stack class is release.# Version 0.0.2:: Switched to a linked=list implementation because it is# cooler.# Version 0.1.0:: Added a <tt>depth</tt> method.# Version 1.0.0:: Added <tt>top</tt> and made <tt>pop</tt> return nil# (<tt>pop</tt> used to return the old top item).# Version 1.1.0:: <tt>push</tt> now returns the value pushed (it used it# return nil).# Version 1.1.1:: Fixed a bug in the linked list implementation.# Version 1.1.2:: Fixed a bug introduced in the last fix.## Client A needs a stack with basic push/pop capability. They write to the# original interface (no <tt>top</tt>), so their version constraint looks like:## gem 'stack', '>= 0.0'## Essentially, any version is OK with Client A. An incompatible change to# the library will cause them grief, but they are willing to take the chance# (we call Client A optimistic).## Client B is just like Client A except for two things: (1) They use the# <tt>depth</tt> method and (2) they are worried about future# incompatibilities, so they write their version constraint like this:## gem 'stack', '~> 0.1'## The <tt>depth</tt> method was introduced in version 0.1.0, so that version# or anything later is fine, as long as the version stays below version 1.0# where incompatibilities are introduced. We call Client B pessimistic# because they are worried about incompatible future changes (it is OK to be# pessimistic!).## == Preventing Version Catastrophe:## From: http://blog.zenspider.com/2008/10/rubygems-howto-preventing-cata.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# happens if fnord 3.0 comes out and it isn't backwards compatible# with 2.y.z? Your stuff will break as a result of using ">=". The# better route is to specify your dependency with an "approximate" version# specifier ("~>"). They're a tad confusing, so here is how the dependency# specifiers work:## Specification From ... To (exclusive)# ">= 3.0" 3.0 ... ∞# "~> 3.0" 3.0 ... 4.0# "~> 3.0.0" 3.0.0 ... 3.1# "~> 3.5" 3.5 ... 4.0# "~> 3.5.0" 3.5.0 ... 3.6# "~> 3" 3.0 ... 4.0## For the last example, single-digit versions are automatically extended with# a zero to give a sensible result.classGem::Versionautoload:Requirement,'rubygems/requirement'includeComparableVERSION_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.defversion@version.dupendaliasto_sversion### True if the +version+ string matches RubyGems' requirements.defself.correct?versionversion.to_s=~ANCHORED_VERSION_PATTERNend### 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) # -> nildefself.createinputifself===inputthen# check yourself before you wreck yourselfinputelsifinput.nil?thennilelsenewinputendend@@all={}defself.newversion# :nodoc:returnsuperunlessGem::Version==self@@all[version]||=superend### Constructs a Version from the +version+ string. A version string is a# series of digits or ASCII letters separated by dots.definitializeversionraiseArgumentError,"Malformed version number string #{version}"unlessself.class.correct?(version)@version=version.to_s.strip.gsub("-",".pre.")@segments=nilend### 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, e.g, 5.3.1.b.2 => 5.4, are ignored.defbump@bump||=beginsegments=self.segmentssegments.popwhilesegments.any?{|s|String===s}segments.popifsegments.size>1segments[-1]=segments[-1].succself.class.newsegments.join(".")endend### A Version is only eql? to another version if it's specified to the# same precision. Version "1.0" is not the same as version "1".defeql?otherself.class===otherand@version==other._versionenddefhash# :nodoc:@version.hashenddefinit_withcoder# :nodoc:yaml_initializecoder.tag,coder.mapenddefinspect# :nodoc:"#<#{self.class}#{version.inspect}>"end### Dump only the raw version string, not the complete object. It's a# string for backwards (RubyGems 1.3.5 and earlier) compatibility.defmarshal_dump[version]end### Load custom marshal format. It's a string for backwards (RubyGems# 1.3.5 and earlier) compatibility.defmarshal_loadarrayinitializearray[0]enddefyaml_initialize(tag,map)# :nodoc:@version=map['version']@segments=nil@hash=nilenddefto_yaml_properties# :nodoc:["@version"]enddefencode_withcoder# :nodoc:coder.add'version',@versionend### A version is considered a prerelease if it contains a letter.defprerelease?unlessinstance_variable_defined?:@prerelease@prerelease=!!(@version=~/[a-zA-Z]/)end@prereleaseenddefpretty_printq# :nodoc:q.text"Gem::Version.new(#{version.inspect})"end### The release for this version (e.g. 1.2.0.a -> 1.2.0).# Non-prerelease versions return themselves.defrelease@release||=ifprerelease?segments=self.segmentssegments.popwhilesegments.any?{|s|String===s}self.class.newsegments.join('.')elseselfendenddefsegments# :nodoc:_segments.dupend### A recommended version for use with a ~> Requirement.defapproximate_recommendationsegments=self.segmentssegments.popwhilesegments.any?{|s|String===s}segments.popwhilesegments.size>2segments.push0whilesegments.size<2"~> #{segments.join(".")}"end### 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+.def<=>otherreturnunlessGem::Version===otherreturn0if@version==other._versionlhsegments=_segmentsrhsegments=other._segmentslhsize=lhsegments.sizerhsize=rhsegments.sizelimit=(lhsize>rhsize?lhsize:rhsize)-1i=0whilei<=limitlhs,rhs=lhsegments[i]||0,rhsegments[i]||0i+=1nextiflhs==rhsreturn-1ifString===lhs&&Numeric===rhsreturn1ifNumeric===lhs&&String===rhsreturnlhs<=>rhsendreturn0endprotecteddef_version@versionenddef_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).mapdo|s|/^\d+$/=~s?s.to_i:send.freezeendend