[rails]task実行時にrake aborted! NameError: uninitialized constant


[まとめ] 現在開催中のKindleセール情報はこちら

発生した問題

./bin/rake でtaskを実行すると問題なく実行できるのに、
bundle exec rake でtaskを実行するとrake aborted!になってしまう
という現象が発生してハマりました。

結論

:environmentが指定できていなかった

結論としては非常に初歩的なミスだったのですが、:environment自体は記述していたにも関わらずエラーが起きていたので、気づくのに時間がかかってしまいました。

エラーの再現方法

  • 検証用のtaskを作成
./bin/rails g task test_task
  • 以下を記述
# lib/tasks/test_task.rake

namespace :test_task do
  desc "実験用のタスクです"
  task :run, :environment do
    TestModel.new # 適当なModelを呼び出す
    p 'task finished!'
  end
end
  • taskを実行
./bin/rake test_task:run
"task finished!"

./bin/rakeで実行した場合は問題なく実行できました。

続いて

bundle exec rake test_task:run
rake aborted!
NameError: uninitialized constant TestModel

???

:environment も設定しているので問題ないはず」と思いながら、色々調べて見つけた方法を試していたのですが解決せず。。。

最終的にものすごくしょうもないミスをしていたことに気づきました。

以下が修正後のtaskです。

# lib/tasks/test_task.rake

namespace :test_task do
  desc "実験用のタスクです"
  task run: :environment do
    TestModel.new
    p 'task finished!'
  end
end

最初のやつとの違いにお気づきでしょうか?

before:
  task :run, :environment do

after:
  task run: :environment do

↑ここです。

「:environment も設定しているので問題ないはず」という思い込みのせいでハマってしまったのですが、最初のやり方だと:environmentは設定できておらず、引数として扱われてしまっていました

以下のようにコードを修正すると

namespace :test_task do
  desc "実験用のタスクです"
  task :run, :environment do |task, args|
    p args
    p 'task finished!'
  end
end

引数のキーとしてenvironmentが設定されていることが確認できます。

./bin/rake "test_task:run[test]"
{:environment=>"test"}
"task finished!"

task run: :environment と記述することで正しくtaskが実行できるようになりました。

ただ、bin/rakeの場合は:environmentが設定されていなくてもエラーにならなかったのはなぜでしょうか?

これは bin/rake 内でspringが読み込まれていたためモデルデータがプリロードされていたのが原因でした。

#!/usr/bin/env ruby
begin
  load File.expand_path("../spring", __FILE__)  # <= ここでspringを読み込み
rescue LoadError
end
require_relative '../config/boot'
require 'rake'
Rake.application.run

これはハマりました。。。

思い込みの力は恐ろしいです。

もう同じことにハマらないようにメモを残しておきます。

task作成時は:environmentの指定方法にご注意ください。

今回の検証内容はruby 2.2.3, Rails 4.2.1で確認しました。

参考

[まとめ] 現在開催中のKindleセール情報はこちら