6

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
Ewout Kleinsmann
  • 1,287
  • 1
  • 9
  • 20
  • Could you update your question with sort of "tree" of how your files are structured? It would be easier to understand what you're trying to achieve. – Paweł Dawczak Mar 11 '15 at 11:21
  • Is there any particular reason that you would like to avoid putting your code in *app/lib/*, instead of directly into *app/*? If not, try point 1 from [this](http://stackoverflow.com/a/19650564/4381282) answer. This approach will make any new subdirectories loaded automatically. Hope that helps! – Paweł Dawczak Mar 11 '15 at 13:23
  • I'd really love an answer to this question as well. We have a similar situation, and nesting our modules in a directory makes our paths a little more convoluted. – LandonSchropp Oct 01 '19 at 22:29

3 Answers3

3

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.

Jack Noble
  • 2,018
  • 14
  • 12
0

Rails doesn't assume namespace, it assumes path to the source file depending on a namespace, so:

$ cat app/foo/bar.rb 
module Foo
  class Bar
    def bar
      puts "i'm here"
    end
  end
end

$ rails c
2.2.0 :007 > b=Foo::Bar.new
 => #<Foo::Bar:0x00000005272770> 
2.2.0 :008 > b.bar
i'm here
 => nil
Eugene Petrov
  • 1,578
  • 1
  • 10
  • 22
  • 1
    Strange, when I do exactly the same, it gives me: `LoadError: Unable to autoload constant Bar, expected /home/(...)/app/foo/bar.rb to define it` – Ewout Kleinsmann Mar 12 '15 at 14:41
  • Strange is that rails fails to load looking at the proper path to the file... I'd double check that the (....) contains correct user name/project name... and maybe check file permissions too – Eugene Petrov Mar 12 '15 at 16:48
0

You can try adding the folder you Rail's autoload paths configuration

# config/application.rb
# Rails 4
config.autoload_paths << Rails.root.join("app", "foo")

# Rails 5+
 config.eager_load_paths << Rails.root.join("app", "foo")
stevendaniels
  • 2,992
  • 1
  • 27
  • 31