Install ruby gem in rescue block when 'LoadError' occurs - ruby

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

Related

How to unzip a file with ruby

Okay I've found the following code for unzippping a file with Ruby.
def unzip_file (file, destination)
Zip::ZipFile.open(file_path) { |zip_file|
zip_file.each { |f|
f_path=File.join("destination_path", f.name)
FileUtils.mkdir_p(File.dirname(f_path))
zip_file.extract(f, f_path) unless File.exist?(f_path)
}
}
end
Above this I'm using the following to ensure the needed gems are installed.
begin
require 'rubygems'
rescue LoadError
'gem install rubygems'
end
begin
require 'zip/zip'
rescue LoadError
'gem install rubyzip'
end
So when I call unzip_file I get the following error:
in `unzip_file': uninitialized constant Zip (NameError)
What am I doing wrong?
Thanks!
Beware: the sample script will also unpack symlinks, and will unpack ../../../../etc/passwd without complaining. The rubyzip gem expects you to do your own pathname laundering.
Note that in rubyzip 1.1.4, Zip::Zipfile was renamed to Zip::File.
The problem with installing the gem that way is that you're shelling out to another process with:
`gem install rubyzip`
and after that finishes installing the gem, your current irb session still won't see it. You'd have to reload irb with exec "irb" and then calling require 'zip' again.
Note: those are backticks not single quotes.
Try this:
begin
require 'zip'
rescue LoadError
`gem install rubyzip`
exec "irb"
retry
end
For me require 'zip' works. I have rubyzip-1.1.2
Now you should be able to use Zip
Also, the gem command is rubygems. So you can't install rubygems with itself. It should already be installed, but if not try this: http://rubygems.org/pages/download

Why can't I load Nokogiri?

I installed Nokogiri without any issues by running:
$ sudo gem install nokogiri
Building native extensions. This could take a while...
Successfully installed nokogiri-1.5.9
1 gem installed
Installing ri documentation for nokogiri-1.5.9...
Installing RDoc documentation for nokogiri-1.5.9...
When I run nokogiri.rb:
#!/usr/bin/ruby -w
require 'nokogiri'
puts "Current directory is: #{ Dir.pwd }"
Dir.chdir("/home/askar/xml_files1") do |dirname|
puts "Now in: #{ Dir.pwd }"
xml_files = Dir.glob("ShipmentRequest*.xml")
if xml_files.empty?
puts "#{ dirname } is empty."
else
xml_files.each do |file|
doc = Nokogiri::XML(open(file))
# ... do something with the doc ...
end
end
end
I got the error:
$ ruby nokogiri.rb
/home/askar/.rvm/rubies/ruby-1.9.3-p429/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require': cannot load such file -- nokogiri (LoadError)
from /home/askar/.rvm/rubies/ruby-1.9.3-p429/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
I'm using Ruby 1.9.3, but for some reason it refers to Ruby 1.9.1. Can this be the cause?
If you observe closely, the path starts with /home/askar/.rvm/rubies/ruby-1.9.3-p429 so the load path should be correct.
Your problem is that you used sudo which will do a gem installation for the system ruby. Try again without sudo, just
gem install nokogiri
to install gems for the current rvm ruby.

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

Ruby: What does 'require: false' in Gemfile mean?

Does this:
gem 'whenever', require: false
mean that the gem needs to be installed, or does it mean it is not required?
This means install the gem, but do not call require when you start Bundler. So you will need to manually call
require "whenever"
if you want to use the library.
If you were to do
gem "whenever", require: "whereever"
then bundler would download the gem named whenever, but would call
require "whereever"
This is often used if the name of library to require is different than the name of the gem.
You use :require => false when you want the gem to be installed but not "required".
So in the example you gave:
gem 'whenever', :require => false
when someone runs bundle install the whenever gem would be installed as with gem install whenever. Whenever is used to create cron jobs by running a rake task but isn't usually used from within the rails (or other framework if not rails) application.
So you can use :require => false for anything that you need to run from the command line but don't need within your code.
require: false tells Bundler.require not to require that specific gem: the gem must be required explicitly via require 'gem'.
This option does not affect:
bundle install: the gem will get installed regardless
the require search path setup by bundler.
Bundler adds things to the path when you do either of:
Bundle.setup
which is called by require bundler/setup
which is called by bundle exec
Example
Gemfile
source 'https://rubygems.org'
gem 'haml'
gem 'faker', require: false
main.rb
# Fail because we haven't done Bundler.require yet.
# bundle exec does not automatically require anything for us,
# it only puts them in the require path.
begin Haml; rescue NameError; else raise; end
begin Faker; rescue NameError; else raise; end
# The Bundler object is automatically required on `bundle exec`.
Bundler.require
Haml
# Not required because of the require: false on the Gemfile.
# THIS is what `require: false` does.
begin Faker; rescue NameError; else raise; end
# Faker is in the path because Bundle.setup is done automatically
# when we use `bundle exec`. This is not affected by `require: false`.
require 'faker'
Faker
Then the following won't raise exceptions:
bundle install --path=.bundle
bundle exec ruby main.rb
On GitHub for you to play with it.
Rails usage
As explained in the initialization tutorial, the default Rails template runs on startup:
config/boot.rb
config/application.rb
config/boot.rb contains:
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
which does the require 'bundler/setup' and sets up the require path.
config/application.rb does:
Bundler.require(:default, Rails.env)
which actually requires the gems.
Whenever you specify a Gem in your Gemfile and run bundle install, bundler will go and install specified gem and load code for that Gem in you app by putting require 'whenever' this way bundler will load code for all of your Gems in your Rails app, and you can call any method from any Gem without any pain, like you do most of the time.
but Gems like whenever, faker or capistrano are something which you do not need in your app code you need whenever code in your schedule.rb file to manage crons and capistrano code in deploy.rb file to customize deployment recipe so you need not to load code for these gems in your app code
and wherever you want to call any method from these Gems you can manually require thsese gems by yourself by putting require "whenever" . so you put :require => false in your Gemfile for these Gems, this way bundler will install that Gem but not load code for that Gem itself, you can do it whenever you want by simply putting like require 'whenever' in your case.
Analogy to Explain
## Gemfile
gem "university_degree", require: false
gem "dealing_with_boss"
"dealing_with_boss" - loaded into memory and ready to go.
degree gem - not "needed"....you need to manually require it, in order to use it.
In order to require gems in your Gemfile, you will need to call Bundler.require.
You can prevent bundler from requiring the gem with require: false, but it will still install and maintain the gem. Check this out for a more detailed explanation.

ruby: code to install gem if missing

is there some ruby code I can use to install a gem from a local file, if that gem is not installed?
i'm thinking it would look something like:
if !gem_installed("some gem name")
system "gem install -l local_copy.gem"
end
i don't know if anything exists that lets me check for gems like this or not...
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})
This is my way of doing this
['json','date','mail'].each { |req|
begin
gem req
rescue Gem::LoadError
puts " -> install gem " + req
Gem.install(req)
gem req
end
require req
}

Resources