【问题标题】:programmatically determine if a certain gem is installed, then install it if not以编程方式确定是否安装了某个 gem,如果没有安装它
【发布时间】:2012-05-30 20:16:07
【问题描述】:

这似乎不是一件难事。

我想要一个 ruby​​ 脚本,它可以确定系统上当前是否安装了某个 gem(任何版本,和/或某个版本规范)(无论 gem list 会出现在哪里),如果没有,则安装它。

是的,我知道在某些情况下 bundler 很擅长。相信我,我有一个案例,出于奇怪的原因,我不想使用 bundler。我真的很想以编程方式询问本地 gem 存储库。

当然,以编程方式安装的一种方法是直接发送到gem install

但我无法找到一种可靠的方法来以编程方式询问以查看是否安装了特定的 gem,而不是使用 shell,而不是使用 ruby​​gem api,什么都没有。

我错过了吗?

【问题讨论】:

    标签: ruby gem


    【解决方案1】:
    # The version requirements are optional.
    # You can also specify multiple version requirements, just append more at the end
    gem_name, *gem_ver_reqs = 'json', '~> 1.8.0'
    gdep = Gem::Dependency.new(gem_name, *gem_ver_reqs)
    # find latest that satisifies
    found_gspec = gdep.matching_specs.max_by(&:version)
    # instead of using Gem::Dependency, you can also do:
    # Gem::Specification.find_all_by_name(gem_name, *gem_ver_reqs)
    
    if found_gspec
      puts "Requirement '#{gdep}' already satisfied by #{found_gspec.name}-#{found_gspec.version}"
    else
      puts "Requirement '#{gdep}' not satisfied; installing..."
      # reqs_string will be in the format: "> 1.0, < 1.2"
      reqs_string = gdep.requirements_list.join(', ')
      # multi-arg is safer, to avoid injection attacks
      system('gem', 'install', gem_name, '-v', reqs_string)
    end
    

    最近的 ruby​​gems 版本提供了安装程序 API,因此您也可以使用 gem 命令,而不是使用:

    # using the same "gdep" variable as above
    Gem.install gem_name, gdep.requirement
    

    但是,我不确定Gem.install 是否尊重您的.gemrc 文件。

    有很多有用的方法可以查询已安装的 gem(参见 rdocs)。一些可能有帮助的:

    • Gem::Specification.find_all_by_name
    • Gem::Requirement#satisfied_by?(gem_version_instance)
    • Gem::Specification#satisfies_requirement?(gem_dependency_instance)
    • Gem.loaded_specs - 您通过 gem 方法或 require 实际加载的 gem 的哈希值

    【讨论】:

    • 完美的答案正是我想要的,感谢您提供了几种 api 方法的示例,而不仅仅是一个。 rubygems ruby​​ 代码 api 很难大致理解,感谢提供示例!
    【解决方案2】:

    最后一个答案很好,但这更准确一点:

    `gem install redis` unless `gem list`.lines.grep(/^redis \(.*\)/)
    

    仅匹配名为 redis 的 gem,而不匹配 redis-native_hash 等其他 gem。

    我看到的另一种方法是尝试要求 gem。

    begin
      require 'some_crazy_gem'
    rescue LoadError
      `gem install some_crazy_gem`
      #...
    end
    

    【讨论】:

    • 是的,我想说“不,不是任何以 'redis' 开头的 gem”,但我猜你的正则表达式和它的空格可以理解吗?仍然有点惊讶,没有更少的hacky方法来做到这一点。例如,没有像 gem list 这样的命令,但它只返回完全匹配而不是子字符串?
    • 你也可以试试gem which gem-name。如果响应以 ERROR 开头,则它不存在。
    • 很好,也很好。现在,如果只有一种方法可以检查特定 gem 的特定版本! rubygems 没有可靠的 ruby​​ api 来处理所有这些东西,这有点疯狂,每个人都求助于屏幕刮壳。
    • @jrochkind 看到我的答案 - 它不依赖于 gem 命令。使用命令行时的提示是使用 gem query -n '^json$',但这不利于检测特定版本,因为您必须解析输出中的版本号。
    • 我更喜欢rescue LoadError 方法,因为gem install 在许多系统上都需要管理员权限。在救援块中,向 STDERR 打印一条消息,拼写出gem install 命令并退出。
    【解决方案3】:

    检查是否安装了比特定版本更新的 gem。

    PROMPT> gem list nakamoto -i -v ">=1.2.3"
    true
    

    【讨论】:

      【解决方案4】:

      我不知道这是否是最佳做法,但是:

      list = `gem list`
      `gem install builder` unless list.include? "builder"
      

      ...在 irb 为我工作。

      【讨论】:

      • 我正在寻找一种不涉及到命令行的 shell,但可以在 ruby​​ 代码中完成的解决方案。接受的答案是这样的
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-10-05
      • 2019-02-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-21
      相关资源
      最近更新 更多