программно определить, установлен ли определенный гем, а затем установить его, если нет

Не похоже, что это будет сложно сделать.

Мне нужен ruby-скрипт, который определяет, установлен ли в настоящее время в системе определенный гем (любая версия и/или определенная спецификация версии) (куда бы gem list ни смотрел), и если нет, то устанавливает его.

Да, я знаю, что в некоторых случаях упаковщик отлично справляется с этим. Поверьте мне, у меня есть случай, когда по странным причинам я не хочу использовать упаковщик. Я действительно хочу программно опросить местное хранилище драгоценных камней.

Один из способов программной установки — это просто раскошелиться на gem install, конечно.

Но я не могу найти надежный способ программно опросить, чтобы увидеть, установлен ли конкретный драгоценный камень, ни с оболочкой, ни с API rubygem, ни с чем.

Я пропустил это?


person jrochkind    schedule 30.05.2012    source источник


Ответы (4)


# 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

Более поздние версии rubygems предоставляют API-интерфейс установщика, поэтому вместо запуска команды gem вы также можете использовать:

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

Однако я не уверен, что Gem.install уважает ваш файл .gemrc.

Существует множество полезных методов для запроса установленных гемов (см. 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
person Kelvin    schedule 02.08.2013
comment
идеальный ответ именно то, что я искал, спасибо за предоставление примеров нескольких методов API вместо одного. Rubygems ruby ​​code API сложно получить общее представление, спасибо за примеры! - person jrochkind; 24.10.2014

Последний ответ был хорош, но это немного точнее:

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

Соответствует только драгоценному камню с именем redis, а не другим драгоценным камням, таким как redis-native_hash или что-то еще.

Другой способ, который я видел, это попытаться потребовать драгоценный камень.

begin
  require 'some_crazy_gem'
rescue LoadError
  `gem install some_crazy_gem`
  #...
end
person Carl Zulauf    schedule 30.05.2012
comment
Да, я собирался сказать Нет, не любой драгоценный камень, начинающийся с «redis», но я думаю, ваше регулярное выражение с пробелом улавливает это? Все еще немного поражен, что есть не менее хакерский способ сделать это. Например, нет такой команды, как gem list, но которая возвращает только точные совпадения, а не подстроки? - person jrochkind; 01.06.2012
comment
Вы также можете попробовать gem which gem-name. Если ответ начинается с ERROR, его там нет. - person Carl Zulauf; 01.06.2012
comment
здорово, это тоже хорошо. Если бы только был способ проверить конкретную версию конкретного драгоценного камня! Это какое-то сумасшествие, что у rubygems нет надежного ruby ​​API для всех этих вещей, и все прибегают к очистке экрана от оболочки. - person jrochkind; 02.06.2012
comment
@jrochkind посмотри мой ответ - он не зависит от команды gem. Когда вы находитесь в командной строке, советуем использовать gem query -n '^json$', но это не очень хорошо для определения конкретной версии, потому что вам придется анализировать номера версий в выходных данных. - person Kelvin; 02.08.2013
comment
Я предпочитаю подход rescue LoadError, потому что gem install требует прав администратора во многих системах. В блоке восстановления напечатайте сообщение STDERR с указанием команды gem install и выйдите. - person pjs; 02.08.2013
comment
Хотел бы я избежать обратной галочки в комментарии. В дальнейшем используйте обратную кавычку вместо одинарной кавычки в каждом случае: Для меня запуск 'gem list'.lines.grep(/^redis \(.*\)/) в irb без установленного redis возвращает пустой массив. Разве мы не должны использовать .blank? или .empty?, чтобы проверить, являются ли результаты пустыми, потому что пустой массив true так же, как непустой массив. Я новичок в Ruby, но это работает для меня: 'gem install redis' if 'gem list'.lines.grep(/^redis \(.*\)/).empty? Приведенный выше код не работает. Возможно, это как-то связано с моей версией рубина. Я на 1.9.3. - person DutGRIFF; 05.02.2014

Убедитесь, что установлен гем новее определенной версии.

PROMPT> gem list nakamoto -i -v ">=1.2.3"
true
person neoneye    schedule 02.07.2019

Я понятия не имею, является ли это лучшей практикой или нет, но:

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

... работал у меня в ирб.

person vlasits    schedule 30.05.2012
comment
Я искал решение, которое не включало бы оболочку в командную строку, но могло бы быть выполнено в рубиновом коде. принятый ответ такой - person jrochkind; 03.05.2016