Skip to content

MR16 Gemspec

Dave Strus edited this page Feb 1, 2016 · 1 revision

Gemspec

The gemspec file is what makes a gem a gem. It includes metadata about the gem, as well as the information necessary to package the gem, and development dependencies.

In spite of the fancy file name extension, the gemspec contains plain old Ruby code. Let's have a look at the gemspec that Bundler generated for us: mutantcorp-mutant.gemspec.

Many editors and IDEs have a "fuzzy finder" feature that lets you find files within projects by typing any part of the filename. In RubyMine, open the fuzzy finder with Shift+Cmd+O. In Sublime Text and Atom, the shortcut is Cmd+t or Cmd+p. Once you've opened the fuzzy finder, just start typing spec to go straight to mutantcorp-mutant.gemspec.

Inside a block that begins Gem::Specification.new do |spec|, you'll see definitions for a bunch of attributes. Only a few attributes are actually required:

  • author or authors
  • files
  • name
  • require_paths
  • summary
  • version

There are two more that the specifications consider required, but they also have default values, so you can safely omit them if the defaults are fine, which they usually are:

  • platform
  • rubygems_version

Bundler also includes a few optional, but recommended, attributes in the gemspec it generates. Let's have a look at ours.

The pure metadata is self-explanatory:

spec.name          = "mutantcorp-mutant"
spec.version       = Mutantcorp::Mutant::VERSION
spec.authors       = ["Dave Strus"]
spec.email         = ["[email protected]"]

spec.summary       = %q{TODO: Write a short summary, because Rubygems requires one.}
spec.description   = %q{TODO: Write a longer description or delete this line.}
spec.homepage      = "TODO: Put your gem's website or public repo URL here."
spec.license       = "MIT"

We need to write real a real summary and description. We can leave homepage as a TODO.

spec.summary       = "Mutant class with all the basics."
spec.description   = "Provides a Mutant class suitable for a variety of purposes."

Next, there's a section in which you can restrict what gem servers this gem can be published to. For proprietary gems, you'll want restrictions in place.

if spec.respond_to?(:metadata)
  spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
else
  raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
end

Next, we specify which files in our project are necessary for the gem to build.

spec.files         = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }

Bundler automatically excludes the gem's tests from the build. The tests will still be in the repository, but they won't be downloaded every time someone uses the gem in another project.

The next two lines specify where to find executables within our gem (spec.bindir), and the names of those executables.

spec.bindir        = "bin"
spec.executables   = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }

Bundler might have put exe in place of bin on both of those lines, despite the face that the directory it generates is called bin. If that's the case, replace those lines with the versions shown above.

With this value for spec.executables, anything we put in the bin will be treated as an executable within our gem.

We have two executables in our gem by default: bin/console and bin/setup. console simply starts an IRB session with our gem's code pre-loaded. (As the comments in the script show, we can easily swap out IRB for Pry.) setup is effectively empty. If our gem requires some setup to run when it's added to a project, we can add code to the setup script.

Next comes the list of require paths.

spec.require_paths = ["lib"]

This specifies the paths to where the actual Ruby code for our project lives. Within our project, we can treat the lib directory as the base path for any require statements.

Finally, we list our gem's dependencies. Right now, we only have development dependencies. That is, other projects that include our gem don't necessarily need to have bundler, rake, and minitest. But we need them while we're developing the gem. We'll determine any additional dependencies as we go.

Let's add one line above our dependencies, specifying that our gem requires at least version 2.0 of Ruby:

spec.required_ruby_version = ">= 2.0"

With that, our gemspec is looking good. Now let's try actually building the thing.