Crafting gems. Jeweler vs Bundler
22 January 2011
Recently I’ve changed some of my gems to use bundler in favor of jeweler. In short both make generating gemspec easier (obviously bundler can do way more than that). Actually I use bundler anyway so why have another dependency in form of jeweler? To compare both let’s take a look at how they work for us. We’ll use jeweler 1.5.2 and bundler 1.0.7.
Creating files structure
$ jeweler mygem create .gitignore create Rakefile create Gemfile create LICENSE.txt create README.rdoc create .document create lib create lib/mygem.rb.rb create test create test/helper.rb create test/test_mygem.rb.rb
Creating basic structure is actually the biggest strength of jeweler.
$ jeweler --help
Typing that will list dozens of available options. Choosing test framework, initializing git repo or specyfing github credentials to name a few.
How that compares to bundler?
$ bundle gem mygem create mygem/Gemfile create mygem/Rakefile create mygem/.gitignore create mygem/mygem.gemspec create mygem/lib/mygem.rb create mygem/lib/mygem/version.rb Initializating git repo in /home/snatcher/temp/garbage/mygem
Slighty less files were created. We don’t have LICENSE and test framwork files included by default. List of available options is also very thin.
$ bundle help gem -b, [--bin=Generate a binary for your library.] [--no-color=Disable colorization in output] -V, [--verbose=Enable verbose output mode]
Bundler’s approach is to generate required files only. Tasks not related directly to gemspec are up to developer.
Handling gem version
Jeweler prefers to keep version number in text file named VERSION
$ cat VERSION 0.0.1
On the other hand bundler creates version.rb file and assignes number to VERSION constant
module Mygem
VERSION = "0.0.1"
end
In my opinion bundler approach is way better as gem version is available always when the code is loaded.
puts Mygem::VERSION # => 0.0.1
Of course using Jeweler we’re also able to have version as constant but bundler gives us it for free.
Gem metadata
With jeweler things like summary, description and author are written to Rakefile. Basing on data in Rakefile jeweler creates gemspec. In that case gemspec is meant to readonly and we put all metadata into Rakefile. It leads to duplication as we have the same parts in both Rakefile and gemspec.
Jeweler::Tasks.new do |gem|
gem.name = "redhillonrails_core"
gem.summary = %Q{Adds support in ActiveRecord for foreign_keys
and other database-related stuff}
gem.email = "michal.lomnicki@gmail.com"
gem.homepage = "http://github.com/mlomnicki/redhillonrails_core"
gem.authors = ["Michał Łomnicki"]
end
To generate gemspec rake task needs to be invoked
$ rake gemspec Generated: redhillonrails_core.gemspec redhillonrails_core.gemspec is valid.
Bundler prefers slightly different approach. We edit gemspec itself
Gem::Specification.new do |s|
s.name = "exceptioner"
s.version = Exceptioner::VERSION
s.platform = Gem::Platform::RUBY
s.authors = ["Michał Łomnicki"]
s.email = ["michal.lomnicki@gmail.com"]
s.homepage = "https://github.com/mlomnicki/exceptioner"
s.summary = "Stay notified of exceptions raised by
your application."
end
No additional work is needed. Gemspec is first class citizen of our gem.
Dependencies
Jewler encurages us to use bundler so we define dependencies using beloved Gemfile.
source :rubygems
gem "activerecord"
group :development, :test do
gem "jeweler", "~> 1.5"
gem "rspec", '~> 1.3'
gem "pg"
gem "mysql"
end
That’s very familiar technique for everyone who is used to bundler.
On the other hand content of Gemfile created by bundle gem command is somehow surprising.
source "http://rubygems.org"
gemspec
So what is a preferred place to set gem dependencies? Actually bundler follows its philosophy and encurages us to put dependencies in gemspec file.
Gem::Specification.new do |s|
# metadata goes here
s.add_dependency("mail", ["~> 2.2"])
s.add_dependency("xmpp4r", ["~> 0.5"])
s.add_development_dependency("rack")
s.add_development_dependency("mocha")
end
Invoking bundle install will still work as expected.
$ bundle install Using rake (0.8.7) Using activesupport (3.0.3) Using i18n (0.5.0) Using mime-types (1.16) Using polyglot (0.3.1) Using treetop (1.4.9) Using mail (2.2.14) Using xmpp4r (0.5) Using exceptioner (0.0.5) Using mocha (0.9.10) Using rack (1.2.1) Using bundler (1.0.7) Your bundle is complete! Use `bundle show [gemname]` to see where a bundled gem is installed.
Releasing
Both jeweler and bundler has similar functionality here.
$ rake release
Invoking the task above will:
- build a gem from gemspec
- tag and push code to the repo
- push gem to rubygems
Summary
Both jeweler and bundler do great job doing tedious work for us. Bundler is younger brother of jeweler which introduced some very good improvements. Jeweler is still more powerful and flexible. Bundler is thinner and relies more on gemspec itself. Personally I’ve changed from Jeweler to Bundler and I’m pretty satisfied with that.