Bundler 在直接调用 .rb 文件时工作,在通过其他 ruby 脚本的 exec 调用时失败

Bundler works when .rb file called directly, fails when called via exec from other ruby script

我需要使用 Bundler + Gemfile 从另一个 ruby script1.rb 使用 Bundler + Gemfile 调用 ruby script2.rb。

我注意到 script2.rb 可用的宝石只有 script1.rb 的宝石。 script2.rb 独有的宝石在 script2.rb 被 script1.rb 调用时不可用。 当script2.rb直接从bashshell调用时,一切可用。

我确定这不是 ENV 问题,我在两个文件中使用 diff 和辅助代码对它们进行了比较,并进行了一些修改以使它们匹配。

File.open("script2_env.txt", 'wb') {|f| f.write(JSON.pretty_generate(ENV.to_h))}

为确保这不是 $LOAD_PATH 问题,我还确保它们匹配。

在由 script1.rb 调用的 script2.rb 中,我添加了以下行以匹配 script1 的 $LOAD_PATH :

$:.unshift "/usr/local/Cellar/rbenv/1.1.2/rbenv.d/exec/gem-rehash"

我对问题的理解是,当从 script1.rb 调用 script2.rb 时,Bundler 未正确初始化,可能是因为没有

eval "$(rbenv init -)"

正如我的 bash_profile

script1/script1.rb :

#!/usr/bin/env ruby
cwd=Dir.pwd ; ourDir=File.dirname(__FILE__) ; Dir.chdir(ourDir)
require 'bundler' ;  Bundler.setup
require "awesome_print"
ap "in script1, we have awesome_print in our GemFile"
exec("/Users/charbon/wip/script2/script2.rb")

script1/Gemfile

source 'https://rubygems.org'
gem 'awesome_print'

script2.rb :

#!/usr/bin/env ruby
puts "we are now in script2.rb"
$:.unshift "/usr/local/Cellar/rbenv/1.1.2/rbenv.d/exec/gem-rehash"
cwd=Dir.pwd ; ourDir=File.dirname(__FILE__) ; Dir.chdir(ourDir)

#make ENV match to script1 ENV
ENV.delete('BUNDLER_ORIG_GEM_PATH') 
ENV['BUNDLE_GEMFILE']=ourDir+"/Gemfile"
ENV['RBENV_DIR']=ourDir

require 'bundler' ;
Bundler.setup
require 'awesome_print'

ap "in script2, we also have awesome_print in our GemFile"
puts "but we also have colored, which is not available, this throws an erro"
require "colored"

script2/Gemfile

source 'https://rubygems.org'
gem 'awesome_print'
gem 'colored'

结果是

 /Users/charbon/wip/script1/script1.rb 
"in script1, we have awesome_print in our GemFile"
we are now in script2.rb
ourDir is /Users/charbon/wip/script2
"in script2, we also have awesome_print in our GemFile"
but we also have colored, which is not available, this throws an error
/Users/charbon/wip/script2/script2.rb:19:in `require': cannot load such file -- colored (LoadError)
    from /Users/charbon/wip/script2/script2.rb:19:in `<main>'

看来你需要在调用script2.rb之前制作Dir.chdir "/Users/charbon/wip/script2/"。 您正在从一个没有 Gemfile 的目录中调用 script2.rb。该脚本将如下所示:

#!/usr/bin/env ruby
cwd=Dir.pwd ; ourDir=File.dirname(__FILE__) ; Dir.chdir(ourDir)
require 'bundler' ;  Bundler.setup
require "awesome_print"
ap "in script1, we have awesome_print in our GemFile"
Dir.chdir "/Users/charbon/wip/script2/"
exec("script2.rb")

script1.rb

#!/usr/bin/env ruby
cwd=Dir.pwd ; ourDir=File.dirname(__FILE__) ; Dir.chdir(ourDir)
require 'bundler' ;  Bundler.setup
require "awesome_print"
ap "in script1, we have awesome_print in our GemFile"

Bundler.with_clean_env do
  Dir.chdir('/Users/charbon/wip/script2/script2.rb') do
    exec("./script2.rb")
  end 
end

script2.rb

#!/usr/bin/env ruby
puts "we are now in script2.rb"
cwd=Dir.pwd ; ourDir=File.dirname(__FILE__) ; Dir.chdir(ourDir)


require 'bundler' ;
Bundler.setup
require 'awesome_print'

ap "in script2, we also have awesome_print in our GemFile"
puts "but we also have colored, which is not available, this throws an erro"
require "colored"

总结并完成的回答:

Bundler 非常well documented:

Any Ruby code that opens a subshell (like system, backticks, or %x{}) will automatically use the current Bundler environment. If you need to shell out to a Ruby command that is not part of your current bundle, use the with_clean_env method with a block

第二种解决方案如Jay Dorsey : use the bundler/inline method

所评论

我已经测试了这两种方法,它们工作正常。

来自我对原问题的评论:

Bundler 支持 inline Gemfiles,它允许您直接在 ruby 脚本中指定所需的 gem(和来源!)。只要您在机器上安装了捆绑器,它就应该可以工作,从而使您可以隔离脚本依赖项。

Bundler 将处理安装和要求所需的 gem,允许您运行您的脚本