Rails 5: Load lib files in production
Solution 1
Autoloading is disabled in the production environment because of thread safety. Thank you to @Зелёный for the link.
I solved this problem by storing the lib files in a lib
folder in my app
directory as recommended on Github. Every folder in the app
folder gets loaded by Rails automatically.
Solution 2
My list of changes after moving to Rails 5:
- Place
lib
dir intoapp
because all code inside app is autoloaded in dev and eager loaded in prod and most importantly is autoreloaded in development so you don't have to restart server each time you make changes. - Remove any
require
statements pointing to your own classes insidelib
because they all are autoloaded anyway if their file/dir naming are correct, and if you leaverequire
statements it can break autoreloading. More info here - Set
config.eager_load = true
in all environments to see code loading problems eagerly in dev. - Use
Rails.application.eager_load!
before playing with threads to avoid "circular dependency" errors. -
If you have any ruby/rails extensions then leave that code inside old
lib
directory and load them manually from initializer. This will ensure that extensions are loaded before your further logic that can depend on it:# config/initializers/extensions.rb Dir["#{Rails.root}/lib/ruby_ext/*.rb"].each { |file| require file } Dir["#{Rails.root}/lib/rails_ext/*.rb"].each { |file| require file }
Solution 3
I just used config.eager_load_paths
instead of config.autoload_paths
like mention akostadinov on github comment:
https://github.com/rails/rails/issues/13142#issuecomment-275492070
# config/application.rb
...
# config.autoload_paths << Rails.root.join('lib')
config.eager_load_paths << Rails.root.join('lib')
It works on development and production environment.
Thanks Johan for suggestion to replace #{Rails.root}/lib
with Rails.root.join('lib')
!
Solution 4
There must be a reason that Autoloading is disabled in production by default.
Here is a long discussion about this issue. https://github.com/rails/rails/issues/13142
Solution 5
This allows to have lib autoreload, and works in production environment too.
P.S. I have changed my answer, now it adds to both eager- an autoload paths, regardless of environment, to allow work in custom environments too (like stage)
# config/initializers/load_lib.rb
...
config.eager_load_paths << Rails.root.join('lib')
config.autoload_paths << Rails.root.join('lib')
...
Tobias
Updated on December 12, 2021Comments
-
Tobias over 2 years
I've upgraded one of my apps from Rails 4.2.6 to Rails 5.0.0. The Upgrade Guide says, that the Autoload feature is now disabled in production by default.
Now I always get an error on my production server since I load all lib files with autoload in the
application.rb
file.module MyApp class Application < Rails::Application config.autoload_paths += %W( lib/ ) end end
For now, I've set the
config.enable_dependency_loading
totrue
but I wonder if there is a better solution to this. There must be a reason that Autoloading is disabled in production by default. -
Ernest over 7 yearsIf you don't want to dig through long discussion thread on Github, you can find distilled explanation here: collectiveidea.com/blog/archives/2016/07/22/…
-
akostadinov over 7 yearsI used
config.eager_load_paths << "#{Rails.root}/lib"
, that's better IMO to follow recommended rails app structure. -
eXa about 7 yearsPutting lib in
app/lib
is recommended by rails members github.com/rails/rails/issues/13142#issuecomment-275549669 -
Martin Svoboda almost 7 yearsSo how does one uses the
lib
folder now? I mean movinglib
dir intoapp
dir seems kind of like a workaround. -
Tim Kretschmer almost 7 years
/app/lib/
placed a file/class and it's NOT autoloading. tested in rails 5.1, new project -
jacklin over 6 yearsIt's worth noting that you need to stop spring. I moved everything to app/lib/ and then wasted a little time wondering why I still couldn't use my classes from the console. spring stop ftw :)
-
Josh Brody over 6 yearsThis completely ruins what the purpose of
lib
is. I'd wait for tenderlove or DHH to chime in. In the meantime, I'd (personally) recommend sticking with @Lev Lukomsky's answer. -
Tobias over 6 years@JoshBrody My opinion now is that you shouldn't need the
/lib
directory at all. Third party libs are most of the time gems and if not there should be a gem created. For other files, I create specific folders in the/app
directory. For examplevalidators
. -
Josh Brody over 6 yearsI agree! I think that all business logic should belong in
/app
; I guess what I'm getting at is if you're writing a Rescue extension or have some non-domain-specific objects that extend ActiveModel, where should that belong? My first instinct tells melib
-
3limin4t0r over 6 yearsWorks like a charm. I didn't like the syntax so changed it to
config.eager_load_paths << Rails.root.join('lib')
. -
Tim Diggins over 6 yearsI think (after getting it wrong on many projects) that
/lib
is meant for functionality that could be separate gems (don't require anything in app or the environment or database) but aren't (yet) -- a kind of staging point. However I find there is always app-related utility functionality that doesn't quite fit in any of the existing/app/*
structures -- that goes well in/app/lib
and then these are good candidates for refactoring (abstracting into app, or pulling apart into something else) -
Stuart.Sklinar about 6 yearsCould you expand on why this fixes the issue?
-
srghma about 6 years@Stuart.Sklinar this allows to have lib autoreload, and works in production environment too. P.S. I have changed my answer, now it adds to both eager- an autoload paths, regardless of environment, to allow work in custom environments too (like stage)
-
Stuart.Sklinar about 6 yearsCould you expand (In your answer)? Code only answer's don't really help anyone understand why it should be done "that way" - I should add I'm not a Ruby dev, just helping clear up SO. Adding some commentary to a "code only answer" would give it some actual context.
-
srghma about 6 years@Stuart.Sklinar sure
-
Steven Aguilar almost 6 yearsWhere would the following line go
Rails.application.eager_load!
-
geoboy almost 6 years@Lev: When I do that, I see the following error in
dev
:NameError - uninitialized constant ApplicationController::JsonWebToken:
Do you know what might be going on? -
Samir Haddad over 5 yearsTo me that was the best answer. My project began on Rails 5.2 from scratch and the folder /lib was still created outside the /app folder. I didn't see a good reason to move it.
-
Hiro over 5 years@geoboy that's when the file is not loaded. it looks like the file is searched first in the root context as in
::ModuleOrClass
and then the current context, say,ApplicationController::ModuleOrClass
where it's called. Your error is showing the second result. -
CWitty almost 5 yearsThis may work but it isn't the best solution. The folder structure is semantic as well. Things in
lib
have a different perceived closeness to the project than things in theapp
directory. Several of the other answers are better than this one. -
Damien Roche over 4 yearsGood suggestion for config.eager_load = true, but bad suggestion on moving lib into app.
-
Damien Roche over 4 yearsYep, this works! Seems Rails devs really enjoy causing lib loading issues :D until next time!
-
William Wong Garay about 4 yearsTo Rails 5.2 uses
config.eager_load_paths += [Rails.root.join('lib')]
instead becauseconfig.eager_load_paths
is a frozen array -
Michał Zalewski about 4 years@WilliamWongGaray config.eager_load_paths is read-only when you try to modify it in initializer. When you add paths in
application.rb
it will work using both methods. -
Jason almost 4 yearsThis discussion is the best, though a lengthy read, source of information on the subject that I've come across.
-
Lev Lukomsky almost 3 years@CWitty
lib
is semantic for gems, but for Rails app it's rather unexpected to to have some ruby code inlib
and all other code inapp
likecontrollers
/mailers
/jobs
/interactors
/policies
/serializers
/decorators
/helpers
/... -
sparkle over 2 yearsIf lib is supposed for code agnostic to the app, where is it supposed to place additional classes related to project but not to a model? (e.g Algorithms tied to the app) ?
-
Amos Joshua over 2 yearsThis worked for me as well, whereas just modifying
eager_load_paths
did not. It does seem to me however that the require_dependency could benefit from using the join methods as well, i.e.require_dependency(Rails.root.join('lib').join('spree').join('core').join('product_filters.rb'))