Gem Sawyer, Modern Day Ruby Warrior
This guest post is contributed by Nick Quaranto, a web developer at Thoughtbot in Boston, MA. Nick maintains RubyGems.org and he’s a proud to be a part of the Ruby community. He cut his teeth on classic ASP and ASP.NET at first, but discovered Ruby on Rails through his university and dove in head first. Nick pretends he’s a bassist with famous prog rock bands when not coding.
You’re using RubyGems on a daily basis when programming with Ruby, but how do they work? Gems are now a ubiquitous part of the Ruby developer’s toolkit. If you’re fresh to Ruby and haven’t really learned what RubyGems can do for you yet, you’re about to find out!
What’s in a gem?
Ruby! Lots of it. Gems are just a simple format for publishing and sharing Ruby code. Let’s explore a simple one, and one of my favorites, jekyll. First, let’s see if the gem exists. We could go to RubyGems.org to do this, but the
gem command has a lot of built-in searching commands.
% gem list jekyll -r *** REMOTE GEMS *** jekyll (0.7.0) jekyll-epub (0.0.3) jekyll-localization (0.0.6) ....
This command will ask the remote source (the
-r flag specifies this) if there’s any gems available under that name. By default this looks at RubyGems.org, but it could be any URL. Next, let’s get the gem on our system:
% gem install jekyll Successfully installed jekyll-0.7.0 1 gem installed % gem unpack jekyll Unpacked gem: '/private/tmp/jekyll-0.7.0'
These two commands downloaded the gem and unzipped it from a compressed state in a new folder from my current directory. The directory structure here is common to all gems. I’ve ignored a lot of the files here, but here’s the basics of the internals.
jekyll-0.7.0 % tree -L 2 . +-- History.txt +-- LICENSE +-- README.textile +-- Rakefile +-- bin ¦ +-- jekyll +-- features +-- jekyll.gemspec +-- lib ¦ +-- jekyll ¦ +-- jekyll.rb +-- test
There’s a few administrative files here, such as the
README and code license. Hopefully you’ll see those in every gem, along with a decent changelog or history. Most gems have a
Rakefile which can run tests and perform other automation tasks as well. This gem is pretty well tested, so the test files are included in both the
test directory for shoulda and a
features directory for cucumber tests.
The meat of a gem really begins with
lib directory. RubyGems really just manages your Ruby load path, or how your ruby code is found by the
require statement. When you
require a gem, really you’re just placing that gem’s
lib directory onto your
$LOAD_PATH. Let’s try this out in IRB and get some help from the pretty_print library included with Ruby. Passing
irb will automatically
require a library when loaded.
% irb -rpp >> pp $LOAD_PATH ["/Users/qrush/.rvm/rubies/ree-1.8.7-2010.02/lib/ruby/site_ruby/1.8", "/Users/qrush/.rvm/rubies/ree-1.8.7-2010.02/lib/ruby/site_ruby/1.8/i686-darwin10.3.1", "/Users/qrush/.rvm/rubies/ree-1.8.7-2010.02/lib/ruby/site_ruby", "/Users/qrush/.rvm/rubies/ree-1.8.7-2010.02/lib/ruby/vendor_ruby/1.8", "/Users/qrush/.rvm/rubies/ree-1.8.7-2010.02/lib/ruby/vendor_ruby/1.8/i686-darwin10.3.1", "/Users/qrush/.rvm/rubies/ree-1.8.7-2010.02/lib/ruby/vendor_ruby", "/Users/qrush/.rvm/rubies/ree-1.8.7-2010.02/lib/ruby/1.8", "/Users/qrush/.rvm/rubies/ree-1.8.7-2010.02/lib/ruby/1.8/i686-darwin10.3.1", "."]
By default we have just a few system directories on our load path and the Ruby standard libraries. If we were to run
require 'rake' right now, it would fail, because RubyGems isn’t loaded yet.
% irb -rpp >> require 'rake' LoadError: no such file to load -- rake from (irb):2:in `require' from (irb):2 >> require 'rubygems' => true >> require 'rake' => true >> pp $LOAD_PATH[0..1] ["/Users/qrush/.rvm/gems/ree-1.8.7-2010.02/gems/rake-0.8.7/bin", "/Users/qrush/.rvm/gems/ree-1.8.7-2010.02/gems/rake-0.8.7/lib"]
Once we’ve required rake, then RubyGems automatically drops the bin and lib directories onto the
$LOAD_PATH. Boom! The
bin directory is used for creating executables that use your gem’s code, such as
rake. These are completely optional and you could have multiple per gem if you wanted.
That’s basically it for what’s in a gem. Drop Ruby code into
lib, name a Ruby file the same as your gem (so for jekyll,
jekyll.rb) and it’s loaded by RubyGems.
lib directory normally contains only one
.rb file on the top directory, and then another folder with the same name as the gem with more code in it. Jekyll has plenty, and Rake does too. Before you go any further with this article, download your favorite gem,
gem unpack it, and take a peek around.
How do I make a gem?
Creating and publishing your own gem is simple thanks to the tools baked right into RubyGems. Let’s make a simple “hello world” gem, and feel free to play along at home! This is really as simple as it gets.
I started with just one Ruby file for my “hola” gem, and the gemspec.
% tree . +-- hola.gemspec +-- lib +-- hola.rb
The code inside of
lib/hola.rb is pretty bare bones, we just want to see some output.
% cat lib/hola.rb class Hola def self.hi puts "Hello world!" end end
The gemspec defines what’s in the gem, who made it, and the version of the gem. It’s also your interface to RubyGems.org, all of the information you see on a gem page (like jekyll’s) comes from the gemspec.
% cat hola.gemspec Gem::Specification.new do |s| s.name = 'hola' s.version = '0.0.0' s.date = '2010-10-03' s.summary = "Hola!" s.description = "A simple hello world gem" s.authors = ["Nick Quaranto"] s.email = 'firstname.lastname@example.org' s.homepage = 'http://rubygems.org/gems/hola' s.files = ["lib/hola.rb"] end
Look familiar? The gemspec is also Ruby, so you could wrap scripts to generate the file names and bump the version number. Once we have our gemspec, we have to build a gem from it. We can then install it locally to test it out.
% gem build hola.gemspec Successfully built RubyGem Name: hola Version: 0.0.0 File: hola-0.0.0.gem % gem install ./hola-0.0.0.gem Successfully installed hola-0.0.0 1 gem installed
Of course, our smoke test isn’t over yet: Let’s require our gem and use it!
% irb -rubygems >> require 'hola' => true >> Hola.hi Hello world!
Hola now needs to be shared with the rest of the Ruby community. Publishing your gem out to RubyGems.org only takes one command, granted you have an account on the site. Once you’re signed up, then you can push out a gem.
% gem push hola-0.0.0.gem Enter your RubyGems.org credentials. Don't have an account yet? Create one at http://rubygems.org/sign_up Email: email@example.com Password: Signed in. Pushing gem to RubyGems.org... Successfully registered gem: hola (0.0.0)
In just a few moments (usually a minute), your gem will be available for installation by anyone.
% gem list -r hola *** REMOTE GEMS *** hola (0.0.0) % gem install hola Successfully installed hola-0.0.0 1 gem installed
It’s really that easy to share code with Ruby and RubyGems.
Exit the warrior, today’s Gem Sawyer
With this basic understanding of the RubyGems ecosystem on your system, I hope you’ll be the next developer to share your creations with others on RubyGems.org. For a more detailed explanation of how to build and deploy a gem, check out this railscast.
Have you written any Ruby gems? Why don’t you share them with us? Let us know in the comments section of this post. Thanks!
Do read these awesome Guest Posts:
- An Introduction to Outside-in Development
- Ruby Forensics
- An introduction to eventmachine, and how to avoid callback spaghetti
- The Testing Mindset
- An Introduction to Desktop Apps with Ruby
- The Ruby movement
- Almost everything is an object (and everything is almost an object!)
- So… you’re new to Ruby!
- Incorporating Web APIs to spark computer programming exercises
- 14 Ways To Have Fun Coding Ruby
- Writing modular web applications with Rack
- How to Learn Ruby (or any programming language)