Want to deploy rails application using kamal? I post tips and help topics on kamal.wiki.
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,.rvmrcorGemfile. -
Install
bundlergem 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
*_filtercallbacks in favor of*_actionbefore_filter skip_before_filter # to before_action skip_before_actionredirect_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_tois compulsory now.Use
optional: trueif the association is ever going to benilclass 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 = falseafter_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]}" endclass 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 fixgrep -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
rubocopto auto fix the formatRef: https://thoughtbot.com/blog/deprecating-static-attributes-in-factory_bot-4-11
rubocop -a spec/factories/xyz.rb --only FactoryBot/AttributeDefinedStaticallyMake sure to comment the following in
rubocop.ymltemporarilyAllCops: 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