In this article, I’ll try to cover all the issues that I had, when I was upgrading one of my Rails app from Ruby on Rails 4.2 to Ruby on Rails 5.0.

Note: This is not a complete, covering every possible case tutorial. You might encounter issues that I didn’t. If so, feel free to comment and I will try to update the post with other issues and solutions.

Preparations – What should we do before upgrading to Rails 5?

Upgrade your Ruby version at least to 2.2.2 (I would recommend 2.3, since it is going to be released soon)

Upgrade bundler

Upgrade your application to the most recent Rails 4.2 version

Check gems compatibility (you may want to be on edge with few gems (or use Rails 5 branches if available))

Write more tests if you don’t have a decent code coverage

The last point is the most important. If you don’t have a good code coverage level and you lack tests, upgrading from Rails 4.2 to Rails 5 might be a big problem.

Upgrading to Rails 5 – Gemfile

Currently there are some gems incompatibilities (I will cover them later) and with some, you need to go edge. Here’s a list of gems that I had to switch to edge (or use a specific version) in order to get things running:

Note that I’ve added a rails-controller-testing gem.This gem brings back assigns to your controller tests as well as assert_template to both controller and integration tests. If you use those methods, after upgrading to Ruby on Rails 5, you will have to add it.

After updating your Gemfile you can do a

bundle install

and hopefully you’re ready for the upgrade!

Configuration files – rake rails:update

There are some changes in configuration files (that I will cover), but I would strongly recommend running:

rake rails:update

allow it to overwrite your files and then just add the stuff that was gone (git diff). IMHO it is way less complex approach, that trying to add all new config options manually.

However not all the changes are worth being mentioned (sometimes there were just comments changes).

bin/rails and bin/rake

If you didn’t have any custom stuff there, just go with the flow ;)

config/application.rb and config/boot.rb

I’ll just quote the comment from application.rb (although it is not a new feature it is still worth reminding):

Settings in config/environments/* take precedence over those specified here. Application configuration should go into files in config/initializers all .rb files in that directory are automatically loaded.

And this is exactly what you should do. If you have any custom stuff in application.rb – just move it to initializers. And what about boot.rb? Leave it as it is.

config/environments/*.rb

development.rb – feature toggling and file watcher

There’s something quite interesting in new development.rb: feature toggling. Thanks to some files that are in tmp/ you can disable/enable some features to test them in the dev mode, without having to switch to production mode (for example caching).

Rails.root.join('tmp/caching-dev.txt').exist?

There is also (commented) file watcher added. You can use it to have an auto-reloading based on your file changes:

config/initializers/*.rb

application_controller_renderer.rb

New initializer configuration for application controller renderer. This is a really good feature that finally allows to drop some “bypass like” solutions. If you didn’t render outside of controllers scope, you can leave this commented.

cors.rb

Here are all the settings related to CORS. Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests. If you don’t know what that means, it means that you can remove this file.

config/redis/cable.yml

Redis settings for ActionCable. Nothing special except one thing: why do we have config/database.yml and config/redis/cable.yml instead of config/databases/redis.yml and config/databases/mysql.yml (or something similar)?

Starting your freshly updated Rails 5 application

As I said before, if you have a decent code coverage level and you know what you’re doing, upgrade should not be a big problem.

At this point, if you’re not using any fancy route settings or any other magic, you should be able to at least start your application:

Monkey patching gems

We’re just before Christmas, so I don’t expect gem maintainers to fix them. That’s why we will fix some of them on our own. Here’s a list of gems with some incompatibilities (even when using edge) that I’ve encountered (not including Rails gems):

and as you’ve probably already guested, #active_record4? returns false and it will try to use Rails 3 method call. To fix this, create an acts_as_taggable_on.rb file in config/initializers and place there following code:

module ActsAsTaggableOn::Utils
def self.active_record4?
true
end
end

after a restart, this problem should be solved.

Decent Exposure – methods that no longer exist

This should be considered as a temporary patch. It is not a solid solution (I just wanted to make it work – not to fix decent exposure).

decent_exposure/expose.rb:14:in `block in extended': undefined method `hide_action'
for ActionController::Base:Class (NoMethodError)
from /gems/decent_exposure-2.3.2/lib/decent_exposure/expose.rb:7:in `class_eval'
from /gems/decent_exposure-2.3.2/lib/decent_exposure/expose.rb:7:in `extended'
from /gems/decent_exposure-2.3.2/lib/decent_exposure.rb:5:in `extend'
from /gems/decent_exposure-2.3.2/lib/decent_exposure.rb:5:in `block in <top (required)>'
from /gems/activesupport-5.0.0.beta1/lib/active_support/lazy_load_hooks.rb:38:in `instance_eval'
from /gems/activesupport-5.0.0.beta1/lib/active_support/lazy_load_hooks.rb:38:in `execute_hook'
from /gems/activesupport-5.0.0.beta1/lib/active_support/lazy_load_hooks.rb:45:in `block in run_load_hooks'
from /gems/activesupport-5.0.0.beta1/lib/active_support/lazy_load_hooks.rb:44:in `each'
from /gems/activesupport-5.0.0.beta1/lib/active_support/lazy_load_hooks.rb:44:in `run_load_hooks'
from /gems/actionpack-5.0.0.beta1/lib/action_controller/base.rb:262:in `<class:Base>'
from /gems/actionpack-5.0.0.beta1/lib/action_controller/base.rb:164:in `<module:ActionController>'
from /gems/actionpack-5.0.0.beta1/lib/action_controller/base.rb:5:in `<top (required)>'
from /bundler/gems/ransack-2a3759317a44/lib/ransack.rb:38:in `<top (required)>'
from /gems/bundler-1.10.6/lib/bundler/runtime.rb:76:in `require'
from /gems/bundler-1.10.6/lib/bundler/runtime.rb:76:in `block (2 levels) in require'
from /gems/bundler-1.10.6/lib/bundler/runtime.rb:72:in `each'
from /gems/bundler-1.10.6/lib/bundler/runtime.rb:72:in `block in require'
from /gems/bundler-1.10.6/lib/bundler/runtime.rb:61:in `each'
from /gems/bundler-1.10.6/lib/bundler/runtime.rb:61:in `require'
from /gems/bundler-1.10.6/lib/bundler.rb:134:in `require'
from /home/mencio/Software/Senpuu/config/application.rb:7:in `<top (required)>

Decent Exposure gem uses some methods from ActionController::Base, that apparently have changed (they no longer exist). To fix that, we will just create dummy methods that will just play along with the Decent Exposure internal logic (config/initializers/decent_exposure.rb):

If it goes about DecentExposure, we need to make one more change. We need to add a require on top of the application.rb file:

# The first and last line are already there - put the decent_exposure one in the middle
require File.expand_path('../boot', __FILE__)
require File.expand_path('../initializers/decent_exposure', __FILE__)
require 'rails/all'

We had to patch this gem before it is loaded, because on load it already performs some logic that requires non-existing methods.

Code base changes

If you’re not planning to use any new Rails features and just (for now) stay with what you had, you won’t have many things to change.

ApplicationRecord instead of ActiveRecord::Base

It is like an ApplicationController. Just an extra inheritance layer just in case you want to have some global (for all models) elements. Just add application_model.rb and change inheritance in all the models:

I wouldn’t be surprised at all (I know this error) but for some reason it was working with 4.2. So if you want to migrate and deal with it later, add this to your dependent models (those for which this error happens):

before_save do
instance_variable_set(:@readonly, false)
true
end
before_destroy do
instance_variable_set(:@readonly, false)
true
end

ActiveRecord::Base default_scope lambda change

Note: I think this also applies to standard scopes.

Not sure if it was a bug or a feature – but it is gone. You can no longer return a nil from a lambda default_scope:

Conclusion

As I’ve stated before: If you have a decent code coverage level and you know what you’re doing, upgrade should not be a big problem. However, I still recommend waiting at least until all the most popular gems are up2date.