I’m not consistent in the way I structure my RubyGems, and I want to be so that I know what I need to provide, and so that people who use my code know what to expect.

Here are a few guidelines for my future self.

Require statement is based on Gem name

I should know, from the name of your gem, which file I need to require to be able to access the core functionality that your gem provides.

  • A gem called nyan_cat should need a require like this: require "nyan_cat".
  • A gem called nyan-cat should need a require like this: require "nyan/cat".
  • A gem called nyan_cat-moar_cats should need a require like this: require "nyan_cat/moar_cats"

File structure

A basic project structure for a gem should look like this:

your-rubygem/
            |- bin/
            |- lib/
            |- tests/
            |- Gemfile
            |- Rakefile
            |- README
            |- LICENCE
            \- your-rubygem.gemspec

The code that provides the functionality for your gem should be written under lib/ in a directory named according to these rules:

  • A gem called nyan_cat: lib/.
  • A gem called nyan-cat: lib/nyan.
  • A gem called nyan_cat-moar_cats: lib/nyan_cat

There file required by “require statement is based on Gem name” should appear under lib/ eg:

  • A gem called nyan_cat should have this file: lib/nyan_cat.rb.
  • A gem called nyan-cat should have this file: lib/nyan/cat.rb.
  • A gem called nyan_cat-moar_cats should have this file: lib/nyan_cat/moar_cats.rb

This file should require everything needed for your Gem to work.

The Gemfile should contain just gemspec, and the Gemfile.lock should not be checked in for Gems. More on the role of Gemfile in gems at http://yehudakatz.com/2010/12/16/clarifying-the-roles-of-the-gemspec-and-gemfile/.

Code structure / Gem namespace

Your gem should have a namespace that matches the directory structure:

  • A gem called nyan_cat should have this namespace: NyanCat.
  • A gem called nyan-cat should have this namespace: Nyan::Cat.
  • A gem called nyan_cat-moar_cats should have this namespace: NyanCat::MoarCats

Everything should live under this namespace.

Versioning

Provide a file, version.rb, which contains the current version of your Gem and nothing else. Be nice to people who use your Gem, stick to the Semantic Versioning scheme.

  • A gem called nyan_cat should have this file: lib/nyan_cat/version.rb.
  • A gem called nyan-cat should have this file: lib/nyan/cat/version.rb.
  • A gem called nyan_cat-moar_cats should have this file: lib/nyan_cat/moar_cats/version.rb

An example version.rb for nyan_cat/moar-cats looks like this:

module NyanCat
  module MoarCats
    VERSION = "0.0.1"
  end
end

Tests

Your code should be tested, but you don’t need to distribute the tests in the Gem file.

Logging

Unless you are providing a logger implementation, it is not your responsibility to configure logging. Logging is good, and can be very useful while debugging, so the solution is not to avoid logging. What I want to do is give your code a logger that I have configured to my liking. It will support the Logger interface. Please make the logger you use an option in your code, let me pass my logger to you, stop worrying about logging configuration.

You can easily allow this by using the NullLogger if no logger is passed to your code:

require "null_logger"

class Foo
  attr_accessor :logger
  private :logger=, :logger

  def initialize bar, options = {}
    self.logger = options[:logger] || NullLogger.instance
  end

  def quux
    logger.info "Called #quux"
  end
end

Find out more about the NullLogger at http://github.com/craigw/null_logger.

Dependencies

Read about your dependencies versioning schemes. Hopefully they use the Semantic Versioning scheme in which case you should depend on the appropriate version. Read about using the “tilde wakka” operator to depend on major, minor or exact versions of gems depending on their versioning scheme.

Rake tasks

Provide tasks to run your tests. The default rake task should run all tests.

README

Provide these things:

  • A brief description of the gem, perhaps with an example of the problem it solves
  • Installation instructions, even if they’re just gem install foo or “stick X in your Gemfile”
  • A brief example of using your gem, possibly with a link to more in-depth documentation
  • Some licencing info, even if it’s just “see the LICENCE file”
  • A “how to contribute back to this project” section, which tells people how to submit patches for your gem
  • A list of authors (it’s nice to see your name here)

Licencing

If you don’t provide a licence, I can’t use your project, because I don’t know the terms under which I can use it. I really want to use your project. Please provide a licence.

written by
Craig
published
2011-12-13
Disagree? Found a typo? Got a question?
If you'd like to have a conversation about this post, email craig@barkingiguana.com. I don't bite.
You can verify that I've written this post by following the verification instructions:
curl -LO http://barkingiguana.com/2011/12/13/how-i-structure-rubygems.md.orig
curl -LO http://barkingiguana.com/2011/12/13/how-i-structure-rubygems.md.orig.asc
gpg --verify how-i-structure-rubygems.md.orig{.asc,}