Writing Ruby Gems
This article is about building a gem from scratch.
The recommended way to create a gem today
is to use bundler’s
bundle new <gemname> command.
This article is meant to take
a closer look at what is generated by such tools
by building it by hand.
Using such tools will make it much easier to get started with a gem, but building a gem from scratch will help you figure out how exactly those tools organize your gem’s structure. Here, we will walk through creating a gem from scratch, without generating any code.
The gem we are creating here is called sudoku, and it is supposed to be able to solve… er… sudoku puzzles. But this tutorial isn’t about creating a sudoku solver, so we’ll not worry too much about the logic that goes into a sudoku solver and instead focus on how we will package our gem.
The gem name sudoku is obviously taken (by me) and you can’t publish the gem to rubygems.org with the same name. I will be using the name sudoku for our gem in the rest of the tutorial, but you can use some other gem name (sudoku-yourname, perhaps?) and replace “sudoku” by your gem’s name wherever applicable.
I’ve pushed the code for this project to github, so you can take a look at the latest code there.
1. Gem Specifications
The first thing to do when creating a new gem is to create a file called gemspec that contains information about the gem. The gemspec file will have the name of your gem, ie. my gemspec will be called sudoku.gemspec.
Create a directory for your sudoku solver gem and add a gemspec file. In your gemspec file, add the following code, replacing the values for name, authors, email and homepage:
Now that you’ve written the gemspec, build the gem using the “gem build” command:
This command will package your gem project into a gem file that can be used to install the gem. This file will have the structure gem_name-version.gem (with the gem name and version coming from the gemspec). Our sudoku gem is at version 0.0.0, so the gem file will have the name sudoku-0.0.0.gem. You can use this file to install the gem for your ruby installation with “gem install sudoku-0.0.0.gem”.
You can check that the gem is available by doing:
If you check the gem installation folder for the gem and you’ll see that there are no files in it. To find the location where the gem will be installed, do “gem env” on the terminal:
Go to the path shown as INSTALLATION DIRECTORY, and within that navigate to gems directory. Here, you will find your new sudoku gem in the directory sudoku-0.0.1. This directory will be empty now because we haven’t yet written any code for our awesome sudoku solver.
Now that we have created a gemspec, used it to build our (empty) gem and even installed it, you can sit back for a few minutes and bask in the glory of your awesome new gem. (Let’s ignore the fact for now that it does absolutely nothing. After all, eventually our gem will be able to solve any sudoku you throw at it.)
2. Adding some code
In the previous post, we saw how to set up the gemspec and also installed the empty gem into our rubygems directory. Now it’s time we started adding some code to our gem.
Create a directory called lib/ and save a file sudoku.rb with the following code:
It’s not much code, and it doesn’t do much, but we have to start somewhere. Now build and install the gem as we did in the previous part of this tutorial:
If you check the installation path for your gem in the rubygems folder, you will still find it empty. This is because rubygems doesn’t know what files to package into our gem. To fix that, let’s tell the gemspec what files need to be added.
Build and install the gem again, and this time you’ll find lib/sudoku.rb in the gem installation directory.
Gem authors often use a version.rb file to store the version information. It’s always prudent to use a separate directory within the lib directory to put your code. This is because
require 'yourgem' causes rubygems to add your gem’s lib/ to the load path. Now every require statement will also look at the files in your lib/ and it’s possible that the names might clash with some other gem.
The convention is to have a directory within lib/ with the same name as the gem. This way only sudoku.rb will be loaded from the load path and we can safely
require 'sudoku/version' within sudoku.rb to access version.rb.
3. Publishing to Rubygems.org
Rubygems.org is the place where most Ruby gems are hosted. Whenever you do “gem install some_gem” you’re most likely installing a gem hosted on rubygems.org. Rubygems makes it incredibly easy to make your gem available for anyone to install. Once we have pushed our sudoku gem to rubygems, anyone can install the gem with “gem install sudoku”.
If you don’t already have an account on rubygems.org, create one. Once you have an account, go back to your gem directory and do this:
That’s it! Your gem is now available for anyone to install from rubygems. go on, try installing it:
There… you’ve published your gem to rubygems.org!
Apart from rubygems.org, it’s always a good idea to share your gem’s code on github. Most Ruby programmers are on github and it makes it easy for people to contribute to your gem.
4. Setting up Test::Unit
So far in this tutorial we haven’t written any code that would be useful in solving Sudoku. The reason is that I didn’t want to start writing any code until we have a test framework set up for testing our code.
Test::Unit is the unit testing framework that ships with Ruby, so we will first set up our gem to run unit tests with the “rake test” command. However, I prefer another testing framework, Rspec, for writing the tests. (We’ll set up Rspec in the next part of the tutorial and then continue using that rather than Test::Unit for testing.)
We will write a simple method in the sudoku module that will return a string “Sudoku: version 0.0.0” (the version number will obviously have to be taken from lib/sudoku/version.rb).
We will put all our unit tests in a directory called test. We will now add a rake task called “test” to run all the unit tests. To create this, we will first need to create a Rakefile that looks like this:
Rake already provides a task called test, so we are making use of that and have configured the task to use the test/ directory with
t.libs << 'test'. We will also configure rake to make the test task the default when rake is run. Running “rake” without a task name would now be the same as running “rake test”. (In the next post, we’ll change this to run our Rspec specs rather than the unit tests.)
Now let’s add a test file test/test_sudoku.rb and add a silly test that we know will fail.
2+2 isn’t equal to 5, so this test should fail. Run the rake task:
Now change the assertion to make the the test pass.
If you try running rake without the task name, you will see that the output is exactly the same.
Now let’s remove the silly test and write a test that acually tests the
version_string method that we’re adding.
Now if you run rake you will get an error with the message:
"NameError: uninitialized constant TestSudoku::Sudoku". To fix this, we need to add the code for the version_string method in lib/sudoku.rb.
Now rake will run the test successfully. Let’s rebuild our gem and install it with the generated sudoku-0.0.0.gem file to see that it installs correctly.
However, we’re not done yet. If you check the gem directory in the gem installation path, you will see that our test/ directory is missing. To tell rubygems to include that code in the package, we’ll add the following line in the gemspec:
Rebuild and install the gem again and you’ll see the test directory in the installed gem path.
In the next part of the tutorial we’ll set up Rspec for testing the gem and along with that start adding some real code for the sudoku solver.
5. Setting up Rspec
In this part, we will replace Test::Unit by another testing framework, Rspec.
Test::Unit is a great testing framework, but I personally prefer using Rspec for its syntactic sugar. At the end of this part of the tutorial, we will have set up both Rspec and Test::Unit, so you can choose whichever framework you feel comfortable with.
First of all, let’s change the Rakefile to add the following two lines to the Rakefile:
This adds a rake task called ‘spec’ and you will be able to run the specs in the
spec/ directory. If you wish to run your specs as the default rake task instead of Test::Unit tests, replace
task :default => 'test' to
task :default => :spec.
The next thing we need to do is to tell rubygems that you want Rspec as a development dependency.
If anyone installs the gem using
gem install sudoku --development, Rubygems will install rspec when installing sudoku.
Now we need to add a spec directory and create spec_helper.rb before we start writing the specs. The spec_helper will look like this:
The Rspec.configure block will contain the options to configure Rspec, and we have used the color_enabled and formatter configurations.
Next, we’ll add a spec similar to the test we wrote using Test::Unit. Add the following code to a file sudoku_spec.rb:
rake spec (or even simply
rake, if you’ve changed the default task to rspec) and you’ll see the output looks something like this:
The passing spec will be displayed in green because we have set the color_enabled option to true. And, because we have set the formatter option to ‘documentation’, the documentation string ‘should return correct version string’ is also displayed in the output.
Let’s now build and install the gem, and check the directory where the gem is installed. You’ll see that the spec folder is missing from the installed gem. To include the specs in the package, we need to change the following line in the gemspec:
We have added the spec/ directory in addition to the test/ directory as the source of test files. Now if you build and install the gem again, the spec files will be present in the installed gem.
You now have the option of using Rspec or Test::Unit for writing the tests for the sudoku solver. You also have the option of not writing any tests, but try not to take that path if you are making a gem that lots of people might find useful. Nobody wants to use an untested library.