Does Rails Define Modules For Me?
Short answer: Sometimes.
In an earlier post, I talked about how we define namespaced classes in Rails.
That is, should we use this form?
or this one?
As I was researching that post, I was trying to understand where the
namespaces were coming from when using the shorthand form of class
MyModule::MyClass
. There was no declaration of MyModule
as a
module or class in any of my code. I also know that, in plain Ruby, I
can’t define a class that way without first defining MyModule
.
If I try, I’ll get a NameError
.
So how was my code loading into Rails without blowing up?
I searched Google with a query that matches the title of this post and didn’t find an answer, so I decided to research it and write the answer myself.
After discovering and using some handy tracing tips for the Rails autoloader, I was able to learn that Rails does indeed define modules in some cases.
If MyModule
is already defined as a class or namespace, the
autoloader never comes into play. But if it isn’t defined, the
autoloader tries to find and load a definition for me.
The Rails autoloader lives in ActiveSupport::Dependencies
. Its
workhorse method is load_missing_constant
, which is called from a
const_missing
handler that the autoloader injects into Module
.
load_missing_constant
looks for a file that might define the
constant (MyModule
in this example). If it can’t find one,
it uses autoload_module!
to dynamically define MyModule
as a
module as long as autoloadable_module?
agrees.
autoloadable_module?
returns true
if the potential module name
matches a directory name somewhere in one of my configured autoload
directories.
If there is no such directory, then the autoloader tries a few other
things. If none of them work, it will ultimately raise the same
NameError
we get from stock Ruby.
So, if I have a directory like app/models/my_module/
or
app/controllers/my_module
in my application, then
autoloadable_module?
will return true
and autoload_module!
will
dynamically define MyModule
as a module. If not, I’ll get an error.
Mystery solved after an interesting morning of spelunking.