我有一个脚本,需要一个宝石,我从一个特定分支的私有github仓库中获取(让我们称之为宝石foobar
)。
我使用rvm,rails verson 2.3.0和gemset(让我们称之为testset)。 我使用bundler来安装我的宝石。
当我使用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'
我是否可以使用ruby script.rb
而只使用require 'foobar
?
答案 0 :(得分:1)
如果您不使用bundle exec
,那么require
将可以使用系统宝石中安装的宝石。它将需要系统中当前安装的最新(更大版本)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只在当前目录中“本地”安装gem,而不是在一般系统gem位置。其中一种情况是,如果您使用path
参数告诉它。但那不是你的想法。另一种情况是,如果您告诉bundler直接从git repo加载gem,并在Gemfile中使用:git
或:github
参数。一般的宝石系统不能直接从git仓库加载宝石,这只是一个捆绑功能。因此,如果您的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是在捆绑器存在之前发明的(几年前) )。我不完全确定为什么人们现在继续使用它们!
有没有我可以使用ruby script.rb而只使用require'foobar?
不确定。您只需确保使用foobar
将gem install foobar
安装到系统宝石中。然后require "foobar"
会将安装的最新版本加载到您的系统宝石中。 (但是您只能将具有特定版本号的已发布的宝石安装到您的系统宝石中,而不是从git中随意检出)。如果要加载特定版本怎么办?好吧,你可以做gem 'foobar', "1.0.2"
而不是require 'foobar'
之类的事情。但是有了一个非常重要的宝石依赖项列表,你很快就会开始陷入一种可怕的混乱,无法管理你正在使用的宝石版本与你正在使用的另一个宝石的版本兼容。除了设置一个新系统并确定您需要从头开始安装哪些版本的宝石以使用script.rb
。它变得一团糟。正是为了这个bundler
被发明了,你明智地使用它。
(虽然可以肯定的是,你可以使用rvm gemsets而不是bundler - 但不能直接从git安装gems,只有Bundler支持,至少没有大量的手动hackery和rvm gemsets在从头开始设置新系统时不是很好,捆绑解决方案要好得多。
如果你做在script.rb
所在的目录中有一个Gemfile和Gemfile.lock(就像你看来的那样),你只想避免写{{1} },你可以把它放在bundle exec
的顶部(在任何其他script.rb
之前!):
require
这基本上只是告诉bundler在运行 require 'bundler'
Bundler.setup
时执行等效的bundle exec
,而不必实际编写script.rb
。然后你可以运行bundle exec
(甚至ruby script.rb
),它仍然会像./script.rb
那样做,但不必在命令行上写它。
希望这会有所帮助。