Share

The role of bundler with gem dependencies

The very first gem I ever created was actually pretty good. Apart from one thing: when you installed it, it didn't install the dependencies (i.e. the other gems that it depended upon). This might seem like a pretty big oversight, and it was, but there's a good reason. I actually did specify the dependencies - I just did it in the wrong place.

Bundler gives you a way of generating a gem skeleton, which you can use to quickly get started with a gemspec, Rakefile, lib directory and more. It also adds a Gemfile to your gem's root directory:

$ bundle gem zomg
      create  zomg/Gemfile
      create  zomg/Rakefile
      create  zomg/LICENSE.txt
      create  zomg/README.md
      create  zomg/.gitignore
      create  zomg/zomg.gemspec
      create  zomg/lib/zomg.rb
      create  zomg/lib/zomg/version.rb
Initializating git repo in /home/jon/projects/zomg

I'd been doing rails development for quite a while by this point, so I knew what a Gemfile was for: specifying your dependencies. Surely, you just add your gems to the Gemfile?

# Gemfile
source 'https://rubygems.org'

gemspec

gem 'json'

Then you just run bundle install and it installs the json gem.

What the Gemfile actually does

If you do this, you will find that your dependencies aren't installed when someone installs your gem with gem install:

$ gem install zomg
Successfully installed zomg-x.x.x

The expected result is that it also installs json. The reason that it doesn't is that you must actually specify your dependencies in the gemspec, not the Gemfile:

# zomg.gemspec

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

Gem::Specification.new do |gem|
  gem.name          = "zomg"
  gem.version       = Zomg::VERSION
  gem.authors       = ["Jon Cairns"]
  gem.email         = ["jon@joncairns.com"]
  gem.description   = %q{TODO: Write a gem description}
  gem.summary       = %q{TODO: Write a gem summary}
  gem.homepage      = ""

  # Add your dependencies!
  gem.add_runtime_dependency 'json'

  gem.files         = `git ls-files`.split($/)
  gem.executables   = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
  gem.test_files    = gem.files.grep(%r{^(test|spec|features)/})
  gem.require_paths = ["lib"]
end

And you can leave it out of your Gemfile. In this case, gem install gives:

$ gem install zomg
Successfully installed json-x.x.x
Successfully installed zomg-x.x.x

What's the point of using bundler when creating a gem?

Bundler makes it easy to install gems when you're developing. It will read your gemspec and include any dependencies that you've added, so that you can run bundle install. You don't even need to use the Gemfile for development dependencies, such as test frameworks or plugins: use add_development_dependency in the gemspec for that.

I don't know of any good reason to add something to the Gemfile when creating a gem - in fact, it's more likely to cause you trouble in the long run.

← Previous post: Wait for a unix process to finish Next post: "Fat model, skinny controller" is a load of rubbish →
comments powered by Disqus