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

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

```ruby: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 の役割です。

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

```ruby: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
```
https://github.com/rails/rails/blob/master/railties/lib/rails/application.rb#L509

`task :environment`のブロック内で呼び出している `require_environment!` をみてみると、以下のように `config/environment.rb` を require していることが分かると思います。

```ruby:railties/lib/rails/application.rb#L335
    def require_environment! #:nodoc:
      environment = paths["config/environment"].existent.first
      require environment if environment
    end
```
https://github.com/rails/rails/blob/master/railties/lib/rails/application.rb#L335

`config/environment.rb` の中身を見ると分かりますが、ここでまさに Rails のアプリケーションコードを読み込んでいます。同じ階層の `application.rb` を require して、 `Rails.application.initialize!` で初期化しています。

```ruby: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` 抜きで実行するとエラーが起きます。

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

end
```

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

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