Rails Autoload Good Practices

I started using rails autoload to load files in my lib folder of http://www.mes-courses.fr. Before that, I had been using hand written require statements, and later hand written autoload statements. Rails autoload are by far the best approach for this. It has a few pitfalls though. Here are the best practices I discovered so far.

  • Use autoload to load files in your lib folder. In config/application.rb
    1
    
    config.autoload_paths += %W(#{config.root}/lib)
    
  • Inside the lib folder, organize your files with directories and use corresponding modules to create logic namespaces
  • Don’t declare the namespaces in a single line like this :
    1
    2
    3
    4
    5
    
    module MyApp::Utils
     class FileHelper
     ...
     end
    end
    
    If ever this file is autoloaded first, you will get an error like “undefined constant MyApp”, it gets more likely with deeper namespace structure. Prefer the following nested declaration :
    1
    2
    3
    4
    5
    6
    7
    
    module MyApp
     module Utils
       class FileHelper
         ...
       end
      end
    end
    
  • Doing “include MyApp::Utils” to include the have access to Utils members (ie “using namespace” in C++) will not work as well as with explicit requires. So if it does not work well, prefer to use “FileHelper = MyApp::Utils::FileHelper”
  • Whenever you are using a base class, I found out that autoload does not always manage to load the base class correctly, in this case, explicitly requiring the base class fixes the issue.
  • If ever you try to monkey patch one of your class directly (in a test for example), the real class might not get autoloaded since it is already declared in the monkey path :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    class MyApp::Engine
      ...
    end
    describe MyApp::Engine do
      before :each do
        @engine = MyApp::Engine.new("name")
        ...
       end
       ...
    end
    
    This might trigger an error like “wrong number of arguments for MyApp::Engine.new”. Knowing MyApp::Engine from the spec file, rails does not try to autoload the other part ! Here is how I fixed this
    1
    2
    3
    4
    5
    
    module MyApp::EngineExtras
      ...
    end
    MyApp::Engine.send(:include, MyApp::EngineExtras)
    ...
    

This works as expected.

At the moment, I still have an issue I did not manage to fix neatly : how can we include namespaces in spec and cucumber step files without polluting the global namespace ?

Comments