PIYO - Tech & Life -

RailsのRakeタスクが定義される順番

Rake Ruby Rails

Railsで使うRakeタスクは大きくわけて3種類あり、普通はこの順番で定義されることになっている。

  • gemのタスク
  • アプリケーション固有のタスク
  • Railsのタスク

まずソースコードを読んでみた。

Rakefile

# Rakefile
require File.expand_path('../config/application', __FILE__)
Rails.application.load_tasks

config/application.rbを読んでからRails.application.load_tasksを呼ぶ。

Rails.application.load_tasks

Rails.application.load_tasksrailtieRails::Engineに定義されている。

# railties-4.1.0/lib/rails/engine.rb
def load_tasks(app=self)
  require "rake"
  run_tasks_blocks(app)
  self
end

この中で呼ばれているrun_tasks_blocksEngineのサブクラスである、Rails::Applicationに定義されていて、実装は次のようになっている。

# railties-4.1.0/lib/rails/application.rb
def run_tasks_blocks(app) #:nodoc:
  railties.each { |r| r.run_tasks_blocks(app) }
  super
  require "rails/tasks"
  task :environment do
    ActiveSupport.on_load(:before_initialize) { config.eager_load = false }

    require_environment!
  end
end

このうちの最初の3行が最初に挙げた3種類のタスクの定義に相当する。

  • gemのタスク
  • アプリケーション固有のタスク
  • Railsのタスク

gemのタスク

gem用のタスクというものがある。たとえばdb:migrateはActiveRecordのRakeタスクで、このタスクはactiverecord-4.1.0/lib/active_record/railties/databases.rakeに定義されている。これをここではgem用のタスクと呼ぶことにする。

gem用のタスクはRailtieを介して定義されている。

まずActiveRecord::Railtieのクラス定義でスーパークラスであるRails::Railtierake_taskメソッドが呼ばれ、タスクの定義がブロックとして渡されている。

# activerecord-4.1.0/lib/active_record/railtie.rb
rake_tasks do
  # ... 略
  load "active_record/railties/databases.rake"
end

このrake_tasksメソッドは単にブロックをArrayとして取っておくだけのものとなっている。

def rake_tasks(&blk)
  @rake_tasks ||= []
  @rake_tasks << blk if blk
  @rake_tasks
end

ここで取っておいたブロックは、RailsのRakeファイルから呼び出されるload_tasksにおいて、このコードの部分

railties.each { |r| r.run_tasks_blocks(app) }

で実行され、各gemのタスクが定義されることとなる。

アプリケーション固有のタスク

アプリケーション固有のタスクというのは、lib/tasks/*.rakeという名前で定義すアプリケーション専用のRakeタスクのことを言っている(勝手に呼んでいる)。

このファイルがどこで読み込まれているかを知るためにはRails::Application.run_tasks_block中のsuperという呼び出しを見ていく必要がある。superであるRails::Engine.run_tasks_blocksのコードは次のとおり。

def run_tasks_blocks(*) #:nodoc:
  super
  paths["lib/tasks"].existent.sort.each { |ext| load(ext) }
end

ここでのsuper@rake_tasksが持っているコードブロックを実行するもので、gem用のタスクのときに使われているがRailsアプリケーションとしては使われていないはず。

アプリケーション固有のタスクはメソッド2行目のこの部分、paths["lib/tasks"].existent.sort.each { |ext| load(ext) }でロードされる。コードは見たまんま。

Railsのタスク

まだ見ていない最後の行でRailsのタスクが定義される。

require "rails/tasks"

requireされているtasks.rbにはタスクが定義されているファイルを複数ロードするというコードが書かれている。

# railties-4.1.0/lib/rails/tasks.rb

# Load Rails Rakefile extensions
%w(
  annotations
  documentation
  framework
  log
  middleware
  misc
  routes
  statistics
  tmp
).each do |task|
  load "rails/tasks/#{task}.rake"
end
# railties-4.1.0/lib/rails/tasks/routes.rake
desc 'Print out all defined routes in match order, with names. Target specific controller with CONTROLLER=x.'
task routes: :environment do
  all_routes = Rails.application.routes.routes
  require 'action_dispatch/routing/inspector'
  inspector = ActionDispatch::Routing::RoutesInspector.new(all_routes)
  puts inspector.format(ActionDispatch::Routing::ConsoleFormatter.new, ENV['CONTROLLER'])
end

rake routesrake statsなどはここで定義されているということがわかる。

Rakeタスクの削除の可否

RailsのRakeタスクを上書き・再定義するには - ぴよログ

すでにロードされているRakeタスクを削除する方法がある。この記事ではアプリケーション固有のタスクを書くときに既存のタスク削除して上書きするという方法にトライした。

この記事でも書いたが、アプリケーション固有のタスクを定義する段階では、Railsのタスクは削除できない。先ほど見たとおりRailsのタスクは最後に読み込まれるからだ。

Rails::Application.run_tasks_blocks自体をモンキーパッチしてしまうなどの方法は無くはないかもしれないが、似たようなコードをコピーしてくることになるのであまりスマートではない。となると、きれいに書く方法はないのかもしれない。