rails: Bug with loading top-level class if namespaced is defined.
Steps to reproduce
Create 2 files:
app/controllers/concerns/example.rb
module Concerns::Example; end
app/models/example.rb
class Example < ActiveRecord::Base; end
Next start console rails c [1] pry(main)> Example
Expected behavior
We should get Class defined in models/example.rb
Actual behavior
When cache_classes or eager_load = false we will get
[1] pry(main)> Example LoadError: Unable to autoload constant Example, expected app/controllers/concerns/example.rb to define it from rails-3d01d00e348f/activesupport/lib/active_support/dependencies.rb:512:in `load_missing_constant’
When cache_classes and eager_load = true we will get
[1] pry(main)> Example NameError: uninitialized constant Example
System configuration
Rails version: 5.0.0 (3d01d00e348f) 5-0-stable but tested also on cc7bd7cc394d0bbd9ead5763e077c8965c305755 (beta3)
Ruby version: ruby 2.3.0p0
About this issue
- Original URL
- State: closed
- Created 8 years ago
- Comments: 16 (16 by maintainers)
Hi guys, what about regular module name (not named concern), is that the expected behavior also? see question
Cause I’m having similar issue now when trying to upgrade.
Yes, @fxn is right on all points. This is working as intended. Concerns don’t have a namespace for the same reason that Controller isn’t a namespace. It’s a technical grouping that needn’t leak into domain language for the modules themselves.
@fxn thanks for clarification. Can you tell me why concerns directories are treated as root directories? Why not just load them like controllers namespaces? like admin/example_controller.rb ?
No, it has not changed.
In general, our naming conventions say that a subdirectory corresponds to a module or class acting as namespace. Well, this convention was broken with the introduction of the concerns subdirectories, now we have an exception.
Your example works because for whatever reason in 4.2.5 app/models/example.rb is found first, so it loads OK. That is the undefined part of this, it worked by chance, but you cannot rely on it.
Why does
Concern::Example
work? Because of the unfortunate exception. Since app/controllers is in autoload paths, Rails is able to resolveConcerns::Example
loading the relative filename concerns/example.rb.Do you see the problem introduced with those concerns directories in autoload paths? The file app/controllers/concerns/example.rb maps to two possible constants instead of one. The intended one is
Example
, but accidentally it would also autoloadConcerns::Example
.Bottom line: Do not use
Concerns
as a namespace, and keep your filenames mod autoload dirs unique.@fxn Then filenames should be unique? Then why we have Namespaces? And what about cache_classes and eager_load = true. Why Example is then uninitialized constant?
I don’t believe this is expected behavior. What about namespaced controllers? Like
admin/posts_controller.rb vs posts_controller.rb ?
This also should not work? Case is exactly the same.
Just remember this was working in rails 4.2 😃
Still on my phone.
The conflicting file is called example.rb, therefore a candidate for autoloading the top-level constant
Example
. Rails leaves unespeficied what happens when there are more than one file with the same name mod autoload dirs. There shouldn’t be.For whatever reason, the one with the module is picked first, Rails detects it doesn’t define what it should define, and that is an error condition.