Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I force a namespace for Rails autoloading without nesting in a folder?

Is there a way to tell Rails that all files in a certain folder are contained in a certain namespace?

Ie.

I have a file bar.rb in app/foo. Rails will assume this file defines Bar, but instead I want this file to define Foo::Bar.

I know I can achieve this by adding my root to Rails' autoload paths, but that isn't a real solution. Is there any other way I can tell Rails that all files within app/foo reside in the Foo namespace?

EDIT: File tree for clarification

app
  assets
  controllers
  models
  foo
    bar.rb
    quux.rb

I would like to be able to define Foo::Bar and Foo::Quux in respectively bar.rb and quux.rb, while also using Rails autoloading. Without having to resort to the tree structure as below:

app
  assets
  controllers
  models
  foo
    foo
      bar.rb
      quux.rb
like image 249
Ewout Kleinsmann Avatar asked Mar 11 '15 11:03

Ewout Kleinsmann


People also ask

How does Rails autoloading work?

Rails automatically reloads classes and modules if application files in the autoload paths change. More precisely, if the web server is running and application files have been modified, Rails unloads all autoloaded constants managed by the main autoloader just before the next request is processed.

What is autoload in Ruby?

The Module#autoload method registers a file path to be loaded the first time that a specified module or class is accessed in the namespace of the calling module or class.

What is namespace in Ruby?

Namespace in Ruby allows multiple structures to be written using hierarchical manner. Thus, one can reuse the names within the single main namespace. The namespace in Ruby is defined by prefixing the keyword module in front of the namespace name. The name of namespaces and classes always start from a capital letter.

Why don’t developers use namespaces in rails?

In general, Rails doesn’t encourage developers to factor code into namespaces. Most apps end up with hundreds of files and very few directories in app/models and similarly flat hierarchies in other app directories.

What is the use of autoload in rails?

More precisely, if the web server is running and application files have been modified, Rails unloads all autoloaded constants just before the next request is processed. That way, application classes or modules used during that request are going to be autoloaded, thus picking up their current implementation in the file system.

Why doesn’t rails use Ruby’s built-in autoload?

It can’t simply use Ruby’s built-in autoload, because that needs to know both the name and file location of each constant up front, and Rails knows neither of these things at boot.

How do you name a file in a Rails application?

In a Rails application file names have to match the constants they define, with directories acting as namespaces. For example, the file app/helpers/users_helper.rb should define UsersHelper and the file app/controllers/admin/payments_controller.rb should define Admin::PaymentsController.


1 Answers

You can autoload files with namespaces corresponding to directories inside /app by adding /app to your autoload paths in your application config.

# config/application.rb
config.autoload_paths << "#{config.root}/app"

I'm not sure whether or not this is what the author of the question meant by

I know I can achieve this by adding my root to Rails' autoload paths, but that isn't a real solution.

But this is definitely a real solution and the correct one. Doing this is fine and not at all unusual in my experience.

There are only minor side effects which are the cost of adopting the inconsistent naming conventions you want. If you refer to an undefined constant that matches the name of another directory in the app you'll get a slightly different error message.

# Models::Foo => LoadError (Unable to autoload constant Models::Foo, expected /app/models/foo.rb to define it)

But if you have existing namespaces that match directories in app loading will still work fine. Here's what Rails is doing:

It takes the paths added to config.autoload_paths and adds them to the defaults (the directories under app: app/models, app/controllers, app/foo etc). Then when a constant is referenced that is not already loaded Rails proceeds through those paths, looking for paths that match the constants. So when you reference Foo::Bar it looks for app/models/foo/bar.rb, app/controllers/foo/bar.rb etc. until it finds a file that defined Foo::Bar. All we're doing is adding app/foo/bar.rb to that lookup.

like image 170
Jack Noble Avatar answered Sep 22 '22 17:09

Jack Noble