POSTS
Creating a Ruby Gem for the Uninitiated
BlogIntroduction
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)