Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

programmatically determine if a certain gem is installed, then install it if not

Tags:

ruby

gem

This doesn't seem like it would be a hard thing to do.

I want a ruby script that figures out if a certain gem (any version, and/or a certain version spec) is currently installed on the system (wherever gem list will look), and if not, installs it.

Yes, I know in some cases bundler is great at that. Trust me that I have a case where for weird reasons I don't want to use bundler. I really do want to programmatically interrogate the local gem repo.

One way to programmatially install is just to shell out to gem install, sure.

But I can't manage to figure out a reliable way to programmatically interrogate to see if a particular gem is installed, not with shell out, not with rubygem api, not anything.

Am I missing it?

like image 554
jrochkind Avatar asked May 30 '12 20:05

jrochkind


3 Answers

# 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

More recent rubygems versions provide an installer API, so instead of shelling out to the gem command you could also use:

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

However, I'm not sure if Gem.install respects your .gemrc file.

There are a lot of useful methods for querying your installed gems (see rdocs). Some that might be helpful:

  • Gem::Specification.find_all_by_name
  • Gem::Requirement#satisfied_by?(gem_version_instance)
  • Gem::Specification#satisfies_requirement?(gem_dependency_instance)
  • Gem.loaded_specs - hash of the gems you've actually loaded via the gem method, or by require
like image 77
Kelvin Avatar answered Nov 09 '22 03:11

Kelvin


Last answer was good, but this is a little more precise:

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

Matches only the gem named redis, and not other gems like redis-native_hash, or something else.

Another way I've seen this done is to try requiring the gem.

begin
  require 'some_crazy_gem'
rescue LoadError
  `gem install some_crazy_gem`
  #...
end
like image 36
Carl Zulauf Avatar answered Nov 09 '22 02:11

Carl Zulauf


Check that there is installed a gem newer than a particular version.

PROMPT> gem list nakamoto -i -v ">=1.2.3"
true
like image 37
neoneye Avatar answered Nov 09 '22 02:11

neoneye