Subscribed unsubscribe Subscribe Subscribe

@kyanny's blog

Write down what I learnt. Opinions are my own.

config.ru の無い Rails アプリケーションで Rails.root が $HOME になりハマった話

前提

config.ru が無い Rails アプリケーション(と呼んでいいのかわからないが)を作ることがたまにある。

  • Model を複数の Rails アプリケーション間で共有したいため抽出した
    • 他の Rails アプリケーションからは gem として利用する
  • バッチ処理用の rake タスク置き場
    • 前述の Model 専用 Rails アプリケーションをロードした上で、もっぱら lib/tasks 以下に .rake ファイルを実装する

この、後者のほうのアプリケーションで、あるときから lib/tasks 以下にある rake タスクを一切読み込むことも実行することもできなくなってハマった、という話。

結論

そのアプリケーションより上階層のディレクトリに config.ru ファイルがあると Rails.root が狂う。

↓ダメな例。 mycompany ディレクトリ直下に config.ru があるのがまずい。

/Users/kyanny/mycompany/
├── config.ru             <-- ダメ!!
└── myapplication
    ├── CONTRIBUTING.md
    ├── Gemfile
    ├── Gemfile.lock
    ├── README.md
    ├── Rakefile
    ├── app
    │   ├── helpers
    │   ├── mailers
    │   └── models
    ├── bin
    │   ├── bundle
    │   ├── console
    │   ├── rails
    │   └── rake
    ├── config
    │   ├── environments
    │   ├── initializers
    │   └── locales
    ├── db
    ├── lib
    │   ├── data
    │   └── tasks
    ├── log
    │   └── development.log
    └── spec
        ├── factories
        ├── lib
        ├── models
        └── support

解説

Rails.root は通常 Rails アプリケーションのルートディレクトリを指す。ファイルシステム上の /app にアプリケーションが置いてあるとしたら、 Rails.root は /app になる。

これは Rails::Application.find_rootRails::Engine.find_root_with_flag というメソッドで処理されており、 find_root_with_flag はカレントディレクトリを起点に config.ru というファイルが見つかるまでパスを上階層に向かってさかのぼっていく。 config.ru が見つからなければ第三引数で与えられた default == Dir.pwd にフォールバックする。

Rails アプリケーションにおいて rake コマンドはルートディレクトリで実行するため、 default はアプリケーションのルートディレクトリになる。ふつうの Rails アプリケーションはルートディレクトリに config.ru があるため、そこで探索が終わり、 Rails.root は期待どおりの値になる。今回のようなふつうでない Rails アプリケーションの場合でも、上位のディレクトリに config.ru が見つからなければ default にフォールバックされるため、 Rails.root は期待どおりの値になる。

Rails::Application.load_tasks は Rails.root + "lib/tasks" のようなパスから rake タスクをロードするので、 Rails.root がアプリケーションのルートディレクトリをさしていれば、期待どおり lib/tasks 以下の rake タスクを実行できる。もし Rails.root がおかしな値になってしまうと、そのパス + "lib/tasks" 以下を探しにいってしまい、おかしなことになる。

今回、自分の環境では、ふつうでない Rails アプリケーションを $HOME/ghq/github.com/mycompany/myapplication のようなパスに置いていた。そして何気なく $HOME/config.ru というファイルを作った。すると、上記のロジックに従って、この Rails アプリケーションが起動するときに Rails.root が $HOME になってしまい、 rake コマンドは $HOME/lib/tasks 以下を探しにいくようになった。その結果、アプリケーション内に実装した rake タスクが利用できなくなってしまった。

原因がわかればあっけないもので、 $HOME/config.ru を削除したら解決した。また、アプリケーションを /tmp 以下など、 $HOME 配下ではない位置に置いたところ、やはり解決した。

何が悪いのか見当もつかず、週末に OS のクリーンインストールをするしかないと諦めていたところ、「Rails.root や Bundler.root の値はどうなっている?」と同僚にヒントをもらい、それが解決の糸口になった。多謝。

教訓

正直、今回のケースは自力で解決策を思いつくのはちょっと無理だったかな、、と思うが、

  • 落ち着いて print デバッグしたり、 debugger で潜る
  • 特定のライブラリを疑ってかかるのはやめて、地道に一歩ずつ調査する
  • ファイルやディレクトリのあるパスに問題があるか見極めるために、 /tmp 以下などに移動してみる