Rakeをつかって快適にBDD

ある程度の規模のプログラムを Ruby で書こうとおもうと、RSpec による BDD (Behavior Driven Development) は大事なんだな、っていうのがなんとなくわかってきた。
ディレクトリ構成やファイル構成はほぼ機械的に決まるので、じゃあ自動化しましょう、ということでこんなRakefileを書いた。

~/.anyrb/Rakefile:

#!/usr/bin/env ruby
# vim: set ft=ruby

require 'rubygems'
require 'rake'
require 'spec'
require 'pathname'
#require 'spec/rake/spectask'

HOME          = ENV['HOME'] || Pathname.new('~').expand_path
APP           = ENV['APP']  || Pathname.pwd.basename
ANYRB_DIR     = "#{HOME}/.anyrb"
SPEC_DIR      = "spec"
PRODUCT_DIR   = "lib"
SPEC_FILES    = FileList["#{SPEC_DIR}/**/*_spec.rb"]
PRODUCT_FILES = SPEC_FILES.map {|f|
  f.sub(/^#{SPEC_DIR}\//, "#{PRODUCT_DIR}/").sub("_spec", "")
}

directory SPEC_DIR
directory PRODUCT_DIR

task :default => [:init]

task :init => [SPEC_DIR, PRODUCT_DIR] do
  unless File.exist?(app_spec = "#{SPEC_DIR}/#{APP}_spec.rb")
    cp "#{ANYRB_DIR}/skeleton/spec.rb", app_spec
  end
end

task :generate => [:init, *PRODUCT_FILES]

rule(/^#{PRODUCT_DIR}\/.+\.rb$/, => [proc {|t|
  t.sub(/^#{PRODUCT_DIR}\//, "#{SPEC_DIR}/").sub(/\.rb$/, "_spec.rb")
}]) do |t|
  mkdir File.dirname(t.name)
  cp "#{ANYRB_DIR}/skeleton/product.rb", t.name
end
% mkdir someapp
% cd someapp
% rake -f ~/.anyrb/Rakefile
% ls
./ ../ Rakefile lib/ spec/
% ls spec/
./ ../ someapp_spec.rb
% rake generate -f ~/.anyrb/Rakefile
% ls lib/
./ ../ someapp.rb
% touch spec/someapp_foobar_spec.rb
% rake generate -f ~/.anyrb/Rakefile
% ls lib/
./ ../ someapp.rb someapp_foobar.rb

チュートリアルとしてはこんなかんじだろうか。-f オプションをいちいちつけるのはめんどくさいので、rake をラップするシェルスクリプトでも書いておくのがオススメ。

~/bin/anyrb:

#!/bin/sh

rake -f ~/.anyrb/Rakefile "$@"

RSpecとRakeでガンガン開発しましょう。