Common practices to make your gem users’ and other developers’ lives easier.
There are only two hard things in Computer Science: cache invalidation and naming things. -Phil Karlton
Be consistent with how your gem files in
bin are named. The hola gem from the make your own gem guide is a great example:
% tree . ├── Rakefile ├── bin │ └── hola ├── hola.gemspec ├── lib │ ├── hola │ │ └── translator.rb │ └── hola.rb └── test └── test_hola.rb
The executable and the primary file in
lib are named the same. A developer can easily jump in and call
require 'hola' with no problems.
Naming your gem
Naming your gem is important. Before you pick a name for your gem, do a quick search on RubyGems.org and GitHub to see if someone else has taken it. Every published gem must have a unique name. Be sure to read our naming recommendations when you’ve found a name you like.
A versioning policy is merely a set of simple rules governing how version numbers are allocated. It can be very simple (e.g. the version number is a single number starting with 1 and incremented for each successive version), or it can be really strange (Knuth’s TeX project had version numbers: 3, 3.1, 3.14, 3.141, 3.1415; each successive version added another digit to PI).
The RubyGems team urges gem developers to follow the Semantic Versioning standard for their gem’s versions. The RubyGems library itself does not enforce a strict versioning policy, but using an “irrational” policy will only be a disservice to those in the community who use your gems.
Suppose you have a ‘stack’ gem that holds a
Stack class with both
pop functionalty. Your
CHANGELOG might look like this if you use semantic versioning:
- Version 0.0.1: The initial
Stackclass is released.
- Version 0.0.2: Switched to a linked list implementation because it is cooler.
- Version 0.1.0: Added a
- Version 1.0.0: Added
popused to return the old top item).
- Version 1.1.0:
pushnow returns the value pushed (it used it return
- Version 1.1.1: Fixed a bug in the linked list implementation.
- Version 1.1.2: Fixed a bug introduced in the last fix.
Semantic versioning boils down to:
0.0.xlevel changes for implementation level detail changes, such as small bug fixes
0.x.0level changes for any backwards compatible API changes, such as new functionality/features
x.0.0level changes for backwards incompatible API changes, such as changes that will break existing users code if they update
Gems work with other gems. Here are some tips to make sure they’re nice to each other.
Runtime vs. development
RubyGems provides two main “types” of dependencies: runtime and development. Runtime dependencies are what your gem needs to work (such as rails needing activesupport).
Development dependencies are useful for when someone wants to make modifications to your gem. When you specify development dependencies, another developer can run
gem install --dev your_gem and RubyGems will grab both sets of dependencies (runtime and development). Typical development dependencies include test frameworks and build systems.
Setting dependencies in your gemspec is easy. Just use
Gem::Specification.new do |s| s.name = "hola" s.version = "2.0.0" s.add_runtime_dependency "daemons", ["= 1.1.0"] s.add_development_dependency "bourne", [">= 0"]
gem from within your gem
You may have seen some code like this around to make sure you’re using a specific version of a gem:
gem "extlib", ">= 1.0.8" require "extlib"
It’s reasonable for applications that consume gems to use this (though they could also use a tool like Bundler). Gems themselves should not do this. They should instead use dependencies in the gemspec so RubyGems can handle loading the dependency instead of the user.
Pessimistic version constraint
If your gem properly follows semantic versioning with its versioning scheme, then other Ruby developers can take advantage of this when choosing a version constraint to lock down your gem in their application.
Let’s say the following releases of a gem exist:
- Version 2.1.0 — Baseline
- Version 2.2.0 — Introduced some new (backward compatible) features.
- Version 2.2.1 — Removed some bugs
- Version 2.2.2 — Streamlined your code
- Version 2.3.0 — More new features (but still backwards compatible).
- Version 3.0.0 — Reworked the interface. Code written to version 2.x might not work.
Someone who wants to use your gem has determined that version 2.2.0 works with their software, but version 2.1.0 doesn’t have a feature they need. Adding a dependency in a gem (or a
Gemfile from Bundler) might look like:
# gemspec spec.add_runtime_dependency 'library', '>= 2.2.0' # bundler gem 'library', '>= 2.2.0'
This is an “optimistic” version constraint. It’s saying that all changes from 2.x on will work with my software, but for version 3.0.0 this will not be true.
The alternative here is to be “pessimistic”. This explicitly excludes the version that might break your code.
# gemspec spec.add_runtime_dependency 'library', ['>= 2.2.0', '< 3.0'] # bundler gem 'library', '>= 2.2.0', '< 3.0'
RubyGems provides a shortcut for this, commonly known as the twiddle-wakka:
# gemspec spec.add_runtime_dependency 'library', '~> 2.2' # bundler gem 'library', '~> 2.2'
Notice that we dropped the
PATCH level of the version number. Had we said
~> 2.2.0, that would have been equivalent to
['>= 2.2.0', '< 2.3.0'].
If you want to allow use of newer backwards-compatible versions but need a specific bug fix you can use a compound requirement:
# gemspec spec.add_runtime_dependency 'library', '~> 2.2', '>= 2.2.1' # bundler gem 'library', '~> 2.2', '>= 2.2.1'
The important note to take home here is to be aware others will be using your gems, so guard yourself from potential bugs/failures in future releases by using
~> instead of
>= if at all possible.
If you’re dealing with a lot of gem dependencies in your application, we recommend that you take a look into Bundler or Isolate which do a great job of managing a complex version manifest for many gems.
…should not be necessary in your gem code, since RubyGems is loaded already when a gem is required. Not having
require 'rubygems' in your code means that the gem can be easily used without needing the RubyGems client to run.
For more information please check out Ryan Tomayko’s original post about the subject.
At its core, RubyGems exists to help you manage Ruby’s
$LOAD_PATH, which is how the
require statement picks up new code. There’s several things you can do to make sure you’re loading code the right way.
Respect the global load path
When packaging your gem files, you need to be careful of what is in your
lib directory. Every gem you have installed gets its
lib directory appended onto your
$LOAD_PATH. This means any file on the top level of the
lib directory could get required.
For example, let’s say we have a
foo gem with the following structure:
. └── lib ├── foo │ └── cgi.rb ├── erb.rb ├── foo.rb └── set.rb
This might seem harmless since your custom
set files are within your gem. However, this is not harmless, anyone who requires this gem will not be able to bring in the ERB or Set classes provided by Ruby’s standard library.
The best way to get around this is to keep files in a different directory under
lib. The usual convention is to be consistent and put them in the same folder name as your gem’s name, for example
Requiring files relative to each other
Gems should not have to use
__FILE__ to bring in other Ruby files in your gem. Code like this is surprisingly common in gems:
require File.join( File.dirname(__FILE__), "foo", "bar")
require File.expand_path(File.join( File.dirname(__FILE__), "foo", "bar"))
The fix is simple, just require the file relative to the load path:
Or use require_relative:
The make your own gem guide has a great example of this behavior in practice, including a working test suite. The code for that gem is on GitHub as well.
Mangling the load path
Gems should not change the
$LOAD_PATH variable. RubyGems manages this for you. Code like this should not be necessary:
lp = File.expand_path(File.dirname(__FILE__)) unless $LOAD_PATH.include?(lp) $LOAD_PATH.unshift(lp) end
__DIR__ = File.dirname(__FILE__) $LOAD_PATH.unshift __DIR__ unless $LOAD_PATH.include?(__DIR__) || $LOAD_PATH.include?(File.expand_path(__DIR__))
When RubyGems activates a gem, it adds your package’s
lib folder to the
$LOAD_PATH ready to be required normally by another lib or application. It is safe to assume you can then
require any file in your
Many gem developers have versions of their gem ready to go out for testing or “edge” releases before a big gem release. RubyGems supports the concept of “prerelease” versions, which could be betas, alphas, or anything else that isn’t ready as a regular release.
Taking advantage of this is easy. All you need is one or more letters in the gem version. For example, here’s what a prerelease gemspec’s
version field might look like:
Gem::Specification.new do |s| s.name = "hola" s.version = "1.0.0.pre"
Other prerelease version numbers might include
1.5.0.beta.3. It just has to have a letter in it, and you’re set. These gems can then be installed with the
--pre flag, like so:
% gem list factory_girl -r --pre *** REMOTE GEMS *** factory_girl (2.0.0.beta2, 2.0.0.beta1) factory_girl_rails (1.1.beta1) % gem install factory_girl --pre Successfully installed factory_girl-2.0.0.beta2 1 gem installed
Several sources were used for content for this guide: