Close
Glad You're Ready. Let's Get Started!

Let us know how we can contact you.

Thank you!

We'll respond shortly.

LABS
The Naked Gemspec

Before we start, the .gemspec itself only appears once. Here it is, as generated by bundle init and hand-tweaked for relevance:

lib = File.expand_path("../lib", __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require "your_gem/version"

Gem::Specification.new do |s|
  s.name        = "your-gem"
  s.version     = YourGem::VERSION
  s.authors     = ["Your Name"]
  s.email       = ["you@example.com"]
  s.homepage    = "https://github.com/you/should-use-github"
  s.summary     = "Describe this gem like you're talking to me."
  s.description = "Describe this gem like you're talking to your mom."

  s.require_paths = ["lib"]
  s.files         = `git ls-files`.split("n")
  s.test_files    = `git ls-files -- spec/*`.split("n")

  s.add_dependency "hashie", "~> 2.0"
  s.add_development_dependency "rspec", "~> 2.12"
end

But what does all this mean? Moreover, how do all these crazy bits fit together?

Files

The most important component of any gemspec is the list of files that it includes when building the gem. After all, a .gem file is just a tarball with a metadata header written in Ruby. Here’s how we make that happen:

s.files         = ['file/one', 'file/two']
s.test_files    = ['spec/one', 'spec/two']

Let’s exploit part of git to give us the list of files. Don’t use git on your project? Start using git. Problem solved! Here’s what it gives us:

$ git ls-files
.gitignore
.rvmrc
Gemfile
LICENSE
README.md
your-gem.gemspec
lib/your_gem.rb
lib/your_gem/version.rb
spec/lib/your_gem.rb
spec/spec_helper.rb

We can make this output into a Ruby array of strings quite simply:

`git ls-files`.split("n")

Now, gems are laid out in a conventional way. That means a lib directory, a spec directory and some predictable files. That means nobody has to guess where your files are, which is fantastic!

Now let’s say you want to exclude your .rvmrc and .gitignore, because those files aren’t really all that important:

`git ls-files`.split("n") - %w(.rvmrc .gitignore)

Note: exclude Gemfile.lock from git, even though it might exist in your directory. This is conventional.

Naming

Your gem’s classes are called YourGem, while they live in files named your_gem. As a matter of taste, I believe gems should be named your-gem. There’s an argument to be made that gem names should match their requires (i.e., the gem should be named your_gem).

Version

The first component here is the version number for your gem. In this example, the your_gem/version.rb looks like this:

module YourGem
  VERSION = "0.1.0"
end

This is a Semantic Versioning string, and it’s the Simplest Thing that Could Possibly Work for a version.

Dependencies

This is the fun part. I’m of the opinion that dependencies should be as loose as possible until they’re not, but that throwing them away by doing >= 0 is the wrong approach.

For example, the above file will pull in RSpec as a dependency, but require any version that matches a pattern like 2.y.z, as long as y is above 12. Note that z is allowed to be anything, which allows patch versions to be included.

Of course, this means that everyone in the community has to play along and not break their gem on a minor version bump. Also, the community now includes you!

Maintenance

So now you’ve got a conventional gem with loosely-required dependencies. How do you know if these change? Well, if you’re on github, you can use Gemnasium to watch for new dependency versions and see if anything’s broken!

Dropping a new version of your gem is as easy as gem release with @svenfuchs‘s gem-release gem.

More Reading

Comments
  1. Tom Hallett says:

    Minor correction. The following:

    `git ls-files`.split(“n”)

    should be:

    `git ls-files`.split(“n”)

Post a Comment

Your Information (Name required. Email address will not be displayed with comment.)

* Copy This Password *

* Type Or Paste Password Here *