Create your first Ruby Gem and release it to Gemcutter

Apr 2010

A few days ago I was set off to create my first RubyGem. There are many resources on how to do this, but it took me a good while to gather all the information I figured I’d need for my application, so I’ve decided to gather my bit of knowledge in this article.

This article’s goal is kick start the creation of your first Gem. To make this experience more enjoyable, I’ve chosen to use a gem called Jeweler.

Note: I am by no means a “ruby-pro”. I have only created a single Gem, but I thought this article could be helpful to a lot of people, and thus I wrote it. If you have any corrections, questions, or suggestions please either email me at sirup@sirupsen.dk or comment below.

Preparing

.. for world domination!

I assume you already know a bit of Ruby, that you know what RubyGems is, and you have already downloaded a few gems, and used some of them in your work. Now your are simply seeking to create your own Gems. You are indeed in for a fun time, coding gems is lots of fun!

Before we can begin, install the Jeweler gem via RubyGems:

$ gem install jeweler

Jeweler is a tool to create the basic skeleton for your Gem, as well as managing the gem.

Creating your gem

.. with your mighty companion Jeweler

Once Jeweler is installed, you want to create your Gem skeleton. I'm going to create a simple Hello World gem for the sake of example, and later on explain a bit about how you could manage your own Gem (at the very least my 2 cents about how a gem should be done).

$ jeweler helloworld # Should be all small letters
	create	.gitignore
	create	Rakefile
	create	LICENSE
	create	README.rdoc
	create	.document
	create	lib
	create	lib/helloworld.rb
	create	test
	create	test/helper.rb
	create	test/test_helloworld.rb
Jeweler has prepared your gem in helloworld

Now your gem skeleton is ready! Let's get in there and check it out.

$ cd helloworld
$ ls
lib/  LICENSE  Rakefile  README.rdoc  test/

This structure might look familiar to you. (Assuming you are like me and have already stalked a few Gems' sources over at Github) Now I'll attempt to explain what these files and folders are.

lib/

This is where your application lives, this is where you'll probably spend the most of your time working on your gem. It is common to have a folder inside this folder called whatever your gem is called (in this example, that would be helloworld), in which your app. is split into a few files, for organizations sake. And then have lib/<gem name>.rb require these files (as /lib/<gem name>.rb is what is required by Ruby whenever somebody requires your gem in their own project).

My 2 cents on organizing stuff in here (skippable)

As said, I am in no way an expert. But this is how I would do it.

My first gem is a gem for a file storage service (Anyhub) which should do two things:

  • Create a library for easy Ruby interaction with Anyhub
  • Contain a small CLI for Anyhub based on it's own library

So I figured I would have a module, containing a few classes:

  • Upload (for uploading files to Anyhub)
  • Account (to manage ones Anyhub account)
  • Runner (to manage the CLI)

The Upload class would simply be able to use the account class, to check if an account was configured in a config file (f.e. account_config.yaml). If a config file was present, it would upload the file(s) specified in arguments to the script. Otherwise, it would return an error. Runner (the CLI) would respond to this error, allowing the user to type in his details so they could be used for the ongoing upload, as well as any following uploads (by saving the details to account_config.yaml via the Account class).

Now, I had made a perfectly good module. The only thing it needed was arguments send to Runner, which would activate it all. By doing a little research I figured if I created the directory bin/ and threw in a file here, this file would automatically be inserted into the installers own bin (f.e. /usr/bin on Linux if installed for all users). So I created the bin/ directory, and a file in here called anyhub with a Ruby shebang at the top. This file simply instanced the Runner class with ARGV.

Now this is just my little not-so-fancy gem theory. It's not exactly done this way (yet) because Anyhub didn't have an API at first - so I created the first version without the Account class, so it might not be exactly like this at the Github repo. just yet.

Anyhub gem @ Github.

LICENSE

Simply a file which contains the license for your project. By default MIT with Jeweler.

Rakefile

Rake configuration for your project. It is here you can define rake tasks, and configure your project (in terms of the name of it, dependencies, description and similar).

Readme.rdoc

The Readme file for your project. If you create a Github repo. it'll show up there. It is also "the index" for your Gems' documentation.

test/

It is here you create your tests.

Configuring your Gem

Remember a few lines ago, I told you how you could configure your Gem via Rakefile? As you might have already guessed, we're going to open that very file now, to configure our example Gem.

After rubygems and rake has been required by Rakefile we see some fancy code, and then something which looks like some configuration. This is indeed where we configure our example Gem. You mostly only need to configure the summary and description the first time, I did it like this:

begin
  require 'jeweler'
  Jeweler::Tasks.new do |gem|
    gem.name = "helloworld"
    gem.summary = %Q{I'm a helloworld gem! I like to hello the world.}
    gem.description = %Q{This is a fancy little test gem.}
    gem.email = "sirup@sirupsen.dk"
    gem.homepage = "http://github.com/Sirupsen/helloworld"
    gem.authors = ["Sirupsen"]
    gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
    # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/  20 for additional settings
  end
  Jeweler::GemcutterTasks.new
rescue LoadError
  puts "Jeweler (or a dependency) not available. Install it with: gem install   jeweler"
end

A little something more on configuring

.. which can be skipped.

If your gem has any dependencies, you can add them here. Dependencies in this context mean "gems which my gem depends on". You should add them, because if you do they are automatically installed along with your Gem whenever somebody tries to install your Gem. If you don't, they'll just get a good ton of errors when they try using your gem.

Do add dependencies, simply add this to your Rakefile:

gem.add_dependency "gem", "version"

For example, in my gem I used the Curb Gem, and therefore I added this to my Rakefile:

gem.add_dependency "curb", ">= 0"

(>= 0 just means "I don't care about which version of the Gem it is, as long as it's there", mostly because I couldn't find much version-specific documentation Curb, otherwise I would have done this properly.)

Let's add some sample code

Now it's time to add some code to our helloworld Gem. I simply open /lib/helloworld.rb, and add these few lines of code:

module HelloWorld
  def self.do
    "Hello World from the all mighty helloworld Gem!"
  end
end

So HelloWorld.do would return the string "Hello World from the all mighty helloworld Gem!". Great, so far, so good.

Version

In order to finish our Gem, we need a version file. Now because Jeweler is so awesome, we don't even need to use our editor to do this, simply execute the following command:

$ rake version:write

And the VERSION file is created. It's not that fancy though.

$ cat VERSION
0.0.0

But that seems correct. This is our first Gem build, so of course, the version is 0.0.0 as of now!

Install it!

Now you can install the Gem. It's very easy:

$ rake install
Password:
(in /home/sirup/Code/Ruby/helloworld)
Generated: helloworld.gemspec
helloworld.gemspec is valid.
WARNING:  no rubyforge_project specified
  Successfully built RubyGem
  Name: helloworld
  Version: 0.0.0
  File: helloworld-0.0.0.gem
Executing "gem install ./pkg/helloworld-0.0.0.gem":
gem install ./pkg/helloworld-0.0.0.gem
Successfully installed helloworld-0.0.0
1 gem installed
Installing ri documentation for helloworld-0.0.0...
Updating class cache with 1983 classes...
Installing RDoc documentation for helloworld-0.0.0...

Moment of truth

irb --simple-prompt
>> require 'helloworld'
=> true
>> HelloWorld.do
=> "Hello World from the all mighty helloworld Gem!"

(Note: If you are not using Ruby 1.9, you might need to require 'rubygems' before requiring helloworld)

Awesome, it works. I hope this has helped you towards creating your first gem. You are welcome to leave a comment, or contact me if you run into any trouble.

Further information

You'll find it all if you visit Jeweler at Github. Below is for quick reference.

Github

I advice you to commit all your code, and push it to Github. Makes it easy for other people to view the source, post issues, and participate in your project.

Github because it's sort of the standard for Ruby open source projects.

Releasing Gem at Gemcutter

If you feel like sharing your Gem to the world (and you probably do). Register an account at Gemcutter.

And now you are ready to release your Gem. Simply run:

$  rake gemcutter:release

To release your gem. (You can also release it at RubyForge instead if you wish so, see the Jeweler readme) You might be asked to sign in to your account, simply do so whenever prompted.

Workflow

.. taken directly from the Jeweler Wiki.

  1. gem install jeweler
  2. Create a new project and customize it, or configure an existing project
  3. Write good code, and commit it
  4. Bump the version with one of the rake tasks:
    • rake version:bump:patch 1.5.3 → 1.5.4
    • rake version:bump:minor 1.5.3 → 1.6.0
    • rake version:bump:major 1.5.3 → 2.0.0
    • rake version:write MAJOR=2 MINOR=3 PATCH=6 1.5.3 → 2.3.6
  5. Release it
    • rake release
    • Optionally release it to Rubyforge: rake rubyforge:release
    • Optionally release it to Gemcutter: rake gemcutter:release
  6. Go to #2