A step by step guide to upgrade rails 4 application to rails 5
Upgrading an application can be tedious if you don’t have good test coverage.
This article covers the common issues you might face while upgrading the from rails 4 to 5.
If you are upgrading to ruby 2.7 export RUBYOPT="-W0"
to avoid getting your screen filled up with deprecations warnings
export RUBYOPT="-W0"
bundle exec rspec
# OR use
RUBYOPT="-W0" bundle exec rspec
Step 1: Upgrading ruby and bundle install
-
Install the required ruby version
-
rvm
rvm install ruby 2.x.x
-
rbenv
rbenv install 2.x.x
-
-
Change the ruby version in
.ruby-version
,.rvmrc
orGemfile
. -
Install
bundler
gem install bundler
-
Change the rails version
# Gemfile gem 'rails', '4.2.0' # To gem 'rails', '5.2.4'
-
run bundle install
If you don’t need specific versions of the gems locked it is better to remove the specified version.
bundle install
-
Additional Note: (
ruby 2.7
)Ruby 2.7 has removed these gems which were default in prior versions.
Add the following gems to your gemfile
gem 'e2mmap' gem 'scanf' gem 'thwait'
Step 2: Config changes
At this point you should be able to install all the dependencies. But, in order to start the application we need to do some config changes.
-
Remove
# config/application.rb config.active_record.raise_in_transactional_callbacks = true
-
Change
# config/application.rb config.serve_static_assets = true # or config.serve_static_files = true # to config.public_file_server.enabled
# config/application.rb config.static_cache_control = 'public, max-age=3600' #to config.public_file_server.headers = { 'Cache-Control' => 'public, max-age=3600' }
# routes.rb ProjectName::Application.routes.draw do # to Rails.application.routes.draw do
-
Create
config/initializers/assets.rb
# config/initializers/assets.rb Rails.application.config.assets.version = '1.0' Rails.application.config.assets.precompile += [ 'application.css', 'application.js' ]
Step 3: Model & Controller changes
Controllers
-
Replace all
*_filter
callbacks in favor of*_action
before_filter skip_before_filter # to before_action skip_before_action
redirect_to(:back) # to redirect_back(fallback_location: root_path)
Models
-
Models now inherit from
ApplicationRecord
-
Create
# app/models/application_record.rb class ApplicationRecord < ActiveRecord::Base self.abstract_class = true end
-
Change the following in all models
class User < ActiveRecord::Base # to class User < ApplicationRecord
-
-
belongs_to
is compulsory now.Use
optional: true
if the association is ever going to benil
class Post < ActiveRecord::Base belongs_to :user, optional: true #....
In a huge application you might want to keep the old behavior. You can change the settings globally in application.rb
# config/application.rb config.active_record.belongs_to_required_by_default = false
after_destroy :send_confirmation, if: 'user&.notifications_enabled?' after_destroy :send_confirmation, if: Proc.new { user&.notifications_enabled? }
-
attribute_changed? is deprecated in after_save callback
Deprecated:
_changed? doesn't work as expected in after_save & after_update
Use saved_change_to_? # Old after_save :send_email, if: :email_changed? # New after_save :send_email, if: -> { saved_change_to_email? }
Deprecated:
_was doesn't work as expected in after_save & after_update
Use_before_last_save # Old archived_record.assign_attributes(email: email_was) # New archived_record.assign_attributes(email: email_before_last_save)
-
AASM if you’re upgrading aasm from 3 -> 4
Follow this guide - Upgrading AASM from version 3 to 4
Mailers
-
Mailers now inherit from ApplicationMailer
# app/mailers/application_mailer.rb class ApplicationMailer < ActionMailer::Base default from: "sample@#{ActionMailer::Base.smtp_settings[:domain]}" end
class UserMailer < ActionMailer::Base # to class UserMailer < ApplicationMailer
Step 4: Migration changes
Directly inheriting from ActiveRecord::Migration is not supported.
Please specify the Rails release the migration was written for:
-
Change the following
class CreateUsers < ActiveRecord::Migration # to class CreateUsers < ActiveRecord::Migration[4.2]
Easy fix
grep -rl "ActiveRecord::Migration$" db | xargs sed -i "s/ActiveRecord::Migration/ActiveRecord::Migration[4.2]/g"
Step 5: Necessary changes in RSpec
RSpec
-
Upgrading FactoryBot to 5
DEPRECATION WARNING: Static attributes will be removed in FactoryBot 5.0.
Please use dynamic attributes instead by wrapping the attribute value in a block:FactoryBot.define do factory :user do name 'John' # Bad & will throw error # To name { 'John' } # Good
Easy Fix:
use
rubocop
to auto fix the formatRef: https://thoughtbot.com/blog/deprecating-static-attributes-in-factory_bot-4-11
rubocop -a spec/factories/xyz.rb --only FactoryBot/AttributeDefinedStatically
Make sure to comment the following in
rubocop.yml
temporarilyAllCops: Exclude: - 'spec/**/*'
-
Fix controller specs
get :show, id: request.id xhr :get, :summary, id: request.id # to get :show, params: { id: request.id } get :summary, params: { id: request.id }, xhr: true
This gem does it for you - rails5-spec-converter
Additional Issues you might get while running RSpec
-
undefined method ‘sort’ for
#<ActionController::Parameters:>
Do not use Hash methods on params