I am trying to install a ruby gem from a ruby script when it can't load the required gem from local system. Here is my code.
begin
require '<gem name here>'
rescue LoadError
puts `gem install <gem name here>`
require '<gem name here>'
end
The code above installs the gem in the rescue block. But when it requires the gem, it shows this error:
.rvm/rubies/ruby-2.4.2/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb
:55:in require': cannot load such file -- <gem name here> (LoadError)
How can this be solved? I want the gem to be loaded within the rescue block if not already present in the system.
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
I'm trying to require hiredis in irb and it's already sending me an error:
irb(main):001:0> require "hiredis"
WARNING: could not load hiredis extension, using (slower) pure Ruby implementation.
=> true
irb(main):002:0>
Upon further investigation, I notice that it's crashing at this part of the code.
# hiredis-rb/lib/hiredis/connection.rb
module Hiredis
begin
require "hiredis/ext/connection"
Connection = Ext::Connection
rescue LoadError
warn "WARNING: could not load hiredis extension, using (slower) pure Ruby implementation."
require "hiredis/ruby/connection"
Connection = Ruby::Connection
end
end
So, I required the file hiredis/ext/connection and the error I get is the following error:
irb(main):001:0> require "hiredis/ext/connection"
LoadError: /home/***/projects/***/.gemset/extensions/x86_64-linux/2.1.0-static/hiredis-0.5.2/hiredis/ext/hiredis_ext.so:
undefined symbol: redisReaderFree -
/home/***/projects/***/.gemset/extensions/x86_64-linux/2.1.0-static/hiredis-0.5.2/hiredis/ext/hiredis_ext.so
from /home/***/.rbenv/versions/2.1.5/lib/ruby/2.1.0/rubygems/core_ext/kernel_require.rb:55:in `require'
I'm using Ubuntu and latest stable version of Redis (2.8.18). How do I fix this? Thanks!
EDIT:
So after looking at the hiredis-rb page: https://github.com/redis/hiredis-rb, it says that I should do the following:
To use hiredis with redis-rb, you need to require
redis/connection/hiredis before creating a new connection.
So I did a require "redis/connection/hiredis" on irb and then require "hiredis" and everything worked fine, no more warnings.
But now the warnings don't happen at all anymore now. I assumed that I'd need to require "redis/connection/hiredis" everytime for it to work but now it just works. So I dunno why it's not warning me anymore now. I thought calling require "redis/connection/hiredis" during that irb session was only for that session, not all succeeding sessions.
Is there a way to require a ruby gem safely so as to not raise an exception if the gem is not found?
I am looking a solution close to this:
if require 'hirb'
# do some hirb related stuff
else
# do other stuff
end
I want this to make sure no unnecessary gems are failing my deploys to production.
It would probably be done like this:
begin
require 'hirb'
rescue LoadError => e
puts "could not find hirb"
end
The best way to do this is to use bundler, that way you can be sure your gems really will be installed.
I have a bunch of Ruby 1.8.x scripts that require ftools.
How can I make these scripts stop throwing exceptions when I run them in ruby 1.9?
I want to preserve as much as possible, so that the scripts run successfully in both 1.8 and 1.9. I do not want to install RVM or something like that because it would be better to just write code that runs fine in both 1.9 and 1.8, to the extent possible.
This is what I have already tried:
begin; require 'ftools' rescue LoadError nil end;
begin; require 'fileutils' rescue nil; end;
How can I get this to work. The desired outcome is for ruby to silently fail if ftools is not found, and then just go on to fileutils instead.
Have you tried requiring your fallback library in the rescue handler?
begin
require 'ftools'
rescue LoadError
require 'fileutils'
end
Is there a way to check if some gem is currently installed, via the Gem module? From ruby code, not by executing 'gem list'...
To clarify - I don't want to load the library. I just want to check if it's available, so all the rescue LoadError solutions don't help me. Also I don't care if the gem itself will work or not, only whether it's installed.
In Ruby 1.9.3 only there is also:
Gem.available?('somegem')
You can use regex expressions too. Handy if I want to allow 'rcov' and GitHub variants like 'relevance-rcov':
Gem.available?(/-?rcov$/)
Looking at the Gem API documentation, using Gem::Specification::find_all_by_name to test for gem availability seems reasonable.
if Gem::Specification::find_all_by_name('gemname').any?
do stuff
end
find_all_by_name always returns an array (of Specification objects), as opposed to find_by_name which raises an exception if no match is found.
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.
Since Gem.available? is deprecated (argh!), you have to rescue again (double aaargh). Yes, find_by_name throws an exception if the gem is not found. So to be backwards-compatible with older rubygems, the common solution seems to be :
def gem_available?(name)
Gem::Specification.find_by_name(name)
rescue Gem::LoadError
false
rescue
Gem.available?(name)
end
Note that the new method allows you to pass a specific version to see if that's loaded:
Gem::Specification.find_by_name('rails', '3.0.4')
You could:
begin
require "somegem"
rescue LoadError
# not installed
end
This wouldn't, however, tell you if the module was installed through gem or some other means.
I use this code and it works smoothly.
def gem_available?(gem_name, version = nil)
version.nil? gem(gem_name) : gem(gem_name, version)
rescue Gem::LoadError
false
end
Examples to use
Let's assume you have rack 1.9.1 installed.
puts gem_available?('rack') # => true
puts gem_available?('rack', '>=2') => # false
Didn't see this anywhere here, but you can also pass fuzzy version strings to find_by_name and find_all_by_name:
Gem::Specification.find_all_by_name('gemname', '>= 4.0').any?