@kyanny's blog

My life. Opinions are my own.

RAILS_ROOT/lib/tasks 以下にあるタスク*だけ*を実行する Rakefile2 の作り方

  • Rails デフォルトの db:* 系タスクを誤って production 環境で実行すると悲惨なので Rakefile は使いたくない
  • しかしバッチ処理などは Rake タスクとしてまとめておきたい(console で都度実行するのはオペミスが怖い)

という事情があって、個別にスクリプトを追加してもいいんだけど(実際いままではそうしてきた)、 Rake でやりたかったのでやってみた。環境は Rails 2.x.y と Rake 0.8.7 です。

https://gist.github.com/1058373

# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.

require(File.join(File.dirname(__FILE__), 'config', 'boot'))

require 'rake'
require 'rake/testtask'
require 'rake/rdoctask'

$:.each do |load_path|
  rails_task_loader_file = 'tasks/rails.rb'
  rails_task_loader_path = File.expand_path(File.join(load_path, rails_task_loader_file))
  if File.exist?(rails_task_loader_path)
    rails_task_dir_path = File.dirname(rails_task_loader_path)
    rails_task_environment = File.join(rails_task_dir_path, 'misc.rake')
    load rails_task_environment
  end
end

# Load any custom rakefile extensions
Dir["#{RAILS_ROOT}/lib/tasks/**/*.rake"].sort.each { |ext| load ext }
$ rake -f Rakefile2 -T

とかすると RAILS_ROOT/lib/tasks 以下にある *.rake で定義されたタスクのみが列挙される。

オリジナルの Rakefile は

require 'tasks/rails'

で rails-x.y.z/lib/tasks/rails.rb を読み込んでいて、そいつのなかで同じディレクトリ内の *.rake を load している。この load は rake-x.y.z/lib/rake.rb のなかで def load ... end と定義されているやつっぽい(実体は Kernel#load の呼び出し)

この *.rake たちが Rails のデフォルトの Rake タスクで、中身をみてみると db:* とか test:* とか見慣れたタスクが定義してある。このうち misc.rake に :environment というタスクが定義されていて、これを読み込まないと

task :foo => :environment do
  # ...
end

のように定義されたタスクが実行できない。ので、自前で作った Rakefile2 で $: の中から 'tasks/rails.rb' があるパスを探してそこにある misc.rake だけを load してやり、その後自前の Rake タスクをロードしてやると、 RAILS_ROOT/lib/tasks/*.rake だけを安全に実行する Rakefile をつくることができる。

ファイル名(拡張子)決め打ちだったりとかいろいろ力業でアレな感じだけど、オペミスで事故るリスクを考えたらマシかなと。