Solutions to Potential Upgrade Problems in Rails 5

Don't be surprised by some of the newest changes in Rails' latest upgrade

Work Equipment Snow Preparations by MTA Photos is licensed under CC BY 2.0

Rails 5 is out! Rails 5 contains hundreds of changes, including new major features like ActionCable, but for the most part, is a drop-in upgrade for existing Rails 4 applications.

The Rails team has provided a fairly comprehensive upgrade guide, but there are some potential upgrade problems that aren’t fully covered.

Autoload vs. Eager Load in production

With Rails 5, autoloading is now completely disabled if config.eager_load = true. What this means is that if file paths outside of app/ are in your config.autoload_paths and not in config.eager_load_paths (for example, lib/), these files will no longer load in deployed environments.

This bit me on a recent Rails 5 upgrade because as config.eager_load = false is set in the development and test environments, the test suite was green and the app worked great in development mode. It was only after I deployed the application that problems cropped up, and the code in lib/ was no longer available to the application.

To test if this is the same with your app, temporarily set config.eager_load = true in config/application.rb and run the application and its test suite. If you get uninitialized constant errors there are three possible solutions.

Add to eager_load_paths

Adding the non-app directories to config.eager_load_paths will fix this problem, but this comes with an important caveat: every Ruby file in these directories will get loaded when the application boots. In my application, lib was also doubling as a vendor location for a number of external libraries and tools, which led to the application trying to load Ruby files in these tools, which in turn resulted in strange errors regarding missing requires and dependencies.

Re-enable Autoload

Alternatively, you can re-enable the autoload functionality for all environments:

config.enable_dependency_loading = true

This will take the application back to the Rails 4 way of “eager_load with autoload fallback”, but do note that this option may eventually itself get deprecated and removed in later versions of Rails.

Move code into app/

A better solution is to move all code the application requires into the app/ directory. Every directory directly under app/ is automatically eager and auto-loaded by Rails, reducing any external custom configuration you may need. I ended up moving the important files in lib to live under app/lib which solved all of my problems.

New Deprecation warnings

A new major version of Rails means a new set of deprecation warnings! Rails 5 is the first version of Rails to require Ruby 2.2.2 or later because of one main feature: keyword arguments. Rails APIs are slowly moving over to make heavy use of keyword arguments, and the first major spam of deprecation warnings related to this change can be found in controller tests.

Controller Tests

There are two important changes to Rails controller testing:

assert_template

If your test suite uses assert_template, or expect(response).to render_template(), this assertion has been moved out of Rails core and is now available via the gem rails-controller-testing. In case of rspec, you’ll need to upgrade rspec-rails to at least version 3.5.0.

get, post, put, delete

These testing methods will spam test output with deprecation warnings regarding the change to keyword arguments. Where these methods have long been used with multiple ordinal arguments (get(path, params, session, headers)), these params after path are now expected to be keyword arguments instead: get(path, params: {}, session: {}, headers: {}).

Depending on how many controller tests are in your test suite, this could be an annoying change, but as a huge fan of keyword arguments, and given how many times I’ve asked “How do I send custom headers into get?!”, this is a very welcome improvement that will make these tests easier to write and read.

Photo of Jason Roelofs

Jason is a senior developer who has worked in the front-end, back-end, and everything in between. He has a deep understanding of all things code and can craft solutions for any problem. Jason leads development of our hosted CMS, Harmony.

Comments

Add a Comment

Hmm...that didn't work.

Something went wrong while adding your comment. If you don't mind, please try submitting it again.

Comment Added!

Your comment has been added to this post. Please refresh this page to view it.

Optional. If added, we will display a link to the website in your comment.
Optional. Never shared or displayed in your comment.
  1. Mathias Jakobsen
    November 22, 2016 at 8:54 AM

    Thank you very much! That Eager Load change was about to make me go crazy.

  2. Peter Abramowitsch
    November 22, 2016 at 22:08 PM

    Looking for help on a rails 5.0 upgrade. My app uses Mongoid and SymmetricEncryption gems, among others. Working fine on rails 4.0 but under 5.0 letting these two gems float to the latest versions the app fails to load:

    Array values in the parameter to Gem.paths= are deprecated.
    Please use a String or nil.
    An Array ({“GEM_PATH”=>[“/Users/peterabramowitsch/.rvm/gems/ruby-2.3.2”, “/Users/peterabramowitsch/.rvm/gems/ruby-2.3.2@global”]}) was passed in from bin/rails:3:in load' /Users/peterabramowitsch/.rvm/gems/ruby-2.3.2/gems/bundler-1.13.6/lib/bundler/runtime.rb:94:in rescue in block (2 levels) in require’: There was an error while trying to load the gem ‘symmetric-encryption’. (Bundler::GemRequireError)
    Gem Load Error is: undefined method option' for Mongoid::Fields:Module Backtrace for gem load error is: /Users/peterabramowitsch/.rvm/gems/ruby-2.3.2/gems/symmetric-encryption-3.8.3/lib/symmetric_encryption/extensions/mongoid/encrypted.rb:90:in <top (required)>’
    /Users/peterabramowitsch/.rvm/gems/ruby-2.3.2/gems/activesupport-5.0.0.1/lib/active_support/dependencies.rb:293:in `require’
    etc etc…..

    Any ideas as to where to look?

  3. Seunghwan
    February 08, 2017 at 6:34 AM

    Hi Jason! Thank you for nice article. It’s really useful.

    But to be precise, the config.eager_load option does not disable autoloading. The “config.cache_classes” option disables autoloading.
    The config.eager_load option just eagerly loads directories in config.eager_load_paths. If config.cache_classes is false and config.eager_load is true, both eager loading and auto loading are enabled.
    The Rails Upgrade Guide document also says that it will be disabled in the “Production environment”. This is because the config.cache_classes option in the Production environment is true.

  4. TurtleKitty
    June 15, 2017 at 21:54 PM

    Thank you for solving my problem.

  5. Steve
    July 02, 2017 at 22:10 PM

    Thanks for this post. If I want to arrange files in the /app/lib within subdirectories, how to I get those files to autoload? Do I have to keep all files in app/lib?

  6. jason.roelofs@collectiveidea.com
    Jason Roelofs
    July 10, 2017 at 18:49 PM

    @Steve As long as you name the classes and modules underneath app/lib as you would other directories, Rails auto-loading will find your code just fine.

  7. kinnrot@gmail.com
    Chen Kinnrot
    September 03, 2017 at 9:37 AM

    Thank you for clearing this issue

  8. Juan
    September 21, 2017 at 16:13 PM

    Hey, thanks you so much for the post. It was very useful.