How to Rescue from a Require "Gem_Name" When the Gem Is Not Installed

How to rescue from a require gem_name when the gem is not installed

require raises a LoadError exception if it can't load the required library. However, you never rescue from LoadError anywhere, you rescue from StandardError.

If you want to rescue from LoadError, you have to say so:

begin 
require 'some_gem'
rescue LoadError
puts 'please install some_gem first!'
end

Even better yet, make sure that you are actually printing the correct missing dependency:

begin 
require 'some_gem'
rescue LoadError => e
raise unless e.message =~ /some_gem/
puts 'please install some_gem first!'
end

(This re-raises the exact same exception that was rescued from, in case that the exception was actually caused by some other missing library somewhere else. You wouldn't want to print misleading information, right?)

Depending on what the intended target audience for the library is and whether or not they might be scared away by a backtrace being dumped to their console, you might want to re-raise the exception in any case, instead of just swallowing it:

begin 
require 'some_gem'
rescue LoadError => e
puts 'please install some_gem first!' if e.message =~ /some_gem/
raise
end

Or, you could skip the puts and instead raise an exception with the message set to what you want to say:

begin 
require 'some_gem'
rescue LoadError => e
raise e.exception('please install some_gem first!') if e.message =~ /some_gem/
raise
end

Except now the exception is raised in the wrong place and thus has the wrong line number and stacktrace and thus is misleading, but that is easily fixed:

begin 
require 'some_gem'
rescue LoadError => e
raise unless e.message =~ /some_gem/
friendly_ex = e.exception('please install some_gem first!')
friendly_ex.set_backtrace(e.backtrace)
raise friendly_ex
end

Now you print pretty much the same thing that you would have printed with the puts, but you have a "proper" exception that for example allows better debugging or allows a consumer of your library to rescue that exception and handle it their way, both of which would have been impossible or at least hard with your solution that just swallows the exception.

Install ruby gem in rescue block when 'LoadError' occurs

After rigorous searching, I found an answer. If we use Gem.clear_paths after installing the gem, it will now available to the script. Total updated code is :

begin
require '<gem name here>'
rescue LoadError
puts `gem install <gem name here>`
Gem.clear_paths
require '<gem name here>'
end

ruby: code to install gem if missing

Checking availability is covered in this previous StackOverflow Quesiton

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

or

matches = Gem.source_index.find_name(gem.name, gem.version_requirements)

As for the install, it looks like rails uses the system for gem install also

 puts %x(#{cmd})

Ruby: How to REQUIRE an installed GEM programmatically?

You can require the gem if it's present and also install the gem if it's not already installed the following way (Look for the comments in the code):

def checkGemColorize(gemName, versionLimit=nil)
isAvailable = false
begin
if versionLimit == nil
gem_present = gem gemName # this will return true if the gem is present
if gem_present
puts "Yes the GEM is installed"
require gemName # here you are requiring the gem
puts "#{gemName} GEM is required just now"
end
else
gem gemName, versionLimit
puts "Yes the GEM is installed with correct version number"
#also if version number is used.
end
isAvailable = true
rescue LoadError
# I added this block of code to install the gem when it's missing
puts "#{gemName} is missing, Installing now...."
`gem install #{gemName}` # installing the missing gem
puts "installed the #{gemName} gem just now!"
isAvailable = true
end
isAvailable
end

#testing IF TRUE on colorize
puts "checking for colorize GEM"
puts checkGemColorize('colorize')

ERROR: Gem bundler is not installed, run `gem install bundler` first

I think this is the problem: You have bundler installed to a specific gemset, which is why it's only available when you're in your app's directory (I'm assuming there's a .rvmrc file in there).

You have a few options:

  1. Install bundler to a global gemset. rvm gemset use global && gem install bundler
  2. If you have Homebrew installed, just do brew install ruby and avoid rvm altogether. (There's also rbenv and ry as alternatives to rvm, but I just use 1.9.3 across all my apps, so Homebrew is fine.)

For reference, $PATH is a shell environmental variable containing a list of directories that hold executables (e.g., echo, ls, vim, etc.). It's intrinsic to shells.

Safely require gems in Ruby

It would probably be done like this:

begin
require 'hirb'
rescue LoadError => e
puts "could not find hirb"
end

Check for Ruby Gem availability

IMHO the best way is to try to load/require the GEM and rescue the Exception, as Ray has already shown. It's safe to rescue the LoadError exception because it's not raised by the GEM itself but it's the standard behavior of the require command.

You can also use the gem command instead.

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

The gem command has the same behavior of the require command, with some slight differences. AFAIK, it still tries to autoload the main GEM file.

Digging into the rubygems.rb file (line 310) I found the following execution

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

It can provide you some hints about how to make a dirty check without actually loading the library.

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

# 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

sudo gem install' or 'gem install' and gem locations

Contrary to all the other posts I suggest NOT using sudo when installing gems.

Instead I recommend you install RVM and start a happy life with portable gem homes and different version of Ruby all living under one roof.

For the uninitiated, from the documentation:

RVM is a command line tool which allows us to easily install, manage and work with multiple ruby environments and sets of gems.

The reason why installing gems with sudo is worse than just gem install is because it installs the gems for ALL USERS as root. This might be fine if you're the only person using the machine, but if you're not it can cause weirdness.

If you decide you want to blow away all your gems and start again it's much easier, and safer, to do so as a non-root user.

If you decide you want to use RVM then using sudo will cause all kinds of weirdness because each Ruby version you install through RVM has its own GEM_HOME.

Also, it's nice if you can make your development environment as close to your production environment as possible, and in production you'll most likely install gems as a non-root user.



Related Topics



Leave a reply



Submit