Namespacing Models et al

Just ran into this for the first time and thought it was worth talking about: because models and controllers and such just live in the root namespace, they can be shadowed when importing a gem.

My proposal (for discussion) is that Rails could put all the app classes inside the module that exists for the app (as defined in config/application.rb ). Autoloading, auto-lookup, etc would use the fully-qualified name, but the unqualified name would still work everywhere in app code because we’re inside the module. Then if an overlapping name exists in a gem we can get at it with ::Name, etc.

Thoughts?

I support the overall idea.

Rails does not control the namespace.

User is defined when user.rb is evaluated. Autoloading finds user.rb if the User constant is unknown, and admin/user.rb if Admin::User is unknown, but namespace usage is up to the application.

In my view, namespacing is something code meant to be shared should do, like a gem. Code not mean to be shared does not need it generally speaking in my opinion.

In the rare event of a constant clash because a gem does not use a namespace then you can workaround that particular situation, but I personally wouldn’t recommend apps to be namespaced as a standard practice.

The case I’ve run into the gem is namespaced, but with a name that clashed with one of my model names. Rails as currently implemented does not control the namespace, but given how the autoloader works I don’t see any reason it couldn’t. A middleground would be to have the autoloader expect classes to be so namespaced, and make sure the activerecord stuff, etc, expected it as well – that has the advantage of being less magical, and the disadvantage of being less magical.

The case I've run into the gem *is* namespaced, but with a name that

clashed with one of my model names. Rails as currently implemented does not control the namespace, but given how the autoloader works I don't see any reason it couldn't. A middleground would be to have the autoloader expect classes to be so namespaced, and make sure the activerecord stuff, etc, expected it as well -- that has the advantage of being less magical, and the disadvantage of being less magical.

It is even more unusual to have a clash with a top-level namespace of a gem :).

I don't think automatic namespacing is the way to go, but out of curiosity if user.rb has

    class User < AR::Base     end

and UsersController references User and triggers loading that file, how would you put User under a namespace? What about the name of the class? What about the nesting by which the constants in User are resolved?

Say the Rails app is named Foo and so the module Foo is the one in config/application. Well then when the autoloader sees a request for constant User for the first time, it would look for a files named user.rb with a class in them named User and load them as module Foo; load ‘user.rb’; end – because UsersController was also so namespaced when it was loaded, further references to User will resolve to Foo::User (because that’s how the scoping/namespacing search rules work in Ruby anyway).

A million times no. The autoloader can be confusing enough without it magically gluing namespaces onto things - making autoloading User result in different classes than just doing a require ‘user’ seems very bad.

—Matt Jones

Well, sure, the alternative I suggested in my first email, if doing it automatically were too magical, was to require and/or allow users to put the namespace on themselves.

This doesn't do what you want: load and require evaluate code resetting the nesting, that's why you can require a file wherever you want, inside a method or whatever, without altering the context of the code in the file. You can at most pass a flag to get an anonymous module created by the interpreter injected.

The only way to do that that I can think of is to manually remove the constant from Object and add it to the app module (in which case the name of the class would not reflect the namespace), or else use some method from the eval family passing File.read('user.rb').

Any of the options are terrible in my view, expectations are broken all over the place.

Let me add a remark here: I personally dislike the word "magic" for Rails features. I believe it has been abused. Everything is deterministic and known, there is nothing magical. has_many has a dozen options and you can control everything. What Rails provides are carefully chosen defaults and conventions that work together in a really integrated way. Everything designed so that we work as less as possible in the most common case.

End of tangent!

Well, sure, the alternative I suggested in my first email, if doing it

automatically were too magical, was to require and/or allow users to put the namespace on themselves.

But Rails supports namespaces doesn't it. If you want a namespace, then program a namespace!

Rails is not going to force or require namespaces in any way to applications, they are just a directory and module away. (If there were practical issues then we could study them.)

If we throw out the magical version (which seems a popular opinion, and is certainly quite fine with me), does letting the autoloader find AppModule::User in app/models/user.rb really break that many expectations? That and having ActiveRecord strip the AppModule prefix off classes for the purposes of table naming (probably) I think would be exactly the same as the “auto namespace” version of the proposal, only without anything being automatic.

That is not Ruby.

As a Ruby programmer, if I see

    class User < AR::Base     end

That User has to belong to Object.

If an application wants AppModule::User then it can define it that way and have autoloading working and everything. Just use Ruby.

If we throw out the magical version (which seems a popular opinion, and is certainly quite fine with me), does letting the autoloader find AppModule::User in app/models/user.rb really break that many expectations? That and having ActiveRecord strip the AppModule prefix off classes for the purposes of table naming (probably) I think would be exactly the same as the “auto namespace” version of the proposal, only without anything being automatic.

As a Ruby programmer, if I see

class User < AR::Base

end

That User has to belong to Object.

Right, this is why I specifically said we’re no longer talking about the “auto namespace” version of the proposal, because of this complaint. Which is fine.

If an application wants AppModule::User then it can define it that way and have autoloading working and everything. Just use Ruby.

I’ll have to play with this more, but I’m pretty sure if I name my classes AppModule::User then at the very least that will change what I have to name my tables (or I will have to manually set the table name on each model) – I will also have to put everything in an ‘appmodule’ folder for the autoloader, I think, and I’m not sure what the autoloader does for the case when I’m inside an AppModule context and I say User (which ruby will search for as AppModule::User at some point already, so maybe this particular part will just work as-is).

I personally dislike the word "magic" for Rails features. I believe it has been abused. Everything is deterministic and known, there is nothing magical.

+10,000,000,000

I don’t like the idea of adding more conventions to how autoload works in ActiveSupport/Rails like specifying a default namespace, etc. I’d certainly prefer for all my application-specific classes to be namespaced under the application name, but I still don’t know how to do that in a practical way. Ideally, I’d like to fix this in the Ruby level, instead of the framework/libraries one: The main problem of namespacing everything is that your routes.rb get more verbose and you’d need to wrap all your codes into a module MyApp-end block, adding an indentation level to all controllers and models for instance… So, I’d love to be able to specify some kind of C++'s namespace (or Java’s package) support to my Ruby files, but currently I don’t know about any practical way of doing that and I really believe this is a feature request to be handled by the Ruby language rather than by Rails itself…