rails generate task [namespace] [task_name]
を実行すると、デフォルトで以下のようなファイルが生成されます。
namespace :sample do
desc "TODO"
task foo: :environment do
end
end
この :environment
の部分なんなんでしょう?なにか環境変数的なのを読み込んでるんでしょうか?
rake における Prerequisites
いろいろ調べてみると、そもそも rake には Prerequisites といういわば事前タスクを設定する機能があって、 :environment
はそれに相当するもののようです。
http://docs.seattlerb.org/rake/doc/rakefile_rdoc.html#label-Tasks+with+Prerequisites
以下のような rake タスクの場合、まず bar
が実行されて、その後に foo
が実行されます。これが Prerequisites の役割です。
# frozen_string_literal: true
namespace :sample do
desc 'TODO'
task foo: :bar do
puts 'This is a main task.'
end
task :bar do
puts 'This is a prerequisite task.'
end
end
$ rake sample:foo
This is a prerequisite task.
This is a main task.
Rails のソースコード内にある environment
タスクの中身
このことから、何かしら Rails 側で Prerequisites を用意しているだろうと考え、ソースコードを追ってみることにしました。結果、 railties/lib/rails/application.rb
の中で :environment
タスクを実行していたことが分かりました。中身を少し詳しくみてみます。
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
task :environment
のブロック内で呼び出している require_environment!
をみてみると、以下のように config/environment.rb
を require していることが分かると思います。
def require_environment! #:nodoc:
environment = paths["config/environment"].existent.first
require environment if environment
end
config/environment.rb
の中身を見ると分かりますが、ここでまさに Rails のアプリケーションコードを読み込んでいます。同じ階層の application.rb
を require して、 Rails.application.initialize!
で初期化しています。
# frozen_string_literal: true
# Load the Rails application.
require_relative 'application'
# Initialize the Rails application.
Rails.application.initialize!
environment
は Rails のアプリケーションコードを読み込むタスク
冒頭の rake タスクをあらためて見てみると、 foo
の前に environment
で Rails のアプリケーションコードを読み込んでくれていることが分かります。実際 Rails 側のクラスやモジュールを呼び出すタスクを :environment
抜きで実行するとエラーが起きます。
namespace :sample do
desc "TODO"
task foo: :environment do
end
end
↓↓ User モデルを一つ取り出してその name を出力しようとするも、「そんなモデル知らんがな」と言われてエラーになってしまいます。
# frozen_string_literal: true
namespace :sample do
desc 'TODO'
task :foo do
user = User.first
puts "user name: #{user.name}"
end
end
$ rake sample:foo
rake aborted!
NameError: uninitialized constant User
/usr/local/bundle/gems/bootsnap-1.3.2/lib/bootsnap/load_path_cache/core_ext/active_support.rb:74:in `block in load_missing_constant'
/usr/local/bundle/gems/bootsnap-1.3.2/lib/bootsnap/load_path_cache/core_ext/active_support.rb:8:in `without_bootsnap_cache'
/usr/local/bundle/gems/bootsnap-1.3.2/lib/bootsnap/load_path_cache/core_ext/active_support.rb:74:in `rescue in load_missing_constant'
/usr/local/bundle/gems/bootsnap-1.3.2/lib/bootsnap/load_path_cache/core_ext/active_support.rb:56:in `load_missing_constant'
/sample/lib/tasks/sample.rake:6:in `block (2 levels) in <main>'
このことを考えると、手動で rake タスクを作るより rails generate
コマンドで行儀よく作った方が思わぬミスは少なくなりそうです。