Or, “Why using send doesn’t work as exptected”
So, I’ve been banging away at a Rails plugin the last few days. One of the things that I initially did was to add an initializer that extended the models from the app to avoid needing to muck-up the model, e.g.:
[project root]/config/initializers/acts_as_foo.rb
SomeModel.send(:acts_as_foo, :bar)
It seemed so clean and elegant. But I noticed that my class variables were being trashed when I tried to access them and was befuddled for a day or so.
It turns out what happens is that Rails reloads all of your models (and, in fact, all classes) for every request while in debug mode. At some level in my brain I knew that, but it didn’t click that when that happens initializers aren’t fired as well, so anything that’s being fired from an initializer that will be trashed on reload won’t be there after the first request. (Though it works in production mode.)
Eventually, after much debugging, it clicked what was happening. But I wasn’t quite content to just burden plugin users by modifying each model. I stumbled across this StackOverflow entry which suggested you could achieve the same by modifying environment.rb’s config block. But really, that just shifted the inelegance around. However, the to_prepare bit set me along the right path.
The elegant solution that I eventually found comes from using the dispatcher:
require 'dispatcher' Dispatcher.to_prepare do SomeMode.send(:acts_as_foo, :bar) end
And there friends we have it: a way to send something to a model from an initializer that will be reloaded every time classes are loaded, or only once during production mode, and every request in development mode.