Best way to load module/class from lib folder in Rails 3?

139,122

Solution 1

As of Rails 2.3.9, there is a setting in config/application.rb in which you can specify directories that contain files you want autoloaded.

From application.rb:

# Custom directories with classes and modules you want to be autoloadable.
# config.autoload_paths += %W(#{config.root}/extras)

Solution 2

# Autoload lib/ folder including all subdirectories
config.autoload_paths += Dir["#{config.root}/lib/**/"]

Source: Rails 3 Quicktip: Autoload lib directory including all subdirectories, avoid lazy loading

Please mind that files contained in the lib folder are only loaded when the server is started. If you want the comfort to autoreload those files, read: Rails 3 Quicktip: Auto reload lib folders in development mode. Be aware that this is not meant for a production environment since the permanent reload slows down the machine.

Solution 3

The magic of autoloading stuff

I think the option controlling the folders from which autoloading stuff gets done has been sufficiently covered in other answers. However, in case someone else is having trouble stuff loaded though they've had their autoload paths modified as required, then this answer tries to explain what is the magic behind this autoload thing.

So when it comes to loading stuff from subdirectories there's a gotcha or a convention you should be aware. Sometimes the Ruby/Rails magic (this time mostly Rails) can make it difficult to understand why something is happening. Any module declared in the autoload paths will only be loaded if the module name corresponds to the parent directory name. So in case you try to put into lib/my_stuff/bar.rb something like:

module Foo
  class Bar
  end
end

It will not be loaded automagically. Then again if you rename the parent dir to foo thus hosting your module at path: lib/foo/bar.rb. It will be there for you. Another option is to name the file you want autoloaded by the module name. Obviously there can only be one file by that name then. In case you need to split your stuff into many files you could of course use that one file to require other files, but I don't recommend that, because then when on development mode and you modify those other files then Rails is unable to automagically reload them for you. But if you really want you could have one file by the module name that then specifies the actual files required to use the module. So you could have two files: lib/my_stuff/bar.rb and lib/my_stuff/foo.rb and the former being the same as above and the latter containing a single line: require "bar" and that would work just the same.

P.S. I feel compelled to add one more important thing. As of lately, whenever I want to have something in the lib directory that needs to get autoloaded, I tend to start thinking that if this is something that I'm actually developing specifically for this project (which it usually is, it might some day turn into a "static" snippet of code used in many projects or a git submodule, etc.. in which case it definitely should be in the lib folder) then perhaps its place is not in the lib folder at all. Perhaps it should be in a subfolder under the app folder· I have a feeling that this is the new rails way of doing things. Obviously, the same magic is in work wherever in you autoload paths you put your stuff in so it's good to these things. Anyway, this is just my thoughts on the subject. You are free to disagree. :)


UPDATE: About the type of magic..

As severin pointed out in his comment, the core "autoload a module mechanism" sure is part of Ruby, but the autoload paths stuff isn't. You don't need Rails to do autoload :Foo, File.join(Rails.root, "lib", "my_stuff", "bar"). And when you would try to reference the module Foo for the first time then it would be loaded for you. However what Rails does is it gives us a way to try and load stuff automagically from registered folders and this has been implemented in such a way that it needs to assume something about the naming conventions. If it had not been implemented like that, then every time you reference something that's not currently loaded it would have to go through all of the files in all of the autoload folders and check if any of them contains what you were trying to reference. This in turn would defeat the idea of autoloading and autoreloading. However, with these conventions in place it can deduct from the module/class your trying to load where that might be defined and just load that.

Solution 4

Warning: if you want to load the 'monkey patch' or 'open class' from your 'lib' folder, don't use the 'autoload' approach!!!

  • "config.autoload_paths" approach: only works if you are loading a class that defined only in ONE place. If some class has been already defined somewhere else, then you can't load it again by this approach.

  • "config/initializer/load_rb_file.rb" approach: always works! whatever the target class is a new class or an "open class" or "monkey patch" for existing class, it always works!

For more details , see: https://stackoverflow.com/a/6797707/445908

Solution 5

Very similar, but I think this is a little more elegant:

config.autoload_paths += Dir["#{config.root}/lib", "#{config.root}/lib/**/"]
Share:
139,122

Related videos on Youtube

Vincent
Author by

Vincent

Updated on March 03, 2020

Comments

  • Vincent
    Vincent about 4 years

    Since the latest Rails 3 release is not auto-loading modules and classes from lib anymore, what would be the best way to load them?

    From github:

    A few changes were done in this commit:
    
    Do not autoload code in *lib* for applications (now you need to explicitly 
    require them). This makes an application behave closer to an engine 
    (code in lib is still autoloaded for plugins);
    
  • ben_h
    ben_h over 13 years
    You shouldn't use require within a rails app, because it prevents ActiveSupport::Dependencies from [un]loading that code properly. Instead you should use config.autoload_paths like the answer above, and then include/extend as required.
  • pupeno
    pupeno about 13 years
    Thank you @Mike, I was going to do what you did, it was good to see an explanation of why it's bad, thanks for not removing the answer.
  • Mike
    Mike over 12 years
    what about include 'mymodule' if you just want to load one module?
  • severin
    severin over 11 years
    Why is this Ruby magic? Ruby just provides the Module#autoload function which you can use to command a file being loaded when accessing an (undefined) constant (see ruby-doc.org/core-1.9.3/Module.html#method-i-autoload). The matching of module/class names to directories/files is in my opinion done in Rails/ActiveSupport (e.g. here: github.com/rails/rails/blob/…). Am I wrong?
  • Timo
    Timo over 11 years
    Yes, I believe you are correct. I was too hasty to "correct" my original answer when Zabba pointed out its "flaw". Let me update my answer a bit more to clarify this issue.
  • Tom Harrison
    Tom Harrison over 11 years
    Note @thankful's answer also if you are looking to autoload the entire subtree of app/lib.
  • pedz
    pedz over 11 years
    I spent a half hour or so mucking about. I needed (wanted) to autoload Sprockets::JSRender::Processor. The path for that can be found by getting into rails console and doing "Sprockets::JSRender::Processor".underscore and disvoering that it is "sprockets/js_render/processor" (with .rb added) HTH someone.
  • user14322501
    user14322501 about 11 years
    You just saved my sanity. ~deep sigh of relief~ thank you so much for sharing :)
  • mjnissim
    mjnissim almost 11 years
    Thank you for this most helpful comment. I didn't understand why some modules were behaving like they did until I read your comment. Blessings on you!
  • Tyler Collier
    Tyler Collier almost 11 years
    This is a critical distinction to understand. Thanks for this.
  • Jacob
    Jacob almost 11 years
    This has the nasty side effect of totally clobbering Rails namespacing conventions. If lib/bar/foo.rb defining Bar::Foo appears before lib/foo.rb defining Foo in the autoload lookup, then you'll get confusing errors like Expected lib/bar/foo.rb to define constant Foo if you try to load lib/foo.rb by referring to the Foo constant.
  • Jason
    Jason over 10 years
    That's because ./lib/this_file.rb looks in the current directory (in Rails console, that would be your Rails root), and /lib/this_file.rb looks for that as an absolute path. Example: ./lib/this_file.rb = /var/www/myrailsapp/lib/this_file.rb, /lib/this_file.rb = /lib/this_file.rb
  • Dennis
    Dennis over 9 years
    @ben_h Should you not require from anywhere in a Rails app? In a rake task I'm currently require-ing and include-ing a module that lives in lib/. Should I not be doing that?
  • Dennis
    Dennis over 9 years
    @ben_h My search reveals that it's common to require your lib/ code (e.g. this blog post, this SO answer). I'm still unsure about the whole thing. Can you give more evidence behind the claim for not using require?
  • Aaron Brager
    Aaron Brager almost 8 years
    Very helpful: "Any module declared in the autoload paths will only be loaded if the module name corresponds to the parent directory name."
  • Besi
    Besi about 6 years
    The links are dead
  • BeeZee
    BeeZee over 3 years
    Thanks for posting that GH issue link. Lots of other project make ref to it as they moved their lib to app\lib.