ruby gem statement - what does it do? - ruby

I think I have a basic understanding of what require/include statements at the top of a ruby script are doing, like
require 'rspec'
These statements are easy to google and find relevant results. But sometimes I have seen a gem statement like
gem 'rspec'
What does this line do?

In ruby code, gem(gem_name, *requirements) defined in Kernel tells Ruby to load a specific version of gem_name. That's useful when you have installed more than one version of the same gem.
For example, if you have installed two versions of rspec, say 2.12.0 and 2.13.0, you can call gem before require to use specific version. Note that gem should come before the require call.
gem 'rspec', '=2.12.0'
require 'rspec'
A gem 'gem_name' without version uses the latest version on your machine, and that's unnecessary. You can call require without gem to get the same behavior.
And besides, in Bundler::Dsl, gem is used to tell bundler to prepare/install specific version of ruby gems. You'll see that in Gemfile

The original behaviour of require, before Rubygems, was to search all the directories listed in the $LOAD_FILES variable for the file, and to load the first one it finds that matches. If no matching file was found, require would raise a LoadError.
Rubygems changes this process. With Rubygems, require will search the existing $LOAD_PATH as before, but if there is no matching file found then Rubygems will search the installed gems on your machine for a match. If a gem is found that contains a matching file, that gem is activated, and then the $LOAD_PATH search is repeated. The main effect of activating a gem is that the gems lib directory is added to your load path. In this way the second search of the load path will find the file being required.
Normally this will mean that the latest version of a gem that you have installed gets activated. Sometimes you will want to use a different version of a gem, and to do that you can use the gem method. The gem method activates a gem, and you can specify the version you want, but doesn’t require any files. When you later require the files you want, you’ll get them from the gem version you specified.

In Ruby, gems are packages with functionality that can be used out of the box (as libraries in other Programming languages).
The gems that you use with your Ruby Project can easily be managed with a tool called "bundler", just google it. The snippet of code you posted is part of the spec file that bundler uses to install and update all the libraries that you specify for your project.
If you are developing a Ruby on Rails, using gems an managing them with bundler is very common and so to say best practice.
Gems are just great because there are so many useful libraries that extend default functionality, eg of rails, and that you can use out of the box!
For a list of gems, visit rubygems.org

Related

What does it mean when a Ruby script starts with Gem?

I am working on learning Ruby and one thing that I have seen in several instances and cannot understand are scripts that start with the keyword gem.
An example can been seen in the Sensu code.
gem "amqp", "1.3.0"
require "amqp"
require File.join(File.dirname(__FILE__), "base")
I understand the require statement for accessing another gem, but what does the exact gem "amqp", "1.3.0" mean?
This is the gem method that Rubygems adds to Kernel (Rubygems is required by default in current Ruby versions). It activates a specific version of a gem (version 1.3.0 of the amqp gem in this case), meaning that the gems lib dir (or whatever dirs the gem specifies) is added to your LOAD_PATH, as are the lib dirs of any dependent gems it has.
It also checks that there are no version incompatibilities with any already activated gems.
All gems are activated when you use them. This normally happens when you call require. Calling gem activates the gem but doesn’t require any files from it, hence the line require "amqp" below (note the difference between amqp the gem, which the gem method refers to, and amqp the file, which is contained in the amqp gem and is what the require method is refering to).
This method is used to ensure you are using a specific version of a gem, without needing to use Bundler (or something similar). Bundler also has a gem method used in Gemfiles, but this is a different (but similar) method.
It’s also used to specify that you want to use the gem version of a library that is also part of the standard library (say if you want to use a more recent version). For example the Yaml library distributed with Ruby is Psych which is also available as a gem.

Two gems share same require?

When I call:
require 'retryable'
These two gems clash:
https://github.com/robertsosinski/retryable
https://github.com/carlo/retryable
as they both have a 'retryable' file they ask the user to require. I'm interested in using the first gem, however this doesn't always happen.
This code is executed as a part of my own gem, and it has to be reliable across all users.
Is there a way to require specifically from a gem (as the gem names are different of course)?
How do I resolve this naming conflict?
EDIT: To clarify, this is the official repo and the gem names are actually different ("retryable-rb" and "carlo-retryable"), however they both ask their users to require the lib/retryable.rb file with require 'retryable'
You can explicitly activate a specific gem with the gem method.
In this case you want the retryable-rb gem, and not any others that may have a retryable.rb file:
gem 'retryable-rb' # activates the gem in question
# and adds its lib dir to load path
require 'retryable' # loads retryable.rb from the retryable-rb gem, as it
# is now on the load path

Difference between gem and require (require open-uri)

I just wanted to understand for my self.
I'm using the nokogiri gem (for parsing HTML). If I got it right to open URLs I need to use a method from the gem 'open-uri'.
But when I include it in my Gemfile (on Windows developer's machine):
gem 'open-uri' - there is an error while bundle install that it can not find gem.
So If I use require 'open-uri' - its working.
So can some explain what is going on?
You're using bundler for your gem dependecies and you're doing it right but OpenUri is part of the Ruby standard library. That's why you only need to require it if you want to use it in your code.
require is used to load another file and execute all its statements. This serves to import all class and method definitions in the file. require also keeps track of which files have been previously required so it doesn't execute it twice.
A RubyGem is a software package, commonly called a “gem”. Gems contain a packaged Ruby application or library. The RubyGems software itself allows you to easily download, install, and manipulate gems on your system.
- What is a Gem?:
The Gemfile is then used by bundler to install the specified gems.
open-uri is not a gem but part of the Ruby Standard Library so it just needs to be required.

Managing conflicting versions of ruby gems

I am building a framework that loads user provided ruby code. It is basically a plugin mechanism. I want the user provide ruby code to be able to require gems of its own. I intend to have the "plugin" package include a vendor directory with the gems.
How can I load gems that are required by the plugin without having them conflict with my framework's gems? For example, if my framework uses treetop version 1.3.0, and a plugin uses treetop 1.4.2 I want each to work with their specified version.
Likewise, is there a way to prevent plugins from conflicting with each other?
I have looked at gem_plugin, _why's sandbox, and some other tools. But I don't see any library that specifically handles this case - I assume its been done before.
I have also looked at the internals of Bundler to see how it manages gem versions. I am prepared to do some pretty complex stuff if need be. But I am still uncertain of how to go about it.
I also have a lot of freedom in how I implement this. So if you think I am barking up the wrong tree, please say so.
Thanks for any advice.
SIDE NOTE: It occurred to me while writing this that I need something similar to the Classloaders in a Java servlet container. A WAR file can include jar files, and the web application's class loader will prefer those over the jars that are on the global classpath. Is there any way in ruby to segment the ruby "classpath" (i.e. load_path, require, etc)?
To be blunt, you can't have two versions of the same gem loaded at the same time.
Bundler does a good (ish) job of looking through all of the required gems and finding a solution to the various overlapping dependencies, but even so it is limited to only loading one version of a gem at a time.
This leads to plugin developers constantly having to update to support any changes that are made in dependent gems in order to avoid just the situation you describe.
(Don't get me started on the screw up that results from the various competing JSON implementations and the pain you have to go through when you have several gem dependencies all requiring different ones.)
Respectfully disagree with the answer above. Here is how I do it:
ruby -S gem list my_gem
`*** LOCAL GEMS ***
my_gem (1.0.1, 1.0.0, 0.0.2)
`
ruby -S gem lock my_gem-1.0.0 > locklist.rb
which generates list of dependencies for a specific version into locklist
require 'rubygems'
gem 'my_gem', '= 1.0.0'
gem 'gem_base', '= 1.0.0'
gem 'rest-client', '= 1.7.2'
gem 'savon', '= 1.1.0'
gem 'addressable', '= 2.3.6'
gem 'mime-types', '= 1.25.1'
gem 'netrc', '= 0.11.0'
now you can do load('locklist.rb') which will load a specific version of a gem along with its dependencies. Look ma, no Bundler.

Ruby: How to include/install .bundle?

I'm new to Ruby. I have a .bundle file. I put it in the source folder and did
require('my.bundle')
But when I call the methods in the bundle, the definition is not found. Do I have to install them or include them in some other way to access them?
I am on Ruby version 1.8.7 (latest version on Mac).
I highly recommend using RVM to manage your Ruby installation, including your gems, so if you don't already have that, get it and follow the instructions for installing it. Make sure you do the part about modifying your bash startup script or you'll see weird behavior, like the wrong Ruby being called. Also, use the steps in "RVM and RubyGems" to install your gems or you can run into weird behavior with gems being installed under the wrong or an unexpected Ruby.
Second, use the gem command to install gems:
gem install gem_to_install
replacing "gem_to_install" with the name of the gem you want, and it will be installed into the appropriate gem folder for your Ruby.
If you are on Ruby 1.92, and trying to require a gem to use as a module in your code, use:
require 'gemname'
if it is installed via the gem command. And, if it is a module you wrote or have in your program's directory or below it, use:
require_relative 'path/to/gem/gemname'
If you are on a Ruby < 1.9 you'll also need to add require 'rubygems' above your other require lines, then use require './path/to/gem/gemname'.
Thanks, but my .bundle is not in gems. How do I install/require a .bundle file I already have?
If you wrote it look into rubygems/gemcutter or bundler for info on bundling and managing gems.
You can install a gem without using the app by going into the directory containing the gem and running setup.rb. See http://i.loveruby.net/en/projects/setup/doc/usage.html for a decent writeup or the official docs at: http://docs.rubygems.org/read/chapter/3

Resources