Safely require gems in Ruby - ruby

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.

Related

How to add "pry" when developing a Ruby gem

I have a gem called "something".
I would like to add pry as a development dependency when developing the gem. However I don't know how to load it.
If I have "require something" inside lib/something.rb , when I release the gem, it throws a LoadError, because pry is only a development dependency.
At the same time I don't want to keep adding and removing pry when I am committing code.
What is the best way to require pry only when developing the application, but not require it as a dependency for the gem?
You can use the add_development_dependency in the gemspec file. You'll still have to require it in your lib/something.rb file within a begin .. rescue LoadError block. (Edit 2, see below)
In your case, it will be something like the following:
spec.add_development_dependency 'pry', '~> 0.9.12.2'
The purpose of add_development_dependency is to separate the gems into dependencies that get installed when you execute gem install mygem vs development-only dependencies that are installed only when you execute gem install mygem --development.
Edit: #Pierre-Louis Gottfrois' solution
Modify the Gemfile directly and add a test group. This question describes the process. This does not appear to be a preferred solution according to Yehuda Katz.
Edit 2: begin require ... rescue LoadError is apparently a common practice for Ruby scripts, according to this Making Ruby Gems article.
I think I found a workaround for that.
If you configure bundler to use pry as your console with
$ bundle config console pry
Then pry is itself required and you don't need to explicitly require in your source files.
Plus, you get a history on pressing ' ↑ '.

How to remove another gem with my gemspec

We have a gem foo that used to have a dependency on another of our gems bar. But now we've pulled the bar code directly into foo.
Is it possible to have the bar gem removed the next time our users upgrade foo?
I seem to have found a quick and dirty solution, similar to adding conditional install time deps. Unfortunately, I haven't figured out how to tell the user, since gem squashes the extension output unless the user specifies verbose.
in the ext/mkrf_conf.rb, I added:
require 'rubygems'
require 'rubygems/uninstaller.rb'
begin
gem = Gem::Specification.find_by_name('other_gem');
Gem::Uninstaller.new('other_gem').uninstall
rescue Gem::LoadError
end

After installing a gem within a script, how do I load the gem?

I have a small Ruby script that I'm writing to automate the preparation of a development environment on local machines. Because I can't be certain that the rubyzip2 library is present on all of the machines, I'm having the script install it when needed.
Currently, my script is doing the following:
begin
require 'zip/zip'
rescue LoadError
system("gem install rubyzip2")
end
Once the gem has been installed, the script continues execution; however, the gem hasn't been loaded so all code requiring rubyzip2 halts the execution.
How do I load the gem into memory so that the script can continue running after installation?
Instead of doing require 'thegem' and rescuing error, you should check the gem availability before, and then, if needed, install it. After, you can require it.
Take a look at this post for the gem availability
Or this post
EDIT
After installation, you need to clear gem paths if you don't want to reload your script.
You could achieve this with this method :
Gem.clear_paths
There are already answered questions here
So your code should looks like this ( for example ) :
begin
gem "rubyzip2"
rescue LoadError
system("gem install rubyzip2")
Gem.clear_paths
end
require 'zip/zip'
With bundler version higher than 1.10 (to update just run gem install bundler) you can use its new 'inline' feature as described here.
require 'bundler/inline'
gemfile(true) do
source 'https://rubygems.org'
gem 'catpix'
end
puts Catpix::VERSION
First parameter of gemfile method is whether gems that aren't already installed on the user's system should be installed.
Use Bundler with your application/library. This will allow users to run bundle and all the gems will be fetched and ready for you to use.
Ok so you may want to use Bundler and set up a Gemfile then have bundler do a bundle install, bundler will fetch out all the gems and install them if it is not already installed and you can then require all the gems in the gem file. Read the documentation in the link for more information.
But what you are looking to do specifically in your question is to use the retry keyword. This keyword will retry the loop after the rescue was called.
So if you require the gem and it fails and the Load Error Exception is called. The Begin Block will rescue, the system call will install the gem, then it will retry and require the gem. Just cautious because this may lead to an infinite loop unless you want to set up a condition to maybe retry it only once.
begin
require 'zip/zip'
rescue LoadError
system("gem install rubyzip2")
retry
end

How can I trap LoadError exceptions in Ruby 1.9 and 1.8?

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

Check for Ruby Gem availability

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?

Resources