Short answer: Sometimes.

In an earlier post, I talked about how we define namespaced classes in Rails.

That is, should we use this form?

Nested Class Definition
module MyModule
class MyClass
# ...
end
end

or this one?

Shorthand Class Definition
class MyModule::MyClass
# ...
end

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.