Anatomy of A Ruby Gem
If you’ve ever installed a Ruby gem off of gemcutter or github, you’ll know that you run the gem install <gemname> command and when it’s completed you magically have access to use it through ‘require <gemname>’. If you’ve ever looked into the source you might want to have an idea of what goes where and how it all works.
First of all, I’m fairly new to this myself, so it’s very possible that I’ll miss something vital, please feel free to flame me in the comments, as long as you tell me where I messed up so I can fix it
Lets take a couple of examples. One is a gem for dissecting XML feeds that I just discovered, feedzirra, the other is a bit more complex, Cinch, the IRC micro-framework I was talking about a few days ago. Starting with feedzirra, here’s the listing of the files and directories in the root of the gem, I’ll take them one by one.
README.rdoc README.textile Rakefile feedzirra.gemspec lib/ spec/
The first two are pretty obvious, they are the README files with GitHub (and others) automatically parses out and displays when you hit the main project page. Here is a list of the README formats that github supports, as well the order that it will display them in. Here the author has included a README file as well as a README.textile (texttile is a simple markup language), and github displays the REAMDE.[something] first.
Rake, as you know is the ruby version of “make” the venerable Unix utility. Inside the Rakefile you find a formatted set of rules in the form of tasks and targets. Hit this tutorial for some more details. The short story is that there are targets (with prerequisite/dependency information) such as install, uninstall, and release, supporting functions if needed, and some boilerplate info (description, name, contact, etc). The targets can be anything it seems, for example Feedzirra has a task for releasing and uploading a new release, whereas cinch just has an ‘install’ task. Rake is used a lot to help the creation of gems, but I don’t think that it’s used in the actual installation.
Here is the meat of it all, the gemspec file consists of the control information for the entire gem. If you check out this tutorial you’ll see that it’s ruby code and can be created with a few utility scripts including Rake, and has some of the following information:
- Gem name and information (homepage, description)
- Author information (email, etc)
- Version and Date
- Other gem dependencies
- One or more boolean flags (ie: is there documentation that needs to be generated)
- A file list of what files need to be installed
- A list of executables of files that will be put in the bin directory when they’re installed
lib/ And Other Files or Directories
Basically what’s left are directories of files that are going to be installed on your system. Of course, “lib” isn’t always going to be there, but for most gems that are adding a new class into your ruby environment, it will be. The gemspec file has the list of the files and directories that will actually be taken out of the gem and installed into your system. There are different ways to specify these files in gemspec, as you’ll see by comparing the s.files directive in the Cinch gemspec and the Feedzirra gemspec.
Inside the lib/ directory there will generally be one or two main files. In Cinch you have:
This is the module that is loaded when you do a ‘require “cinch”‘ and the cinch/ directory has the supporting files. The cinch.rb module file has a bunch of requires of “cinch/irc”, “cinch/rbase”, etc which are the sub-modules that it uses. More on Modules vs Classes in a later post.
This will be the specs that the author has put in his source tree and includes so that other developers can look and use their tests. Again, this isn’t going to always be there, but it seems to be very in vogue to do this whole “testing” thing, and including your spec or test directory is The Right Thing To Do.
So when you run ‘gem install [foo]‘ where do the files go? Lets use Cinch again for this one as it’s fairly simple. Here’s a listing of the files that are in the source on github:
./LICENSE ./lib/cinch.rb ./lib/cinch/irc ./lib/cinch/irc/parser.rb ./lib/cinch/* [ more library module files ] ./examples/hello.rb ./examples/* [ more example files ] ./README.rdoc ./spec/irc/parser_spec.rb ./spec/* [ more specs ] ./Rakefile ./cinch.gemspec
(Note some of the directories are abbreviated).
When you do a gem install the files moved into the path defined in $GEM_HOME thusly:
cinch/lib => $GEM_HOME/gems/cinch-0.2.6/lib cinch/examples => $GEM_HOME/gems/cinch-0.2.6/examples [...] cinch/README.rdoc => $GEM_HOME/gems/cinch-0.2.6/README.rdoc => $GEM_HOME/doc/cinch-0.2.6/ri/* => $GEM_HOME/doc/cinch-0.2.6/rdoc/*
Nothing hugely interesting, files are basically just moved into your gem home directory under a directory named with the name and version of the gem you’re installing. Note that the doc/*/ri and doc/*/rdoc files are auto-created when the gem install runs, if you don’t pass the –no-ri or –no-rdoc flags of course. The internal magic in rubygems and ruby allow your programs to find the gems when they are ‘required’.
How to Examine A Gem
One of the nicest ways to play with these things, and what I did was to use RVM and then doing a gem install, as well as using git clone to pull down the source files from github. RVM, combined with it’s great gemset support, is a great tool and makes it really easy to add, remove, and play with no fear of mucking things up.
So that’s about it for the normal library gems that you’ll find on github. Hopefully you now understand a bit more about how a gem is structured and what happens when you install and use gems, and how they will interact with each other. I found lots of resources out there, including some great stuff on how to create your own gems, gemspec files, and so on, listed below.