脚本以 'bundler exec ruby script.rb' 运行,但在使用 'ruby script.rb' 时失败

script runs with 'bundler exec ruby script.rb' but fails when using 'ruby script.rb'

我有一个脚本,它需要一个 gem,我从具有特定分支的私人 github 回购协议中获得(我们就称它为 gem foobar ).

我正在使用 rvm,rails 版本 2.3.0,和一个 gem 集(我们称之为测试集)。 我正在使用捆绑器安装我的 gems.

当我使用 bundler exec ruby script.rb 执行我的脚本时,它运行良好。但是,当我使用 ruby script.rb 尝试 运行 时,出现以下错误:

require': cannot load such file -- foobar (LoadError)

还值得注意的是,如果我按如下方式使用 gem 的路径,则仅使用 ruby 即可:

require '~/.rvm/gems/ruby-2.3.0@testset/bundler/gems/foobar-a41fed1fcab7/lib/foobar.rb'

有什么办法可以让我在只使用 require 'foobar 的情况下使用 ruby script.rb 吗?

如果您不使用 bundle exec,那么 require 将需要安装在您系统中的 gems gems。它将需要系统中当前安装的最新(更大版本)gem;如果系统中没有安装具有该名称的 gems,它会给你 LoadError。

(注意,require不只是用来加载gem,它还可以用来加载加载路径上的其他文件,但出于这个问题的目的,我们谈论的是它加载 gems 的能力)。

您的系统上没有安装 foobar gem,因此出现 LoadError。

那么为什么它适用于 bundle exec? bundler 是一个依赖 management 工具。当您使用 bundle exec 时,捆绑程序会加载您在 Gemfile/Gemfile.lock 文件中指定的 gem 的确切版本,并使它们可供 require 使用。在这种情况下,require gemname 不一定会加载安装在您系统上的最新版本,它会加载在 Gemfile.lock 中指定的版本——因为 bundle exec 设置了那样做.

通常当您 运行 bundle install 时,捆绑程序也会将所有 gem 安装到系统位置。但在某些情况下,捆绑程序仅在当前目录中安装 gems "locally",而不是在一般系统 gem 位置。其中一种情况是,如果您使用 path 参数 告诉 它。但这不是你正在发生的事情。另一种情况是,如果您告诉捆绑程序直接从 git 存储库加载 gem,并在 Gemfile 中使用 :git:github 参数。通用 gem 系统无法直接从 git 存储库加载 gems,这只是一个打包器功能。因此,如果您的 Gemfile 中列出了带有 :git 选项的内容,它将始终只为本地应用程序安装,而不是在系统范围的 gem 位置。我认为这就是这种情况下发生的事情,线索是您的示例路径中的 gems/foobar-a41fed1fcab7 有效 - gems 在其名称末尾列出了类似 -a41fed1fcab7 的内容通常直接来自-git-repo gems,a41fed1fcab7 是一个 git SHA,准确识别当前正在使用的 git repo 的哪个版本.

我可以从你的路径中看出你也在使用 rvm gemsets,这让事情变得更复杂了一点,但这已经足够令人困惑了,所以我没有深入探讨——我写的所有内容仍然成立,除了 "your system-wide gems" 实际上是 "the current rvm gemset"。就个人而言,我相信你最好不要使用 rvm gemset 功能——没有它,事情已经够混乱了,而且现在我们有了 bundler,rvm gem 就不再需要它了sets 是在 bundler 出现之前(几年前)发明的。而且我不完全确定为什么人们现在继续使用它们!

Is there anyway I can use ruby script.rb while only using require 'foobar?

当然可以。您只需要确保 foobar 已安装到您的系统 gems,并且 gem install foobar。然后 require "foobar" 将加载安装到您的系统 gem 的 最新 版本。 (但是你只能安装 released gems 到你的系统 gems,而不是从 git 任意签出)。如果要加载特定版本怎么办?嗯,你 可以 做类似 gem 'foobar', "1.0.2" 而不是 require 'foobar' 的事情。但是,有了一个非常重要的 gem 依赖项列表,您很快就会开始陷入混乱,无法准确管理您正在使用的 gem 版本与哪个版本兼容您正在使用的另一个 gem。除了设置一个新系统并准确确定您需要从头开始安装哪个版本的 gems 以使用 script.rb。它变得一团糟。 bundler 正是为此而发明的,您最好使用它。

(当然,您 可以 使用 rvm gemsets 而不是捆绑器——但不能直接从 [=97= 安装 gems ], 只有 bundler 支持,至少没有大量的手动 hackery,并且 rvm gemsets 在从头开始设置新系统时不是很好,bundle 解决得更好)。

如果你 有一个 Gemfile 和 Gemfile.lock 在与 script.rb 相同的目录中(就像你看起来那样),而你只是想避免不得不写 bundle exec,你可以把它放在 script.rb 的顶部(在任何其他 requires 之前!):

 require 'bundler'
 Bundler.setup

script.rb 为 运行 时,这基本上只是告诉捆绑程序执行 bundle exec 的等效操作,而不必实际编写 bundle exec。然后你可以只 运行 ruby script.rb (甚至 ./script.rb),它仍然会做与 bundle exec 相同的事情,但不必在命令行上写.

希望这对您有所帮助。