Ruby with Steve Klabnik

Intro

There are a few key steps to building a gem. Here’s what we need to do:

Figure out what our gem needs to do.

Install git

Install Ruby

Generate the skeleton

Write some tests

Implement the code

Write some docs

Push to GitHub

Push to RubyGems

Tweet about it

Ten easy steps. Let’s do this!

Oh, one more comment before I start: I use vim to do all of my editing. Many people also use Textmate on Mac OS X, and Sublime is becoming increasingly popular. But I like vim. I don’t use any plugins, either. I do like cntrl-p. In general, I keep my tooling very vanilla; it helps when I pair, when I teach, and probably to tell you the truth I’m probably just lazy.

Anyway. Ten steps!

Figure out what our gem needs to do.

The most important part! Every gem needs some kind of raison d’être. Any time I have some Ruby code I think someone else might use, I try to extract it into a gem. This means I currently have push access to 38 gems, and I am a committer on many more. Oh well, everyone has to have a hobby…

In this case, we want to make a gem so that I can show you how I make a gem. So we’ve got a name, which is often the hardest part of making the damn thing. We’ll call it “how_i_start”. Even with just the name, we already have something to talk about: naming conventions. Ruby gems should be named with all lower case letters, and no punctuation other than _ and -. Use _ for separating words, and - to indicate an extension to an existing gem.

Not every single gem follows these conventions, especially if they are quite old. For example, activerecord should really be active_record. If you’re starting a new gem, don’t contribute to this confusion! You can find a slightly more full description of these naming guidelines on the RubyGems Guides.

So, we’ve got a name, but what should it do? Let’s make it do something very simple: we’ll include a simple executable that prints out a link to this article. That’ll be a very small amount of behavior, and also be relevant.

I’ve already written and released a gem with this name, so if you want to push your own version of the RubyGem, you’ll have to change it to something else. Sorry about that! Maybe try the extension convention for names, and call yours how_i_start-jonathan. Of course, only do that if your name is Jonathan, or he’ll be really disappointed when he reads this article. I’ve got your back, Jonathan!

Install git

Rubyists use git to manage versions. If you don’t like it, well… sorry. You’re gonna have a bad time. Everything in Ruby world assumes git.

Done. This is probably the easiest step in the whole thing. Well, maybe not. But one command is pretty easy.

You can’t skip this step. Later, our tools will assume that we have git installed. It’s a bit fascist, I’ll admit, but at least the trains run on time. :[

Install Ruby

We can’t make a gem without Ruby! It’s also essential to use some sort of tool to switch between different Ruby versions, as well. When a new version of Ruby comes out, we want all those new goodies.

There are a bunch of options here, but I prefer minimalism. I use ruby-build to install Ruby, and chruby (“chuh ruby”) to change between different rubys.

Oh, I should also mention that I use Linux almost exclusively. These instructions will also work almost unchanged on an Apple computer, if you happen to be like many Rubyists. If you’re on Windows, I highly recommend RubyInstaller. I don’t use a version switcher when I’m on Windows.

Let’s install ruby-build so we can build and install a Ruby. I wonder if I could have fit more ‘install’ and ‘build’ and ‘Ruby’ into that sentence. Anyway, it’s simple:

You tell ruby-build which version of Ruby to build, and where you want to put it. ~/.rubies is one of the places that chruby looks by default, and I like that each user on the machine can have their own Rubies. I mean, I’m the only person (other than the NSA, probably) that is using my laptop, but still, keeping it all local to your regular user is nice.

That should run for a while, and then you have a Ruby installed! Next, chruby. chruby is sweet because you’re able to type:

This means that I’ll always have 2.1.1 right at my fingertips. Of course, given that it’s a shell script, this is also how you use chruby. Just type the version after the name, and you’re good to go. Easy peasy.

That’s it for tooling! We’re all good to go. Now, let’s dig in to the gem-building specific stuff.

Generate the skeleton

Turns out that we’re barely gonna even need to do any setup, as there’s a tool that does it for us. Ruby is super-huge on convention, so that means we have pretty awesome tools. They’d be even better with static types, but what’cha gonna do?

To do the generating, we need to install Bundler. There are only two Rubists in the world who don’t use Bundler. I’m only half-kidding. Bundler’s main job is to help you deal with versions of the dependencies your application needs. But since it does that, it also comes with an awesome generator to help you make gems. It’s going to do 90% of the work for us.

Surprise! Your dependencies are in another castle. Bundler knows how to figure them out from our gemspec, which we’ll talk about in a moment. We don’t need to edit this file at all, it works just fine.

Rakefile

Rake is Ruby’s version of the venerable Make tool for building things. We don’t need to edit this either, as it already contains the stuff needed to do a bunch of cool things:

These three commands help us with making a gem. rake build will attempt to package up our gem. rake install will rake build, and then install it into our Ruby, so we can give it a whirl. Finally, rake release will actually release our gem. We’ll talk more about all this later.

LICENSE.txt

Rubyists almost exclusively love the MIT license, because it makes making money really, really easy. I say all kinds of controversial political things on Twitter, and everybody shrugs. As soon as I suggest that I might use the GPL, people lose their cool.

You should use whatever license you want. I won’t judge you. Everyone else might, though. :/.

README.md

Bundler gives us a pretty okay README to start with. We’ll modify this more soon. The md stands for Markdown, the One True Document Format.

.gitignore

Bundler is kind enough to make sure to create a decent ignore file for git so that we don’t check bad things in. There’s one thing that’s different when you’re making a gem, and that’s the Gemfile.lock. Normally, if you were building an app, you’d check this in, but when you’re making a gem, you don’t. If you don’t know why this is, go read this.

how_i_start.gemspec

This file specifies all the metadata for our gem. The default values are decent, but I’m going to edit them, and then show you the output.

The first part sets up loading paths. The real meat is in the block. It’s all pretty basic stuff. The second of the three blocks is kinda interesting, but you never need to touch the generated files. Basically, they use Git Magic (tm) to figure out which files should be included in your gem. This means that you’ll never accidentally distribute the wrong files, unless you forget to commit them. And if they’re not committed, they don’t exist. You know it’s true.

The require_paths line is a Ruby convention: all the files for your library go in lib.

The last two are interesting, too: they say that in order to work on our gem, we need bundler and rake. If we were using a special test framework, we’d add it here. add_development_dependency has a sibling method that we won’t use, but I feel like I should tell you about: add_dependency. If our gems needed other gems to work, we’d use that to add them, here. These two methods are what Bundler uses to figure out what to install when we’re working on our gem, and what Rubygems uses to figure out what to install when we’re installing our gem. TL;DR: they’re super important.

Whew! That file is the most important, as it tells us where everything else goes. Let’s move on.

lib/how_i_start.rb

As I mentioned, this file is in lib because that’s where our files go. This file has the same name as the gem, so it’s the file that gets required when you say require "how_i_start" in a Ruby program. Very important.

lib/how_i_start/version.rb

By keeping this constant in its own file, we can not load up our entire gem when we need to check what version it is. Careful readers will notice that our Gemspec did this, exactly.

You’ll edit this file when you’re about to release a new version of the gem.

Write some tests

Step four! Every Rubyist except for DHH believes in test-driven development, so we’ll write a test first. This test will be very, very simple.

I prefer to use minitest for testing, as it’s included with Ruby. I don’t find the extra complexity of other testing frameworks particularly worthwhile, though I will say that RSpec’s mocking framework is kinda nice.

That first line was there from Bundler. It’s what made the previous three tasks we discussed. You have to add the rest. The first line requires the necessary stuff from Rake. The second instantiates the task. The line in the block tells the task where to find our files. Finally, we set our default task to run our tests.

Easy enough! We start off by requiring the test runner, requiring our library, and then we make a class to hold our test. One method, starting with test_, is our test itself. We have one simple assertion, which checks that we’ve set a constant to the URL of this post. Nice and easy.

The most important part of TDD is to run your tests and watch them fail. Here we go!

RDoc can tell us if we’re missing any documentation. Awesome. I always open up the HTML docs in my browser to see if they look okay.

Push to GitHub

Rubyists assume you use GitHub. It was originally created by some Rubyists, lots of early users were Rubyists. If you like a different code hosting platform, sorry. :/. I can only think of one gem that I use that doesn’t use GitHub.

Make a new GitHub repository with the same name as your gem, and then use git to push it up:

We just increase the version, commit it, and then run the Rake task that Bundler gave us. Since this isn’t my first time, it uses my saved credentials, but it might ask you for yours.

Tweet about it

If a gem gets released in a forest, and nobody is there to hear it, it certainly… yeah okay, that didn’t really work out. My point is, if you make a gem, and nobody knows about it, then it’s not very useful. Promotion is hard, but there is an answer: Twitter. Even people that hate Twitter post stuff to Twitter. It’s just the way of the Ruby world.