Verifique la disponibilidad de Ruby Gem
Pregunta
¿Hay alguna forma de verificar si alguna gema está instalada actualmente, a través del módulo Gem? Desde el código ruby, no ejecutando 'lista de gemas' ...
Para aclarar: no quiero cargar la biblioteca. Solo quiero verificar si está disponible, por lo que todas las soluciones rescue LoadError
no me ayudan. Además, no me importa si la gema en sí funcionará o no, solo si está instalada.
Solución
En mi humilde opinión, la mejor manera es intentar cargar / requerir el GEM y rescatar la excepción, como Ray ya ha demostrado. Es seguro rescatar la excepción LoadError porque no es generada por el propio GEM, pero es el comportamiento estándar del comando require.
También puedes usar el comando gem en su lugar.
begin
gem "somegem"
# with requirements
gem "somegem", ">=2.0"
rescue Gem::LoadError
# not installed
end
El comando gem tiene el mismo comportamiento que el comando require, con algunas pequeñas diferencias. AFAIK, todavía intenta cargar automáticamente el archivo GEM principal.
Excavando en el archivo rubygems.rb (línea 310) encontré la siguiente ejecución
matches = Gem.source_index.find_name(gem.name, gem.version_requirements)
report_activate_error(gem) if matches.empty?
Puede proporcionarle algunas sugerencias sobre cómo hacer un cheque sucio sin cargar realmente la biblioteca.
Otros consejos
También hay:
Gem.available?('somegem')
También puedes usar expresiones regex. Útil si quiero permitir variantes 'rcov' y GitHub como 'relevancia-rcov':
Gem.available?(/-?rcov$/)
Mirando la documentación de la API Gem, usando Gem: : Especificación :: find_all_by_name para probar la disponibilidad de gemas parece razonable.
if Gem::Specification::find_all_by_name('gemname').any?
do stuff
end
find_all_by_name
siempre devuelve una matriz (de objetos de Especificación), en lugar de find_by_name
que genera una excepción si no se encuentra ninguna coincidencia.
¿Desde Gem.available? está en desuso (¡argh!), tienes que rescatar nuevamente (doble aaargh). Sí, find_by_name produce una excepción si no se encuentra la gema. Por lo tanto, para ser compatible con versiones anteriores de rubygems, la solución común parece ser:
def gem_available?(name)
Gem::Specification.find_by_name(name)
rescue Gem::LoadError
false
rescue
Gem.available?(name)
end
Tenga en cuenta que el nuevo método le permite pasar una versión específica para ver si está cargada:
Gem::Specification.find_by_name('rails', '3.0.4')
Podrías:
begin
require "somegem"
rescue LoadError
# not installed
end
Sin embargo, esto no le diría si el módulo se instaló a través de gem u otro medio.
Uso este código y funciona sin problemas.
def gem_available?(gem_name, version = nil)
version.nil? gem(gem_name) : gem(gem_name, version)
rescue Gem::LoadError
false
end
Ejemplos de uso
Supongamos que tiene instalado el rack 1.9.1.
puts gem_available?('rack') # => true
puts gem_available?('rack', '>=2') => # false