POSTS

Creating a Ruby Gem for the Uninitiated

Blog

Introduction

One of the problems with gem authorship is that the various tools for gem generation all seem to stomp upon one another and seem to have varying states of freshness. Toss into this the questions about whether a given gem-generation framework eases the sharing via Gemcutter (or is it still pointing to Github?) and it’s a confusing start for the uninitiated.

I will detail the process that ultimately got me to re-structure some old code of mine as my first gem.

Getting started

After an IRC discussion, radar pointed me to using bundler to accomplish a skeletal gem layout. Both I as well as other IRC lurkers were surprised by this recommendation. The popular opinion up to that time seemed to hold that bundler was a much more heavyweight or was a deploy time dependency tool. The bundler guide I was pointed to explains the use of bundler to get the skeletal layout.

An abridged version looks like gem install bundler && bundle gem new_gem_name, edit your gemspec, edit your code, gem build when it’s good to go.

Limited Raking

You can now run rake -T and see the skeletal set of tasks.

rake build    
rake install  
rake release

What about running tests, locally deploying, or clobbering old gems? To do that we need to make the Rakefile mas macho. Normally this would entail writing several Rake tasks. Fortunately the gem-this gem makes this augmentation easy.

Gem This

After getting and installing gem-this, you can, from within the bundle-generated directory, run gem this:

$ gem this

Appended to existing Rakefile

Now

rake -T

gives you many additional tasks:

build
clean
clobber_package
clobber_rdoc
gem
gemspec
install
package
rdoc
release
repackage
rerdoc

YAGNI

The odd thing was that I didn’t really need those additional tasks. At the end, all I wound up keeping was the rdoc tasks. My Rakefile wound up looking like:

require 'bundler'
require "rubygems" 
require "rake/rdoctask" 

# Add the bundler tasks, this comes because I used `bundle gem`.
Bundler::GemHelper.install_tasks

# `gem this` tasks

# Generate documentation
Rake::RDocTask.new do |rd| 
rd.rdoc_files.include("lib/**/*.rb")
rd.rdoc_dir = "rdoc" 
end   

Ignorance Is Bliss

I would also add that it’s important to make sure you have a .gitignore in place when you start development. This was generated by bundler in the first step automagically.

Write Some Code

Once you have completed these steps, you are ready to get coding.

Jeweler

Another tool in the ecosystem is Jeweler. When I initally started this work, I was unsure as to whether the bundler approach was better than then jeweler apporoach. The main differece seems to be in the approach to the gemfile. Bundler integrates tightly with git. Assuming git’s presence allows bundler to derive things that, in a jeweler-generated gemspec, require explicit definition.

In a bundler gemspec:

s.test_files    = `git ls-files -- {test,spec,features}/*`.split("\n")</code>

Which means that it’s deriving a file list based on the SCM, that’s nice.

versus

a jeweler gemspec

s.files = [
".document",
"Gemfile",
"LICENSE.txt",
"README.rdoc",
"Rakefile",

As you can see there’s a difference in approach. At the end of the day, the approach that works for me is to use bundle gem and then to add the following methods into the Rakefile.

# Generate documentation
Rake::RDocTask.new do |rd| 
rd.rdoc_files.include("lib/**/*.rb")
rd.rdoc_dir = "rdoc" 
end

#Added to get testing working
require 'rake/testtask'
Rake::TestTask.new(:test)