LoginSignup
110
47

More than 3 years have passed since last update.

Rails における rake タスクの :environment について

Last updated at Posted at 2019-02-09

rails generate task [namespace] [task_name] を実行すると、デフォルトで以下のようなファイルが生成されます。

sample.rake
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 の役割です。

foo.rake
# 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 タスクを実行していたことが分かりました。中身を少し詳しくみてみます。

railties/lib/rails/application.rb#L509
    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 していることが分かると思います。

railties/lib/rails/application.rb#L335
    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! で初期化しています。

config/environment.rb
# 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 抜きで実行するとエラーが起きます。

sample.rake
namespace :sample do
  desc "TODO"
  task foo: :environment do
  end

end

↓↓ User モデルを一つ取り出してその name を出力しようとするも、「そんなモデル知らんがな」と言われてエラーになってしまいます。

sample2.rake
# 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 コマンドで行儀よく作った方が思わぬミスは少なくなりそうです。

110
47
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
110
47