nyarn.tech

にゃーん

Rails5でapp以下のディレクトリが読み込まれないとき

Railsのautoload問題でまたハマった。
app以下のディレクトリはデフォルトでproductionでは eager_load 、developmentでは autoload される。
なので、アプリケーションから呼ぶコードはだいたいapp以下のディレクトリに置いておけば問題ない、
はずが読み込まれなかった。。。

具体的にはこんな感じのディレクトリ構成で

$ tree -d  -L 1  ./app/
./app/
├── assets
├── controllers
├── helpers
├── hoge
├── javascript
├── jobs
├── mailers
├── models
├── services
└── views

hoge/fuga.rbや、services/foo_service.rbを置いていたとする。

このときrails consoleから Fugaを呼ぼうとすると NameError (uninitialized constant Fuga) が発生した。
ActiveSupport::Dependencies.autoload_paths を実行して autoload されるディレクトリを確認すると、
たしかにhogeやservicesが含まれていない。。

解決策

Springのキャッシュが効いていて、app以下にディレクトリを追加してもRails側が検知できていなかった。
そのため bin/spring stop を実行することでキャッシュがクリアされてautoloadが行われる。

ちなみに rails serverではSpringのキャッシュが読まれないのでこの現象は起こらない。
あとは、Gemfileやconfigを書き換えてもキャッシュがクリアされる。

Springのキャッシュについては公式のREADMEが参考になる

github.com

また autoload_paths でディレクトリが見えるのに、classが読み込まれないときもSpringを再起動すればよい。

このケースはSpringに明示的にそのディレクトリ (app/servicesapp/libs)をwatchして
変更を検知するように設定すれば再起動の必要がなくなる。

config/spring.rbでwatchしたいディレクトリを指定できるので

%w(
  .ruby-version
  .rbenv-vars
  tmp/restart.txt
  tmp/caching-dev.txt
  app/services
).each { |path| Spring.watch(path) }

のように書き足して、もう一度Springを再起動すると設定できる。