【问题标题】:script runs with 'bundler exec ruby script.rb' but fails when using 'ruby script.rb'脚本使用“bundler exec ruby​​ script.rb”运行,但在使用“ruby script.rb”时失败
【发布时间】:2016-01-29 00:23:52
【问题描述】:

我有一个脚本,它需要一个 gem,我是从一个具有特定分支的私有 github 存储库中获取的(我们只需调用这个 gem foobar)。

我正在使用 rvm、rails 版本 2.3.0 和 gemset(我们称之为 testset)。 我正在使用 bundler 来安装我的 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 运行脚本是最佳实践。否则,您将走上一条黑暗的道路,以确保 x 数量的宝石在全球范围内可用。如果您尝试包含本地文件或 gem,那么您必须 require 'path/to/gem'
  • 不会 rvm 解决这个问题吗?因为我只在特定的 gemsets 中提供我的包?
  • 对我来说,这听起来像是您正在尝试使用 gemsets 完成同样的事情,这在历史上很容易出现问题,您可以使用 bundler 完成,为您开箱即用。我不完全确定您的 RVM 设置是什么样的,但有很多陷阱。 bundler exec 是最佳实践,因为它简单、可靠并且每个人都喜欢它。在它的核心,它是一个比 RVM gemsets 更复杂但更优雅的系统。如果我是你,我会将 RVM 的使用限制为仅控制 Ruby 版本和所需的全局 gem。你听说过 RBenv 吗?
  • @Iwnnay 没有。总的来说,我实际上对 ruby​​ 很陌生(因此提出了 nooby 问题)

标签: ruby rvm bundler


【解决方案1】:

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

(注意,require用于加载 gems,它还可以用于加载加载路径上的其他文件,但出于这个问题的目的,我们正在谈论关于它加载宝石的能力)。

你的系统上没有安装foobar gem,所以你得到了LoadError。

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

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

我可以从您的路径中看出您也在使用 rvm gemsets,这使事情变得更加复杂,但这已经足够令人困惑,所以我没有深入讨论——我写的所有内容都仍然存在,除了“你的系统-wide gems”实际上是“当前的 rvm gemset”。就我个人而言,我相信你最好不要使用 rvm gemset 功能——没有它,事情就会变得很混乱,而且现在我们已经不再需要它了,rvm gemsets 是在 bundler 存在之前发明的(几年前)。而且我不完全确定为什么人们现在继续使用它们!

我可以在只使用 require 'foobar 的同时使用 ruby​​ script.rb 吗?

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

(虽然可以肯定,您可以使用 rvm gemsets 而不是 bundler ——但不能直接从 git 安装 gems,只有 bundler 支持,至少不需要大量手动黑客和 rvm gemsets在从头开始设置新系统时并不是很好,bundle 可以更好地解决这个问题)。

如果您确实在与script.rb 相同的目录中有一个 Gemfile 和 Gemfile.lock(如您所见),并且您只是想避免编写 bundle exec,您可以只需将其放在script.rb 的顶部(在任何其他requires 之前!):

 require 'bundler'
 Bundler.setup

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

希望这会有所帮助。

【讨论】:

  • 完美,我诚实地问了这个问题,因为我在其他任何地方都没有看到可靠的答案,这很好地解释了一切。谢谢
猜你喜欢
  • 1970-01-01
  • 2014-01-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-23
相关资源
最近更新 更多