Gemist

An extremely minimal solution to gem isolation

Gemist is a solution to load the correct gems for a project based on a Gemfile manifest.

Gemist is a leaner (4kb), faster runtime that is Bundler-compatible. It does not actually require Bundler.

Getting started

Install it:

$ gem install gemist

Make a file in your project called Gemfile.

# Gemfile
gem "sinatra"
gem "haml"
gem "sass"
gem "ohm", "0.1.3"

# These will only be for the development environment
group :development do
  gem "json-pure", require: "json"
end

# You may specify multiple files to be required
gem "rails", ">= 3.0", require: ['rails', 'action_controller']

# You can also specify more than one version requirement
gem "compass", "~> 0.11.1", "<= 0.11.5"

In your project file, do this. This requires the gems defined in the Gemfile based on the RACK_ENV.

require 'gemist'
Gemist.require

When you run your app, and some gems are not present, a message will show:

$ ruby init.rb
Some gems cannot be loaded. Try:

gem install ohm -v 0.1.3
gem install json-pure

How does it work?

Gemist uses Rubygems to load specific gems. Did you know you can specify a gem version using Rubygems's Gem.activate? Gemist is merely a light bridge that does that for you by reading your Gemfile.

For example, if your project has this Gemfile:

gem "sinatra", "~> 1.2.0", require: "sinatra/base"
gem "nokogiri", ">= 1.2"

Then you do:

Gemist.require

All Gemist.require does in the background is:

require 'rubygems'

::Gem.activate "sinatra", "~> 1.2.0"
::Gem.activate "nokogiri", ">= 1.2"
require "sinatra/base"
require "nokogiri"

How to do other things

Gemist doesn't have some of Bundler's conveniences that Rubygems can already handle.

Freezing gem versions

If your project has a Bundler-generated Gemfile.lock file, Gemist will use it.
This file can be generated using bundle update --local. Note that this is completely optional!

Also, to ensure your app will work with future gem releases, you should add versions like so (using ~> is highly recommended):

# Gemfile
gem "sinatra", "~> 1.1"

Vendoring gems

Gemist does NOT vendor gems for you. Rubygems helps you with that already!

First, don't specify your vendored gems in your Gemfile.

Second, freeze your gems like so:

$ mkdir vendor
$ cd vendor
$ gem unpack sinatra

Then load them manually:

# init.rb
$:.unshift *Dir['./vendor/*/lib']
require 'sinatra/base'

More common usage

If you prefer to require gems individually yourself, use Gemist.setup.

require 'gemist'
Gemist.setup

Alternatively, you may also use the syntactic sugar (does the same thing as above):

require 'gemist/setup'

To require gems from a specific group, use Gemist.require <group>. (By default, Gemist assumes whatever is in RACK_ENV.)

require 'gemist'
Gemist.require :development

There's also syntactic sugar for Gemist.require ENV['RACK_ENV']:

require 'gemist/require'

Benchmarks

Informal benchmarks with a Gemfile of one of my projects on Ruby 1.9.2:

Benchmark.measure { require 'bundler'; Bundler.require }  #=> 2.5s average
Benchmark.measure { require 'gemist';  Gemist.require }   #=> 1.6s average

Not going to happen

Gemist will never have:

  • Dependency resolution.
    If there are conflicts in your gems's requirements, just manually specify the gem version that will satisfy both. Alternatively, stop using too many gems.

  • An installer (like 'bundle install').
    Seriously, just install the gems yourself! Gemist even gives you the exact command to do it.

Authors

Done by Rico Sta. Cruz and released under the MIT license.