RubyGems
RubyGems is a system for managing Ruby software libraries. Ruby codepackaged in this manner is called a gem. When you find Ruby softwareyou want to use in a project, gems offer a means of downloading,installing and managing the software.
Ruby's connection with Perl caused converts to ask an obvious question“Where is the CPAN (Comprehensive Perl Archive Network) forRuby?”If you have done any Perl programming or used Perl software, youlikely have downloaded something from CPAN to make the software work. As itis the de facto standard for sharing libraries in Perl, access to CPANmakes it easier to re-use code developed by others. This tool allowsthe developer to focus on new problems instead of re-inventing the wheel.
As it turns out, package management is not as simple as it sounds. Itgets even more complicated when you are trying to solve a problem for avariety of platforms and operating systems (Ruby runs on a *nix/*BSD/MacOS X/WinX). There have been several attempts at building a working system.
Ryan Leavengood is credited with creatingthe very first RubyGems project back in 2001 (see the on-line Resources). The project got startedbut did not really gain enough momentum to take off. Other solutions wereattempted, but they did not really catch on enough to dominate the field.
In November 2003, Rich Kilmer, Chad Fowler, David Black, PaulBrannan and Jim Weirch got together at a Ruby conference and startedcoding. Their goal was to create a solution once and for all. Theyobtained permission to use the existing name RubyGems fromLeavengood, even though they did not use any code from the previousproject.
RubyGems set out to solve several problems. The focus was on simplifyingthe process of installing, removing, updating and managing Rubylibraries. The developers added an interesting twist by allowing the system tomanage multiple versions of the same library easily.Using the versioningscheme from RubyGems, it is possible to provide very powerful control overthe selection of which version of a library your code will actuallyuse.
There are plans to include RubyGems as part of the core distribution ofRuby, but until that happens, you need to install it. Your Linuxdistribution may have a package (RPM, Deb and so on) for RubyGems. In theevent that it does not, you can install it from source easily, assumingyou have Ruby and the development headers for Ruby already installed onyour Linux box.
You can do the following as a user:go torubyforge.org/projects/rubygems, anddownload the current version (0.8.11 at the time of this writing):
tar xzf rubygems-0.8.11.tgzcd rubygems-0.8.11
You must be root to install the software (assuming you want it to beavailable to all users):
ruby setup.rb all
Now that RubyGems is installed, you should have the gem command(gemis the command used to interact with the RubyGems package system).Test it out by running:
gem list
It should show a single package—sources (0.0.1) installed. The gemcommand is the way you interact with the RubyGems package system.
Now that you have the gem command, you can begin installing gempackages. You need to be root to install or modify gems, but anyuser can query the system to find out what is installed. When you wantto find software, you can always check out RubyForge (see Resources). Itis the main clearinghouse for Ruby open-source software.
One of the most popular RubyForge projects is Ruby on Rails. The Rails gem(and the gems it depends on) can be installed with the following command:
gem install rails --include-dependencies
Another very popular project is RMagick. RMagick is a useful Rubyinterface to ImageMagick (see Resources),and it can beinstalled with the following command:
gem install rmagick
This gem includes non-Ruby code. When you install it, it will compile theC code as part of the installation process. If you do not have compiletools installed, the installation will fail.
RubyGems features a number of useful features, including:
gem search rails --remote gems.rubyforge.org
This returns a list of all the packages and versions availableon RubyForge that have the word rails in the title of thepackage. Here are a few more, well, er, gems:
gem update: updates all the current versions ofgems to their latest version.
gem cleanup: removes old versions of gems that areinstalled.
gem uninstall:removes a given gem from the repository.
Because I try to keep up with the most current version of the gem software,I usuallygem update and thengemcleanup the repository to get rid ofold libraries. Doing this keeps the gems directory a little cleaner and makesit easier to sort through if and when you need to look in the directory.
Now that you have some software installed, you will want to use it. Toget started, you may want to read the documentation on the gems tolearn their API. If you have installed rdoc on your system, gemautomatically generates the rdoc (Ruby documentation) for all of the gemsyou install. You can view this documentation in two different ways.The first one is to run the command:
gem_server
This automatically launches a Ruby-based Web server on port 8808. You canadd the -p option to launch the server on a different port. This makesit easy for you to use your Web browser to browse the documentationfor all of the gems that are installed. The gem_server can be stopped bypressing Ctrl-C. Also, be aware that the server accepts connections fromall hosts that are able to connect to that port. So, if you are concernedabout opening a port on your server, you may want to try the alternatemeans of access.
The other way to access this documentation is to navigate to the place onthe filesystem where gem has generated it. In most cases, it will be in/usr/lib/ruby/gems/1.8/doc, but in the event that gem has been installedin a different path, you can ask gem where the correct directory is:
gem environment gemdir
This command gives you the base directory where gem isinstalled. The documentation is stored in the doc subdirectory of thatdirectory. When you access the files this way, you do not get the summaryoverview that you get from the gem_server; instead you get only a directorylisting of all the gems that are installed.
In order to make your Ruby scripts able to use the Ruby librariesyou have now installed, you need to use Ruby's require mechanismto load in the code. The simplest way to use RubyGems is to call thefollowing lines:
require 'rubygems'require 'RMagick'
This loads all the RubyGems code and automatically allows you to usethe latest gem version of RMagick that you have installed. If the codeis available locally, it will be included from there instead.
If you would like to tie your software to a specific version of thelibrary, a different function must be called:
require 'rubygems'require_gem 'RMagick' , '>=1.10'require_gem 'rake', '>=0.7.0', '<0.9.0'
These statements tell Ruby to use RMagick as long as it is greater thanor equal to 1.10. The second line allows any version of rake as longas it is greater than or equal to 0.7 and less than 0.9.0. The versionstatement supports a number of operators: =, !=, >, >=, <, <= and ~>.The last one is a special operator. It assumes that you are followinga RubyGems standard for versioning.
You increase X when you release a version that is incompatible withthe previous version. You increase Y when you release a version with a newfeature that is otherwise compatible. You increase Z when you releasea fix for the software.
This allows the ~> requirement to select within a special range. Sofor example:1.0, 1.0.1, 1.0.2,1.1 are all ~> 1.0, and1.1, 1.1.2 are ~> 1.1.
This lets you support minor changes in the gem version without havingto change the require statements in your code.
A word of advice: if you are putting in require statements that aretied to a version, make sure you have a central place for callingand organizing them. This will make it easier to determine what othersoftware you depend on and to adjust version requirements later whenthey need to change.
So far, gems have really been about using other people's software inyour code. If you decide you have a library that might be usefulto other people, you easily can package it up as a gem.
Now that you know how to use gems, you might want to know how tobuild them. The process of turning your code into a gem is a two-part process. The nice thing about that process is you do nothave to modify your code to make it available as a gem. The first partis getting your library set up in a directory structure that is suitablefor conversion to a gem. I'm going to be using an existing project calledIPAdmin (see Resources) as my example of how this works.
The directory structure is organized as follows:
/ipadmin/lib: this directory contains all of the Ruby code related to the project.
/ipadmin/pkg: this is where the gem will be generated.
/ipadmin/tests: this is where any unit or other tests should be stored.
/ipadmin/README: this file should contain a summary of theproject—especially the license under which it is being released (feelfree to add a separate file for the license).
This is the bare minimum layout you need to build up a gem.
More complex projects (rake for example) add the following directories:
/rake/bin: this is for any command-line scripts that are part of the project.
/rake/doc: additional documentation about the project.
This shows how some projects (rake, capistrano) are able to add in newcommand-line tools once they are installed on a system.
RMagick includes a special directory:
/RMagick/ext: this is where non-Ruby source should be stored if it isgoing to be compiled.
This is another power option. RubyGems supports shipping non-Ruby sourcecode in the gem. When the user installs this “source” gemon the destination computer, gem attempts to compile the extra codeas part of the installation. The advantage of shipping a gem this wayis that the non-Ruby code will bind to the actual libraries that areinstalled on the destination computer. This is exactly what happenswhen you install RMagick. If you do not have the proper libraries(ImageMagick) or a compiler, the install will fail. To get around theproblem of not being able to compile the code, it is possibleto ship a precompiled version of the gem. In this case, the source filesare compiled and then simply included in the gem.
Once you have your code set up in the correct directory structure, youcan focus on the other part of the process of gem building—the gemspecification. This is basically a manifest that gives gem all theinformation it needs about the gem being built. You can builda gem spec as a standalone file, but it is easier to work with if youmake it a Rakefile. This simplifies the building process.
There is a Rakefile in the main directory of IPAdmin:
require 'rubygems'Gem::manage_gemsrequire 'rake/gempackagetask'spec = Gem::Specification.new do |s| s.platform = Gem::Platform::RUBY s.name = "ipadmin" s.version = "0.2.2" s.author = "Dustin Spinhirne" s.email = "dspinhir @nospam@ yahoo.com" s.summary = "A package for manipulating IPv4/IPv6 address space." s.files = FileList['lib/*.rb', 'test/*'].to_a s.require_path = "lib" s.autorequire = "ip_admin" s.test_files = Dir.glob('tests/*.rb') s.has_rdoc = true s.extra_rdoc_files = ["README"]endRake::GemPackageTask.new(spec) do |pkg| pkg.need_tar = trueendtask :default => "pkg/#{spec.name}-#{spec.version}.gem" do puts "generated latest version"end
This is a good example of a standard Rakefile for a gem. Here you cansee that it is including RubyGems and adding some tasks from rake.The main spec handles providing all the information about the gem thatis being built. The last task adds a simple helper that allows you torun rake in the directory and automatically build a gem.
Each of the lines in the spec has a special meaning. Theentire list of options that can be set is available from the GemspecReference on the RubyGems Manuals site(see Resources).
platform determines for what platform the gem is meant. If you are justusing pure Ruby, it can stay with this default. This flag becomesvery important when you are shipping precompiled gems.
name, version, author, email and summary provide basic informationabout the gem and its author. This is how users can find out who isresponsible for the code.
files defines the list of files that are to be included in the gem. TheFileList command is provided by rake, which does two things that makelife easier. First, it handles globs (*) and patterns meaning that youcan grab a lot of files easily. It also understands that certain filesshould be excluded. By default, it excludes CVS, svn, bak and core files.
require_path is set to determine what directories should be searched forcode. The value for this would change if you were building extensionsin the ext.
autorequire designates which file will be loaded whenrequireipadminis called in code. ipadmin.rb in this module handles requiring the otherthree libraries that ship with ipadmin.
test_files is a list of files that should be executed when the gem isinstalled if the user adds the -t argument to the gem install. This isa way to provide safety checks to make sure everything worked afterthe gem is installed.
has_rodc is a way to tell gem you have included rdoc tags in thecode. If this flag is false or missing, gem will notgenerate documentation automatically.
extra_rdoc_files allows you to include other files in the documentationthat is generated by gem. In this case, the README file is being linkedinto the document ion. If you had other documents, they could be listedhere.
Because IPAdmin is a very simple project, it does notinclude one very useful command: add_dependency. If you build a gem thatdepends on another gem, this command allows these dependencies to bespecified. You even can tie it to a version number in the same way you canwith require_gem. When you install a gem that has a dependency, gemchecks to see if it is met. If it is not met, gem offers to installit. To add a dependency on rake, you could add this to the spec definition:
s.add_dependency("rake",">=0.7.0")
Thanks to a patch from Paul Duncan, the latest version of RubyGems(0.8.11) now has some features to support signing your gems usinga public/private key. This introduces some new options for the gemspecification (signing_key and cert_chain). This change also allowsyou to install gems in a high-security mode that will installonly gems that are signed by trusted sources. Because the feature itself isvery new, some pieces of infrastructure to make it usefulin the greater scheme of things are missing—namely, an easy way to buildup a chain of trust so that end users do not have to add certificatesfor every single gem author out there. That being said, these featuresmight be useful if you want to control gems inside your network acrossa lot of servers. You could download them once and sign them withan internal certificate. Then, you could update all your servers byrequesting gems from the server where you distribute these signed gems.Duncan has written a great overview of getting started with gem signingon the RubyGems Manuals site (see Resources).
Now that you have a gem, you probably want to share it. There areseveral ways to distribute your code. The simplest way is to hostthe file. When people want to install it, they can download the fileand run gem in the same directory.
The second option is to host the project at RubyForge.org. RubyGemsships with RubyForge as the default source for gems. RubyForge even runs aspecial script so that once you upload your new gem to your account, itautomatically is available to all users of RubyGems.
Assuming you do not want to use RubyForge, there are two optionsleft to make it possible to distribute your gem via RubyGems. First,you need to run your own server. The easiest way to do that is tosimply fire up gem_server. It automatically shares gems with anyonewho connects to it.
The other option is tocd to a directory inside of the webroot of an existing Web server.Create a directory called gems, and copy all the gems you want to distribute into that directory.
Run the following command, and replace DIR with the full path to thedirectory above the gems directory. This creates yaml and yam.Z files:
generate_yaml_index.rb -d DIR
You need to re-run the script anytime you modify the gems you areserving. Keep in mind that if you use either of these options, yourusers have to add the --source URL_OF_YOUR_SITE to the gem installcommand. This allows gem to search that site for gems.
RubyGems is a package management system unto itself. If your system doesnot already have package management, this is a huge improvement. On theother hand, if your Linux system has package management, RubyGems canadd some complexity. This is largely a side effect of RubyGems beingcompletely separate from the host packaging system. According to theRubyGems Web site, the problem is related to the version-per-directorylayout. This apparently conflicts with the Filesystem Hierarchy Standard(see Resources). Hopefully, some sort of middlegroundwill be found, because the joy of having a good package management systemis having a single place to make sure everything is up to date and worksproperly together. The risk is really related to gems that installnon-Ruby code. For example, I believe it is possible to install a gemand then have the host package system replace a shared library that ismanaged by the host system with an incompatible version, which wouldrender the gem useless.
In the long run, I hope that someone comes up with a good solutionto the problem. So far, I have not been affected seriously by thispotential issue. I use apt to manage Ruby and the rest of the system,and I use RubyGems to manage the gems I need. The one problem I had wasmore related to user error. I failed to install a library that RMagickrequired. The compilation of the RMagick extension failed, but I didnot see the error because it scrolled by too fast, and the gem reportedthat it was installed. Eventually, I figured out what was going on, andno computers were harmed in the process. It could be argued that thisproblem may have been prevented if I were doing everything in apt, becauseit would have installed the missing library as soon as I installed RMagick.On the other hand, because a lot of the Rails and other Ruby gems seem tobe updating frequently, it has been nice to be able to keep up with thelatest version of the Ruby software instead of having to wait for newDebs to be released.
Package management for Ruby got off to a rocky start. Now that we haveRubyGems, it is hard to imagine working without it. RubyGems crams alot of features into a very tiny package. It has made it a lot easier tofind, distribute and manage a wide variety of Ruby software. Now thatyou have made it through this brief introduction, you can start usinggems in your own development.
Resources for this article:/article/9019.
Dirk Elmendorf is one of the founders of Rackspace Managed Hosting(www.rackspace.com). He is currently addicted toRuby on Rails, and bythe time you read this he will be happily married to Annie Tiemann!