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

12

276

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);
Traherne answered 28/7, 2010 at 19:36 Comment(0)
S
252

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)
Swansea answered 28/7, 2010 at 19:48 Comment(1)
Note @thankful's answer also if you are looking to autoload the entire subtree of app/lib.Splayfoot
F
201
# 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.

Fructification answered 18/10, 2010 at 18:49 Comment(1)
The links are deadBasrelief
G
84

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.

Gavrielle answered 22/3, 2012 at 9:59 Comment(6)
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?Rubrician
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.Gavrielle
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.Scalariform
You just saved my sanity. ~deep sigh of relief~ thank you so much for sharing :)Revell
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!Stull
Very helpful: "Any module declared in the autoload paths will only be loaded if the module name corresponds to the parent directory name."Uzziel
B
42

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://mcmap.net/q/110389/-rails-3-library-not-loading-until-require

Branham answered 8/1, 2013 at 6:52 Comment(1)
This is a critical distinction to understand. Thanks for this.Lail
H
28

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

config.autoload_paths += Dir["#{config.root}/lib", "#{config.root}/lib/**/"]
Hollie answered 2/10, 2011 at 7:46 Comment(0)
H
19

In my case I was trying to simply load a file directly under the lib dir.

Within application.rb...

require '/lib/this_file.rb' 

wasn't working, even in console and then when I tried

require './lib/this_file.rb' 

and rails loads the file perfectly.

I'm still pretty noob and I'm not sure why this works but it works. If someone would like to explain it to me I'd appreciate it :D I hope this helps someone either way.

Hemimorphic answered 15/10, 2013 at 1:44 Comment(1)
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.rbBookstall
R
7

I had the same problem. Here is how I solved it. The solution loads the lib directory and all the subdirectories (not only the direct). Of course you can use this for all directories.

# application.rb
config.autoload_paths += %W(#{config.root}/lib)
config.autoload_paths += Dir["#{config.root}/lib/**/"]
Reenareenforce answered 22/9, 2010 at 15:28 Comment(1)
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.Preoccupation
E
6

As of Rails 5, it is recommended to put the lib folder under app directory or instead create other meaningful name spaces for the folder as services , presenters, features etc and put it under app directory for auto loading by rails.

Please check this GitHub Discussion Link as well.

Enenstein answered 2/3, 2018 at 11:25 Comment(1)
Thanks for posting that GH issue link. Lots of other project make ref to it as they moved their lib to app\lib.Polyphemus
B
5

config.autoload_paths does not work for me. I solve it in other way

Ruby on rails 3 do not automatic reload (autoload) code from /lib folder. I solve it by putting inside ApplicationController

Dir["lib/**/*.rb"].each do |path|
  require_dependency path
end 
Badoglio answered 18/10, 2011 at 10:51 Comment(0)
C
4

If only certain files need access to the modules in lib, just add a require statement to the files that need it. For example, if one model needs to access one module, add:

require 'mymodule'

at the top of the model.rb file.

Caper answered 31/7, 2010 at 1:27 Comment(5)
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.Episcopalian
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.Inesinescapable
what about include 'mymodule' if you just want to load one module?Bungalow
@Episcopalian 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?Somehow
@Episcopalian 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?Somehow
I
2

Spell the filename correctly.

Seriously. I battled with a class for an hour because the class was Governance::ArchitectureBoard and the file was in lib/governance/architecture_baord.rb (transposed O and A in "board")

Seems obvious in retrospect, but it was the devil tracking that down. If the class is not defined in the file that Rails expects it to be in based on munging the class name, it is simply not going to find it.

Inconformity answered 19/10, 2017 at 16:0 Comment(0)
T
1

There are several reasons you could have problems loading from lib - see here for details - http://www.williambharding.com/blog/technology/rails-3-autoload-modules-and-classes-in-production/

  • fix autoload path
  • threadsafe related
  • naming relating
  • ...
Tammietammuz answered 20/4, 2013 at 10:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.