项目可能需要与默认 Ruby 不同的 Ruby 版本。即使它需要相同版本的 Ruby,它也可能需要不同版本的 RubyGem。

这个问题并不是 Ruby 独有的。所有语言都会出现依赖性问题。用于处理困境的技术因每种语言而异。在 Ruby 中,大多数开发人员使用 Ruby 版本管理器(例如 RVM 或 rbenv)来管理多个 Ruby 版本。你还可以使用版本管理器来管理 Gem 依赖项,但更受欢迎的方法是使用依赖项管理器。

到目前为止,Ruby 社区中使用最广泛的依赖项管理器是 Bundler Gem。这个 Gem 允许你配置每个项目需要哪些 Ruby 和哪些 Gems。

安装 Bundler

如果你使用的是 Ruby 2.5 或更高版本,则可以跳过此部分:最新版本的 Ruby 会自动安装 Bundler。

Bundler 是一个 Gem,所以你必须使用 gem 命令来安装它。如果你使用 Ruby 版本管理器,则必须在希望使用 Bundler 的每个 Ruby 版本中安装 Gem。切换到你要使用的适当 Ruby 后,使用此命令安装 Bundler Gem:

$ gem install bundler

Gemfile 和 Gemfile.lock

Bundler 依赖于一个名为的文件 Gemfile 来告诉它应该使用哪个版本的 Ruby 及其 Gems。该文件是一个简单的 Ruby 程序,它使用域特定语言 (DSL) 来提供有关 Ruby 和 Gem 版本的详细信息。它是 Bundler 的配置或指令文件。

创建之后 Gemfile,该 bundle install 命令会扫描它,下载并安装列出的所有依赖项,并生成一个 Gemfile.lock 文件。Gemfile.lock 显示程序的所有依赖项;这包括 中列出的 Gems Gemfile,以及它们所依赖的 Gems(依赖项),它们可能未在 Gemfile. 你安装在项目中使用的 RubyGems 依赖于许多其他 gem,从而创建一个大的依赖树,这是很常见的。

让我们来看一个简单的例子。假设你正在编写需要 Ruby 2.3.1 和程序 sinatra,erubis 和 rack gem。我们 Gemfile 合并了这些依赖项,看起来像这样:

source 'https://rubygems.org'

ruby '2.3.1'
gem 'sinatra'
gem 'erubis'
gem 'rack'
gem 'rake', '~>10.4.0'

我们现在运行:

$ bundle install

安装指定的 Gems(如果需要)并创建一个 Gemfile.lock,如下所示:

GEM
  remote: https://RubyGems.org/
  specs:
    erubis (2.7.0)
    rack (1.6.4)
    rack-protection (1.5.3)
      rack
    rake (10.4.2)
    sinatra (1.4.7)
      rack (~> 1.5)
      rack-protection (~> 1.4)
      tilt (>= 1.3, < 3)
    tilt (2.0.5)

PLATFORMS
  ruby

DEPENDENCIES
  erubis
  rack
  rake (~> 10.4.0)
  sinatra

RUBY VERSION
   ruby 2.3.1p112

BUNDLED WITH
   1.13.6

请注意,Gem 名为 Bundler,但你使用的命令是 bundle(不带 r)。实际上,bundle 并且 bundler 是别名。

如果你对有关其 Gemfile 工作原理的更多详细信息感兴趣,请参阅 Bundler 文档以获取有关如何构建 Gemfile。

使用 Bundler 运行应用程序

一旦 Bundler 创建你的 Gemfile.lock,请添加:

require 'bundler/setup'

在任何其他 Gems 之前到你的应用程序的开头。(如果你的应用程序是 Rails 应用程序,则不需要此操作)。

要解决此问题,请 bundler/setup 阅读 Gemfile.lock;对于列出的每个 Gem,它会将包含该 Gem 的目录添加回 $LOAD_PATH. 完成后,require 只找到每个 Gem 的正确版本。这可确保加载你的应用程序依赖的特定 Gem 和版本,而不是该 Gem 的冲突版本。

现在,你需要做的就是运行你的应用程序,并在你 require 提交文件时加载正确的 Gem 。

你可能会看到一些建议添加:

require 'rubygems'

然而,这是不必要的。这个声明是 RubyGems 成为 Ruby 的正式组成部分之前的保留。Ruby 现在自动提供此功能。

Ruby、Gem 和应用程序现在在哪里?

Bundler 不会干扰你的红宝石及其宝石。它们保留在你安装 Bundler 之前的位置,并且将来会继续使用相同的设置。这意味着你仍然可以使用 gem env、rvm info 和 rbenv version 以及其他信息命令来查找你可能需要的信息。

但是,Bundler 提供了一个名为 binstubs; 如果你使用此功能,你可能需要将一些目录添加到你的 PATH. 该 gem env 和 rvm info 命令将反映这一点。

Bundler 开发团队非常小心地与 Ruby 版本管理器合作,以确保事情仍然按预期工作。偶尔,你可能会遇到一些奇怪的问题,尤其是在升级 Ruby 版本管理器之后。这些问题通常会被 Ruby 社区迅速解决。

执行 Bundler

正如我们之前看到的,依赖 Bundler 的应用程序应该 require 在 bundler/setup 加载任何 Gems 之前打包。此包可确保应用程序加载所需的 Gems。

不幸的是,你肯定会遇到不能只添加 require 'bundler/setup’到代码中的情况,或者程序本身可能会运行具有冲突需求的代码。发生这种情况时,你需要经常使用神秘 bundle exec 命令。

你可以使用 bundle exec 在符合 Gemfile.lock 版本信息的环境中运行大多数命令。实际上,我们可以使用此功能来查看如何 bundle exec 修改你的环境:

总结

Bundler 可让你准确描述要与 Ruby 应用程序一起使用的 Ruby 和 Gems。具体来说,它允许你在特定版本的 Ruby 下安装每个 Gem 的多个版本,然后在你的应用程序中使用正确的版本。

Bundler 是一个 RubyGem,所以你必须像普通 Gem 一样安装它:gem install bundler.

要使用 Bundler,你需要提供一个名为的文件 Gemfile,该文件描述你的应用程序所需的 Ruby 和 Gem 版本。你使用 Bundler 网站上描述的 DSL 来提供此信息。

Bundler 使用 Gemfile 来 Gemfile.lock 通过 bundle install 命令生成文件。

Gemfile.lock 描述你的应用程序需要的每个 Gem 的实际版本,包括其中列出的 Gems 所 Gemfile 依赖的任何 Gems 。该 bundler/setup 包告诉你的 Ruby 程序使用它 Gemfile.lock 来确定它应该加载哪些 Gem 版本。

该 bundle exec 命令确保 Gems 安装的可执行程序不会干扰你的应用程序的要求。例如,如果你的应用程序需要特定版本 rake 但默认版本 rake 不同,请 bundle exec 确保你仍然可以运行 rake 与你的应用程序兼容的特定版本。