Rails tip #4: Writing Capistrano recipes to be loaded from gems
Making a gem of your custom Capistrano recipes is a nice way to remove duplicated knowledge across your Rails projects.
On your first pass, you'll probably do what I did: gank and modify capistrano/recipes/deploy.rb
.
This works great, but you'll find it's a little tricky to use your new recipes from a Capfile
. It turns out you can't just "require mygem
" and "load 'mygem/recipes/deploy'
" because Capistrano doesn't load
from ruby's $LOAD_PATH
—it keeps its own minimally-initialized load_paths
setting instead.
So, you have to either modify the load_paths
or use an absolute path, like this:
%w( rubygems wordpress ).each { |lib| require lib }
load Gem.required_location('wordpress', 'wordpress/recipes/deploy.rb')
load 'config/deploy'
This is what I did for a while, but something didn't seem right. My Capfile
didn't look as nice as I expected, and I wasn't using require
, whose comments clearly mention third-party recipes.
Take two
Staring at capistrano/configuration/loading.rb
until it made sense, I saw instance
, which triggered some vague distant memory that somehow turned into a productive thought:
If you wrap your deploy.rb
recipes in a load block, like this:
Capistrano::Configuration.instance(:must_exist).load do
# previous file contents here
end
You can simplify your Capfile
with a shorter require
statement:
require 'rubygems'
require 'wordpress/recipes/deploy'
load 'config/deploy'
Nice. I'm still a little bummed by the require
/ load
imbalance, but on the whole this feels more like things were supposed to be.
5 Rails tips
Each day this week, Joachim and I will post something we've learned in our time programming together. It's fun to do, and we might just win something as well.
So far, we've written:
- Reloadable custom FormBuilder
- Faking DATA in tests
- Filter BLOBs from ActiveRecord logging
- Writing Capistrano recipes to be loaded from gems