使用 Bundler 在 Ruby 中验证 gem 的版本

10

是否有一种方法可以从Ruby程序内部验证我是否拥有最新版本的gem?也就是说,是否有一种以编程方式执行bundle outdated #{gemname}的方法?

我尝试查看bundler的源代码,但找不到直接的方法。目前我正在使用下面这种方式,它很脆弱、慢且不优雅:

IO.popen(%w{/usr/bin/env bundle outdated gemname}) do |proc|
  output = proc.readlines.join("\n")
  return output.include?("Your bundle is up to date!")
end

谢谢大家,很多答案证实了我的发现:没有API可以做到这一点。我选择了其中一个,因为它提供了一个可行的代码片段来完成这个任务。 - pupeno
我觉得你选择的答案有一些问题。其中有一个 exit 1 会终止你的程序执行。你能让这段代码正常工作吗?我在尝试的时候遇到了错误。 - Emil
他们在1.2.x和1.3.x版本之间引入了exit 1,因此现在需要进行一些猴子补丁来解决问题。 - Alexey Kharchenko
我已经将 bundle outdated 提取为一个可重用的方法,没有退出和控制台输出。我认为这应该比猴子补丁“exit”或交换输出流更清洁。请查看我的更新答案以获取测试代码链接。 - Emil
6个回答

6

避免外部执行的方法:

对于bundler 1.2.x版本:

require 'bundler/cli'

# intercepting $stdout into a StringIO
old_stdout, $stdout = $stdout, StringIO.new 

# running the same code run in the 'bundler outdated' utility
Bundler::CLI.new.outdated('rails')

# storing the output
output = $stdout.string 

# restoring $stdout
$stdout = old_stdout 
对于bundler 1.3.x版本:
require 'bundler/cli'
require 'bundler/friendly_errors'

# let's cheat the CLI class with fake exit method
module Bundler
  class CLI 
    desc 'exit', 'fake exit' # this is required by Thor
    def exit(*); end         # simply do nothing
  end 
end

# intercepting $stdout into a StringIO
old_stdout, $stdout = $stdout, StringIO.new 

# running the same code run in the 'bundler outdated' utility
Bundler.with_friendly_errors { Bundler::CLI.start(['outdated', 'rails']) }

# storing the output
output = $stdout.string 

# restoring $stdout
$stdout = old_stdout     

我在运行时遇到了一个错误。此外,“outdated”中有一个狡猾的“exit 1”。我认为它不能像这样工作。 - Emil
嗯。没错。它可以在 bundler 1.2.3 上运行,但在 1.3.5 上会出问题。 - Alexey Kharchenko

4
目前没有编程方法可以在bundler中使用outdated命令,因为该代码位于Thor CLI文件中,会向用户打印输出。Bundler的测试也向系统发出命令并检查输出 (链接到outdated测试)。 不过,撰写自己的方法以反映cli.rb中outdated方法所做的操作应该很简单。请参见此处的突出显示的代码: 链接到Bundler源码中的outdated方法。删除带有Bundler.ui的行,并根据out_count的值返回true / false即可。 更新:我已将“bundle outdated”提取为一个可重复使用的方法,没有控制台输出和退出。您可以在这里找到要点:链接到Gist。我已在bundler 1.3上测试了这个方法,看起来是有效的。

不幸的是,这将成为以编程方式实现的唯一真正方法。我对Bundler的编写质量感到惊讶。根本没有关注点分离; UI代码完全与版本检查逻辑交织在一起。看起来有点可悲。 - Jim Stewart
@JimStewart,我相信他们会接受一些重构的贡献。 - Fábio Batista

0
检查最新bundler源代码的源代码
我可以想到这个

https://github.com/carlhuda/bundler/blob/master/lib/bundler/cli.rb#L398

$ irb
1.9.3p327 :001 > require 'bundler'
 => true 
1.9.3p327 :002 > def outdated_gems(gem_name,options={})
1.9.3p327 :003?>   options[:source] ||= 'https://rubygems.org'
1.9.3p327 :004?>   sources = Array(options[:source])
1.9.3p327 :005?>   current_spec= Bundler.load.specs[gem_name].first
1.9.3p327 :006?>   raise "not found in Gemfile" if current_spec.nil?
1.9.3p327 :007?>   definition = Bundler.definition(:gems => [gem_name], :sources => sources)
1.9.3p327 :008?>   options["local"] ? definition.resolve_with_cache! : definition.resolve_remotely!
1.9.3p327 :009?>       active_spec = definition.index[gem_name].sort_by { |b| b.version }
1.9.3p327 :010?>    if !current_spec.version.prerelease? && !options[:pre] && active_spec.size > 1
1.9.3p327 :011?>             active_spec = active_spec.delete_if { |b| b.respond_to?(:version) && b.version.prerelease? }
1.9.3p327 :012?>         end
1.9.3p327 :013?>       active_spec = active_spec.last
1.9.3p327 :014?>       raise "Error" if active_spec.nil?
1.9.3p327 :015?>   outdated = Gem::Version.new(active_spec.version) > Gem::Version.new(current_spec.version)
1.9.3p327 :016?>   {:outdated=>outdated,:current_spec_version=>current_spec.version.to_s,:latest_version=>active_spec.version.to_s}
1.9.3p327 :017?>   end
 => nil 
1.9.3p327 :018 > 
1.9.3p327 :019 >   
1.9.3p327 :020 >   
1.9.3p327 :021 >   
1.9.3p327 :022 >   outdated_gems('rake')
 => {:outdated=>true, :current_spec_version=>"10.0.3", :latest_version=>"10.0.4"} 

这可能无法与早期版本的bundler一起使用。


0

bundle check 命令会列出那些需要更新的 gem,你可能需要使用它。


我的问题是关于在Ruby内部以编程方式运行它。 - pupeno

0

嗯,听起来你可能想要使用 bundle show 或者 gem env


0

令人失望的是,这看起来出奇地困难。

在bundler中有一些问题,官方给出的回答似乎是:

目前还没有记录的Ruby API。 不过,这是我们的计划之一。

通过查看bundler源代码cli.rb,很明显从Ruby中调用它会比较棘手,或者以合理的方式重现代码。

因为其中夹杂着退出的调用,所以从CLI调用方法将会困难。

重现代码看起来也不好玩,因为其中包含了相当多的bundler逻辑。

祝你好运!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接