class Gem::Version

The Version 类将字符串版本处理成可比较的值。版本字符串通常应为由句点分隔的数字序列。每个部分(由句点分隔的数字)都被视为自己的数字,用于排序。例如,3.10 的排序高于 3.2,因为十大于二。

如果任何部分包含字母(目前仅支持 a-z),则该版本被视为预发布版本。在第 N 部分包含预发布部分的版本排序低于在第 N-1 部分包含预发布部分的版本。预发布部分使用标准的 Ruby 字符串排序规则按字母顺序排序。如果预发布部分同时包含字母和数字,它将被拆分成多个部分,以提供预期的排序行为(1.0.a10 变为 1.0.a.10,且大于 1.0.a9)。

预发布版本在正式版本之间排序(从新到旧)

  1. 1.0

  2. 1.0.b1

  3. 1.0.a.2

  4. 0.9

如果你想指定一个版本约束,其中包含 1.x 系列的预发布版本和正式版本,这是最好的方法。

s.add_dependency 'example', '>= 1.0.0.a', '< 2.0.0'

软件如何变化

用户期望能够指定一个版本约束,让他们对新版本的库能够与他们的软件正常工作(如果版本约束为真)以及不能正常工作(如果版本约束为假)有一个合理的预期。换句话说,完美系统将接受库的所有兼容版本,并拒绝所有不兼容的版本。

库会以 3 种方式(嗯,超过 3 种,但我们先集中讨论这 3 种!)发生变化。

  1. 变化可能仅是实现细节,对客户端软件没有影响。

  2. 变化可能添加新功能,但以一种早期版本编写的客户端软件仍然兼容的方式添加。

  3. 变化可能以一种旧软件不再兼容的方式改变库的公共接口。

此时给出一些示例是合适的。假设我有一个支持 pushpop 方法的 Stack 类。

Category 1 变化的示例

Category 2 变化的示例可能是

Category 3 变化的示例可能是

RubyGems Rational 版本控制

示例

让我们通过上面 Stack 示例的项目生命周期来工作。

Version 0.0.1

初始 Stack 类已发布。

Version 0.0.2

切换到链表实现,因为它更酷。

Version 0.1.0

添加了 depth 方法。

Version 1.0.0

添加了 top,并使 pop 返回 nil(pop 以前返回旧的顶部项)。

Version 1.1.0

push 现在返回被推送的值(它以前返回 nil)。

Version 1.1.1

修复了链表实现中的一个错误。

Version 1.1.2

修复了上一次修复引入的错误。

客户端 A 需要一个具有基本 push/pop 功能的堆栈。他们编写了原始接口(没有 top),因此他们的版本约束看起来像

gem 'stack', '>= 0.0'

基本上,对于客户端 A 来说,任何版本都可以。对库的不兼容更改会给他们带来麻烦,但他们愿意承担风险(我们称客户端 A 为乐观)。

客户端 B 与客户端 A 相同,除了两件事:(1)他们使用 depth 方法,(2)他们担心未来的不兼容性,因此他们编写的版本约束如下:

gem 'stack', '~> 0.1'

depth 方法是在版本 0.1.0 中引入的,因此该版本或之后的任何版本都可以,只要版本保持在 1.0 以下,不兼容性就会被引入。我们称客户端 B 为悲观主义者,因为他们担心未来的不兼容更改(悲观主义者是 OK 的!)。

防止 Version 灾难

来自:www.zenspider.com/ruby/2008/10/rubygems-how-to-preventing-catastrophe.html

假设你依赖 fnord gem 版本 2.y.z。如果你指定依赖项为“>= 2.0.0”,那么你就万事大吉了,对吗?如果 fnord 3.0 发布了,并且它与 2.y.z 不向后兼容,会发生什么?你的东西会因为使用“>=”而中断。更好的方法是使用“近似”版本说明符(“~>”)指定你的依赖项。它们有点令人困惑,所以依赖项说明符的工作方式如下:

Specification From  ... To (exclusive)
">= 3.0"      3.0   ... &infin;
"~> 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

对于最后一个示例,单位数版本会自动扩展一个零以给出合理的结果。