Lighthouse has a new layout. Prefer the old one? Return to the old layout, and switch back any time from the link at the top of each page.

This project is archived and is in readonly mode.

Autoloading behaves weird when class caching is turned off

#6320

The algorithm of auto-loading paths in Rails 3 is a bit weird when class caching is turned off. Assuming you add all subdirectories under app/models to the load paths by using the new "config.autoload_paths" setting in config/application.rb:

config.autoload_paths += Dir["#{config.root}/app/models/**/"]

And you have a subdirectory under app/models with name class1 and a file under this subdirectory with name class1.rb. When you are referring any classes under this subdirectory in class1.rb, you have to make sure they are in the namespace of Class1. Otherwise Rails will complain about your referred class is not in the namespace of Class1. In more details, if you have a directory structure like this:

- app
  - models
    - class1
      - class1.rb
      - class1_reference.rb

When you refer to Class1Reference in Class1, you will get "Expected app/models/class1/class1_reference.rb to define Class1::Class1Reference". If the subdirectory is not named class1, strangely this example will work... Let's take a look at a comparing directory structure:

- app
  - models
    - not_class2
      - class2.rb
      - class2_reference.rb

This second example have everything the same as the first one except that the subdirectory name (not_class2) is different from the file name (class2). Surprisingly, this example works as expected. The code of this two examples are available here: https://github.com/jingweno/loading_path_gotchas_in_rails3/tree/mas....

Reported by Owen · January 21st, 2011 @ 02:11 AM

State: wontfix
Milestone: none
Assigned to: nobody
Importance: Low

Activity

  1. Owen
    Owen

    More detail is available at my blog post with comparing examples: http://owenou.com/2011/01/20/loading-path-gotchas-in-rails3.html

    January 21st, 2011 @ 12:56 PM

  2. tsailipu
    tsailipu

    I am investigating this potential bug with a different twist. Glad to find this post and bug report.

    My example is having:
    app/models/user.rb
    app/models/Modu/user.rb

    and have autoload_path::
    config.autoload_paths += %W(#{config.root}/app/models/Modu)

    Where app/models/user.rb is simply:

     class User
       puts "hi User"
     end
    

    and app/Modu/user.rb is simply:

     module Modu
       class User
       puts "hi Modu::User"
       end
     end
    

    Upon "rails c" or "rails server," the following exception will occur:

    hi Modu::User
    /Library/Ruby/Gems/1.8/gems/activesupport-3.0.3/lib/active_support/dependencies.rb:492:in `load_missing_constant': Expected /Users/apps/xyz/app/models/Modu/user.rb to define User (LoadError)
    

    January 21st, 2011 @ 08:19 PM

  3. tsailipu
    tsailipu

    BTW, the bug I reported above happens regardless of caching.

    January 25th, 2011 @ 05:05 AM

  4. José Valim
    José Valim
    • State changed from new to wontfix
    • Importance changed from to Low

    Rails is panicking when you add subdirectories of a given directory to autoload paths. In both cases, you will have in the load paths both app/models/ and app/models/client1 (or app/models/Modu) and then Rails heuristic to find which model to load is picking up the one you do not expect to. The ideal way is to not include subdirectories in autoload paths and reference the constants using namespaces.

    January 25th, 2011 @ 05:41 AM

  5. Owen
    Owen

    Hi José,

    How about my example? I don't have two classes with the same name. I have a subdirectory named class1 and a file under it named class1.rb. If class1.rb refers to class1reference.rb, if will also raise exception. I have documented it with a blog post: http://owenou.com/2011/01/20/loading-path-gotchas-in-rails3.html. Please take a look. Thanks.

    January 25th, 2011 @ 04:36 PM

  6. José Valim
    José Valim

    Owen, but you still have app/models/client1 which is a directory. Rails get confused about defining a module with client1 (which it does automatically for directories) or loading the module (which would define a class). Again, the origin of the issue is the same, having a subpath inside another in autoload_paths. This causes ambiguity and any way Rails try to solve it will likely clause conflicts.

    January 25th, 2011 @ 05:17 PM

  7. Owen
    Owen

    Thanks for the clarification, José. So what's the best practice to manage subdirectories in Rails 3? I have too many models and I would like to manage them in different directories. Should I still organize them by having a base file "require" all the files in the subdirectories?

    January 25th, 2011 @ 05:35 PM

  8. tsailipu
    tsailipu

    Sounds like Rails 3 could improve the loading logic: remember the path to the class, then when asked and if there are classes with the same name, check to see whether the references have the path, if not, complain. Bailing out during "rails c" or "rails server" when an autoload path contains the same class name as another in the default app/models path renders Rails 3 brittle.... BTW, Jose, I don't know what changed, but the example I quoted doesn't have a problem in Rails 2 (e.g. 2.3.5). So I don't know why you wouldn't think this is something worth looking into in Rails 3. Thanks for the explanation!

    January 25th, 2011 @ 05:49 PM

  9. Owen
    Owen

    Hi José, can you provide recommendations on how to manage subdirectories in Rails 3. Thanks!

    January 28th, 2011 @ 09:28 PM

  10. myopenid.com (at bobics)
    myopenid.com (at bobics)
    • Tag changed from class cache, class reloading, autoload to class cache, class reloading, autoload, sti

    I realize this ticket is marked "wontfix" but I'd like to add a use case where loading subfolder without a namespace makes sense. My current issue is that I want to use Single Table Inheritance and organize the sub classes in a folder. I should be able to use:

    class Automobile ... end
    class Car < Automobile ... end
    class Truck < Automobile ... end

    And put car.rb and truck.rb under a folder "automobile" without having to scope them. If I use the following:

    class Automobile::Car < Automobile ... end

    Then I get "Automobile::Car" in the "type" column for STI. This is definitely NOT what I want, I'd rather just have "Car".

    March 30th, 2011 @ 07:47 AM