This post originated from an RSS feed registered with Ruby Buzz
by Gabriel Horner.
Original Post: Building DRY Gems With Thor And Jeweler
Feed Title: Tagaholic
Feed URL: http://feeds2.feedburner.com/tagaholic
Feed Description: My ruby/rails/knowledge management thoughts
Being DRY is a concept I try to embrace as a programmer. Lately when creating new gems, I’ve noticed that I copy, paste and tweak the same boilerplate gemspecs/Rakefiles. Fed up with this non-DRY gem configuration, I came up with a gemspec config file solution. With this yaml config file and some thor tasks, I’m able to generate gem specifications for any of my gems.
Before going any further, let me disclaim that while this post uses thor and jeweler, they could be replaced with similar gems i.e sake for thor and any gem creator that takes Gem::Specification objects for jeweler. Having said that, the idea of a gemspec config file is solely dependent on my GemspecBuilder class. If there’s interest from others I can package it up as gem.
Rant
If you’ve been using jeweler like I have, you start each new gem by copying your boilerplate Rakefile which contains a special section for your jeweler config. Taken from the jeweler readme, a Rakefile can as easy as:
begin require'jeweler' Jeweler::Tasks.newdo|gemspec| gemspec.name="the-perfect-gem" gemspec.summary="TODO" gemspec.email="josh@technicalpickles.com" gemspec.homepage="http://github.com/technicalpickles/the-perfect-gem" gemspec.description="TODO" gemspec.authors=["Josh Nichols"] end rescueLoadError puts"Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com" end
While this is easy, it’s not DRY once you start copying and pasting Rakefiles across your gems. Sure, your name and email are unlikely to change. But what if you want to have other default gemspec attributes such as files, has_rdoc and extra_rdoc_files? And if you want to change some of your default attributes later? What if you’d like to reuse some of the information in your gem specifications to create a gem’s web page? Do you feel yet that maybe you should’ve had a more DRY setup? So instead of having copy and paste configurations spread across my gems, I have oneconfiguration file for them. Since this config file has one section for common gemspec attributes, gemspec management becomes easy and DRY.
How It Works
Using the gemspec config file and a given or detected gem name, GemspecBuilder.build creates a Gem::Specification object. Jeweler’s task class uses this object to create a Jeweler object. To run a jeweler task, I invoke thor to delegate the correct task to the Jeweler object. My thor tasks look like this:
# cd into one of my gems
bash> cd hirb
# equivalent to jeweler's rake version
bash> thor jeweler:version
Current version: 0.1.2
# equivalent to jeweler's rake gemspec
bash> thor jeweler:gemspec
hirb.gemspec is valid.
Generated: hirb.gemspec
# equivalent to jeweler's rake build
bash> thor jeweler:build
Successfully built RubyGem
Name: hirb
Version: 0.1.2
File: hirb-0.1.2.gem
# equivalent to jeweler's rake version:bump:patch
bash> thor jeweler:bump patch
Current version: 0.1.2
Updated version: 0.1.3
If you aren’t familiar with thor, you may be wondering why I didn’t just stop with making the above Rakefile leaner:
begin require'jeweler' require'gemspec_builder'#if it were packaged as a gem Jeweler::Tasks.new(GemspecBuilder.build) rescueLoadError puts"Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com" end
What thor provides that rake and sake can’t provide is letting you write your tasks as Ruby. This is quite powerful when you want pass arguments and options to your tasks (and testing tasks and sharing task libraries, etc…). Also, thor like sake, allows you to install your ruby tasks to be invoked from anywhere. This allows me to use my jeweler tasks without putting any jeweler (gem creator specific) tasks in my gem’s Rakefile. I think this is a good thing since I leave potential forkers of my gem with a choice of what gem creator they want to use for recreating my gemspec. In the same vein, I forgot to mention that one gemspec config file gives you the painless choice of switching gem creators when the need arises.
Install
If you’re using jeweler as your gem creator, you can give this idea a try:
GemspecBuilder and the thor-jeweler tasks are wrapped as a thor bundle. I thought I would mention it since little has been written about them since their announcement.