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)

Most upvoted comments

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 resolve Concerns::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 autoload Concerns::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.