Building a Ruby Gem in 2015

I recently released “chandler”, a small Ruby gem for managing GitHub release notes. It started as an idea to scratch an itch, turned into a weekend project, and then into a full-blown open source Ruby gem. Here’s what I learned along the way.

I thought I knew the basics of publishing a gem. After all, I built airbrussh not too long ago, and had read the guides and an excellent book on the subject. But it is now halfway through 2015, and the tools are constantly changing and improving. My takeaways:

Use Bundler

Most Ruby developers know Bundler as bundle install and bundle exec, but it also has a powerful bundle gem command that generates a new gem from a good (and configurable) template.

I had used bundle gem before, but it surprised me how much the template has improved with the latest versions. To start a new gem project, make sure you install the latest version of Bundler first (1.10.5 as I write this):

gem install bundler

Then create the basic gem structure with options to your liking. Here’s how I started chandler (Minitest, MIT license, with a binary):

bundle gem chandler --test=minitest --mit --bin

Bundler can default to these options with the help of a ~/.bundle/config file. Here’s mine:

---
BUNDLE_GEM__MIT: 'true'
BUNDLE_GEM__TEST: minitest

Good documentation goes a long way

From past open source projects, I discovered that documentation is just as important as the code. With chandler I decided to start writing documentation first, and I’m really happy with how it turned out.

README.md. bundle gem will set you up with a skeletal README.md. Before writing any code, I tried “README-driven development”. That is, I pretended the gem was already done and I was explaining to a fellow developer why I created it, what it does, and how to get started; I even showed some code samples. RDD is supposed to help you think through your implementation strategy and produce a better design. It worked for me! Chandler’s README.md.

CHANGELOG.md. A surprising number of projects forget to include a change log. This is important! I found out about keepachangelog.com on a recent episode of the Changelog podcast and really took the suggestions to heart. Don’t forget to mention your version number strategy (hint: use Semver). Sample CHANGELOG.md.

CONTRIBUTING.md. This document is a great place to set expectations: Are unsolicited pull requests OK? Will code be rejected if it doesn’t meet style guidelines or have tests? Are there special instructions for setting up a development environment? Sample CONTRIBUTING.md.

GitHub reminder
If you put a CONTRIBUTING.md document in the root of your project, GitHub will automatically remind people of it whenever they create an issue or pull request.

Travis is fantastic

Travis is a continuous integration (CI) service that plugs nicely into GitHub. For small closed source projects, Travis can be a hard sell: pricing starts at a hefty $129/month. But for open source projects, I found that the service is free and too good to pass up.

For me, the benefits of Travis are twofold:

Travis
Every commit and PR gets a helpful pass/fail grade, courtesy of Travis.

Signup is as easy as logging in via GitHub, and to my delight, bundle gem had already set up my project with a .travis.yml. All I had to do was add another Ruby version to make sure Travis tested both Ruby 2.1 and 2.2.

language: ruby
rvm:
  - 2.1
  - 2.2
before_install: gem install bundler -v 1.10.5

Free code analysis is at your disposal

Travis is the big one, but there are other services free to open source projects that integrate nicely with Ruby projects on GitHub: Code Climate and Coveralls.

Under the hood these services are probably repackaging existing open source tools like RuboCop and Simplecov, but their simple setup and powerful integration with GitHub add a lot of value.

Badges
Don’t forget to add those badges!

Code quality reports with Code Climate. Code Climate analyzes your code to verify that it is generally well-written, well-factored, and not overcomplicated, assigning each class or module a letter grade. Your entire project earns a “GPA”, with the highest score being 4.0. I was already using RuboCop, but Code Climate adds a nice dashboard and additional analysis like code churn.

Code Climate
Straight-A’s!

Code Climate is a little less generous with its free open source tier: unlike Travis, Code Climate refreshes every few days, not on every commit or pull request. But you can always log into the Code Climate dashboard and trigger a manual refresh.

Installation couldn’t be easier: just sign into Code Climate with your GitHub credentials and enable it. No gem required!

Coverage reports with Coveralls. Code coverage statistics won’t tell you whether your tests are necessarily good, but they do alert you to sections of your codebase that are completely untested. This is especially important for an interpreted language like Ruby, which doesn’t have a compilation step to perform general sanity checks. Having code coverage close to 100% is therefore a good first step to ensuring that there won’t be unpleasant surprises at runtime.

Code Climate and Coveralls both provide code coverage reports, but Coveralls is easier to set up: no build configuration is necessary. (Code Climate requires you add a special token to your Travis environment.)

Pull request
As long as you are already running Travis, Coveralls will automatically run during your Travis builds and add code coverage checks to pull requests.

Basic installation is easy (just add it to the gemspec and invoke it at the beginning of minitest_helper.rb), but tuning Coveralls can get a little tricky. In my case I had to drop down to Simplecov’s configuration to get the reporting just right.

if ENV["TRAVIS"]
  require "simplecov"
  require "coveralls"

  SimpleCov.formatter = Coveralls::SimpleCov::Formatter
  SimpleCov.start do
    # No need to report coverage metrics for the test code
    add_filter "test"
  end

  # Eager load the entire lib directory so that SimpleCov is able to report
  # accurate code coverage metrics.
  chandler_lib = File.expand_path("../../../lib", __FILE__)
  at_exit { Dir["#{chandler_lib}/**/*.rb"].each { |rb| require(rb) } }
end

Plan the launch

After I cut my first release (by running rake release to tag and push to rubygems.org), there were a couple more things to do on “launch day”.

Rubygems metadata. The gem description and homepage URL comes from the .gemspec file in the gem itself, but there are few more pieces of metadata that must be supplied manually. I had to go to the rubygems.org site, log in, find my gem, and press the Edit link. This is where I was able to enter the source code, documentation, wiki, and bug tracker URLs (all pointing to GitHub in my case).

Spreading the word. I found that RubyFlow and Changelog Ping are two good ways to get the word out to fellow Ruby and open source developers. If you’re dreaming big, then check out the Mozilla Hacks article, How to Spread the Word About Your Code.

Don’t forget other platforms! If you’re like me, you use a Mac for development and Linux for deployment, which means Windows isn’t even on your radar. But there are Rubyists out there using Windows that would appreciate a gem that works on their platform, too. As you launch, make sure to double-check that your gem works in a Windows environment. You can even set up a Windows-based CI system; like Travis, this will automatically test compatibility for you!

Always be learning

I learned an incredible amount just by building and publishing my very simple gem. Not only did I pick up a bunch of knowledge about GitHub and its ecosystem of build tools and conventions, but I felt like I leveled up my Ruby skills as well.

If, like me, your day job is primarily Rails, then you may be surprised how different and fun Ruby can be when Rails is stripped away (require statements, remember those?).

So go ahead, publish a gem and keep learning. And if you need to maintain a change log, chandler can help you with that. I think you’ll like it!