Rails 5: Load lib files in production

92,180

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:

  1. Place lib dir into app 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.
  2. Remove any require statements pointing to your own classes inside lib because they all are autoloaded anyway if their file/dir naming are correct, and if you leave require statements it can break autoreloading. More info here
  3. Set config.eager_load = true in all environments to see code loading problems eagerly in dev.
  4. Use Rails.application.eager_load! before playing with threads to avoid "circular dependency" errors.
  5. 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')
...
Share:
92,180
Tobias
Author by

Tobias

Updated on December 12, 2021

Comments

  • Tobias
    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 to true 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
    Ernest over 7 years
    If 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
    akostadinov over 7 years
    I used config.eager_load_paths << "#{Rails.root}/lib", that's better IMO to follow recommended rails app structure.
  • eXa
    eXa about 7 years
    Putting lib in app/lib is recommended by rails members github.com/rails/rails/issues/13142#issuecomment-275549669
  • Martin Svoboda
    Martin Svoboda almost 7 years
    So how does one uses the lib folder now? I mean moving lib dir into app dir seems kind of like a workaround.
  • Tim Kretschmer
    Tim Kretschmer almost 7 years
    /app/lib/placed a file/class and it's NOT autoloading. tested in rails 5.1, new project
  • jacklin
    jacklin over 6 years
    It'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
    Josh Brody over 6 years
    This 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
    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 example validators.
  • Josh Brody
    Josh Brody over 6 years
    I 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 me lib
  • 3limin4t0r
    3limin4t0r over 6 years
    Works like a charm. I didn't like the syntax so changed it to config.eager_load_paths << Rails.root.join('lib').
  • Tim Diggins
    Tim Diggins over 6 years
    I 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
    Stuart.Sklinar about 6 years
    Could you expand on why this fixes the issue?
  • srghma
    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
    Stuart.Sklinar about 6 years
    Could 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
    srghma about 6 years
    @Stuart.Sklinar sure
  • Steven Aguilar
    Steven Aguilar almost 6 years
    Where would the following line go Rails.application.eager_load!
  • geoboy
    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
    Samir Haddad over 5 years
    To 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
    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
    CWitty almost 5 years
    This 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 the app directory. Several of the other answers are better than this one.
  • Damien Roche
    Damien Roche over 4 years
    Good suggestion for config.eager_load = true, but bad suggestion on moving lib into app.
  • Damien Roche
    Damien Roche over 4 years
    Yep, this works! Seems Rails devs really enjoy causing lib loading issues :D until next time!
  • William Wong Garay
    William Wong Garay about 4 years
    To Rails 5.2 uses config.eager_load_paths += [Rails.root.join('lib')] instead because config.eager_load_paths is a frozen array
  • Michał Zalewski
    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
    Jason almost 4 years
    This discussion is the best, though a lengthy read, source of information on the subject that I've come across.
  • Lev Lukomsky
    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 in lib and all other code in app like controllers/mailers/jobs/interactors/policies/serializers/de‌​corators/helpers/...
  • sparkle
    sparkle over 2 years
    If 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
    Amos Joshua over 2 years
    This 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('produ‌​ct_filters.rb'))