Search code examples
ruby-on-railsrubyrequire

What is the difference between Rails.application.config.autoload_paths and standard Ruby require/require_relative?


I see that the following configuration in application.rb:

config.autoload_paths += %W(#{config.root}/app/models/custom_pack/base)
config.autoload_paths += Dir["#{config.root}/app/models/custom_pack/custom_container/**/"]
config.autoload_paths += Dir["#{config.root}/app/models/custom_pack/helpers/**/"]
config.autoload_paths += Dir["#{config.root}/app/models/custom_pack/extensions/**/"]

is in the autoload_paths:

Rails.application.config.autoload_paths.grep /custom/
 => ["/Users/dviglione/projects/core/app/models/custom_pack/base", "/Users/dviglione/projects/core/app/models/custom_pack/custom_container/", "/Users/dviglione/projects/core/app/models/custom_pack/helpers/", "/Users/dviglione/projects/core/app/models/custom_pack/extensions/"]

But these files are not being loaded. Because I get an error:

 `method_missing': undefined method `create_element' for #<MyElement:0x007f82eca39898> 

That create_element method is defined in one of the files that should have been loaded.

However, when I use require/require_relative, it does work:

# /initializers/my_initializer.rb
require "custom_pack/base"

# models/custom_pack/base.rb    
require_relative 'custom_container/base'
require_relative 'custom_container/parent'
require_relative 'custom_container/child'

Dir[File.join(File.expand_path("../helpers", __FILE__), "*_helper.rb")].each { |file| require file }
Dir[File.join(File.expand_path("../extensions", __FILE__), "*_adapter.rb")].each { |file| require file }

From what I read from the documentation, when you use require 'erb', Ruby looks for the file in the directories listed in $LOAD_PATH. That is, Ruby iterates over all its directories and for each one of them checks whether they have a file called "erb.rb". If it finds any of them, the interpreter loads it and ends the search. Otherwise, it tries again in the next directory of the list. If the list gets exhausted, LoadError is raised. For autoloading, the idea is that when a constant like Post is hit and missing, if there's a post.rb file for example in app/models Rails is going to find it, evaluate it, and have Post defined as a side-effect. Rails has a collection of directories similar to $LOAD_PATH in which to look up post.rb. That collection is called autoload_paths.

So why does require/require_relative work, but autoload_paths does not?


Solution

  • There's a number of things that could be going on here.

    1.If a file is following the path: lib/foo/bar.rb the class needs to be defined like:

    class Foo::Bar
    end
    

    2.Autoload also lazy loads files, which means your models are only loaded when you call them. For example:

    puts "I was loaded!"
    
    class MyLibrary
    end
    
    irb(main):001:0> require 'mylibrary'
    I was loaded!
    => true
    
    irb(main):001:0> autoload :MyLibrary, 'mylibrary'
    => nil
    irb(main):002:0> MyLibrary.new
    I was loaded!
    => #<MyLibrary:0x0b1jef>
    

    As for the actual usage of autoload, it's recommended to start using require instead. In theory autoload sounds nice but it can cause problems when certain classes are dependent on other modules. Because of this autoload is in the process of being deprecated.