检查Ruby Gem是否可用

42

有没有一种方法可以通过 Gem 模块检查某个 gem 是否已经安装好了?从 Ruby 代码中,而非通过执行 'gem list' 命令...

为了澄清 - 我不想加载这个库。我只是想检查它是否可用,所以所有的 rescue LoadError 解决方案对我都没有帮助。另外,我并不关心这个 gem 是否能工作,只关心它是否已经被安装过。

7个回答

39

在 Ruby 1.9.3 中,还有:

Gem.available?('somegem')

你也可以使用正则表达式。如果我想要允许 'rcov' 和 GitHub 的变体,如 'relevance-rcov',这将非常方便:

Gem.available?(/-?rcov$/)

28
此方法现已弃用: 注意:Gem.available? 已被弃用,请使用 Specification::find_by_name。它将在 2011-11-01 或之后被移除。 - Mike Fiedler
2
截至今天,Gem::available?已经不存在了。我建议删除这个答案或添加一条注释,以避免误导人们。 - anon
这个答案现在需要被删除。 (Ruby 2.5.1 Gem不可用?方法) - zw963

37

查看 Gem API 文档,使用 Gem::Specification::find_all_by_name 来测试 gem 的可用性似乎是合理的。

if Gem::Specification::find_all_by_name('gemname').any?
  do stuff
end

find_all_by_name 总是返回一个数组 (Specification 对象的数组),而 find_by_name 则在找不到匹配项时引发异常。


5
如果你正在使用rubygems 1.8或2.0,则以下是最佳答案。无需使用rescue关键字。 或者,您可以使用 if ... .any? 更易读的语法。 - Kelvin

32

我认为最好的方法是尝试加载/需要这个GEM并捕获异常,就像Ray已经展示的那样。可以安全地捕获LoadError异常,因为它不是由GEM本身引发的,而是require命令的标准行为。

您也可以使用gem命令。

begin
  gem "somegem"
  # with requirements
  gem "somegem", ">=2.0"
rescue Gem::LoadError
  # not installed
end

gem命令与require命令具有相同的行为,但存在一些细微差别。据我所知,它仍然尝试自动加载主GEM文件。

深入研究rubygems.rb文件(第310行),我发现了以下执行内容

matches = Gem.source_index.find_name(gem.name, gem.version_requirements)
report_activate_error(gem) if matches.empty?

它可以为您提供一些提示,如何在没有实际加载库的情况下进行脏检查。


1
非常准确。我将使用以下代码:Gem.source_index.find_name('some_name').map {|x| x.name} - 它会返回匹配的已安装 gem。 - viraptor
使用 $ gem list | grep YOURGEM 来检查 YOURGEM 是否存在。 - Simone Carletti
GEM::LoadError 应该是 Gem::LoadError - Anna

30

由于Gem.available?已经被弃用(唉!),因此您需要再次进行解救(双倍的痛苦)。是的,如果未找到宝石,则find_by_name会抛出异常。因此,为了向后兼容旧版rubygems,常见的解决方案似乎是:

def gem_available?(name)
   Gem::Specification.find_by_name(name)
rescue Gem::LoadError
   false
rescue
   Gem.available?(name)
end

请注意,新方法允许您传递特定的版本以查看是否已加载:

Gem::Specification.find_by_name('rails', '3.0.4')

6
您可以:
begin
  require "somegem"
rescue LoadError
  # not installed
end

然而,这并不能告诉您模块是通过 gem 还是其他方式安装的。


2
这里有两个问题:我不想加载库(至少现在不想),而且如果“somegem”中的代码因其他原因抛出LoadError,那么这将失败。 - viraptor

1
我使用这段代码,它运行得非常顺畅。
def gem_available?(gem_name, version = nil)
  version.nil? gem(gem_name) : gem(gem_name, version)
rescue Gem::LoadError
  false
end

使用示例

假设您已安装了rack 1.9.1。

puts gem_available?('rack') # => true
puts gem_available?('rack', '>=2') => # false

0

在这里没有看到过这个,但是您也可以将模糊版本字符串传递给find_by_namefind_all_by_name函数:

Gem::Specification.find_all_by_name('gemname', '>= 4.0').any?

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