Ruby on Rails - 8 read

How To Upgrade to Rails 7

RF logo

RailsFactory

Marketing

It can be frustrating to work on a software project using an outdated framework. The new features and resources that come with the evolution of languages and frameworks cannot be utilized by developers working on an out-of-date project. Due to limitations in that outdated environment, project managers face deliveries that are less frequent and more complex.

Therefore, the users of your web application are the most important beneficiaries. They may experience some challenges such as system performance, security, and vulnerabilities that have the potential to result in data loss and other issues.

We believe that the update procedure should be a part of your software development routine based on best practices. Your other development activities ought to have a priority that is comparable to that of daily code updates. Minor code updates, deprecation warning fixes, and the application of security patches when they are available are all part of this routine. To put it another way, it would be fantastic if you could reduce the amount of technical issues caused by software updates that impact your day-to-day activities.

Please be aware that, as of the final release of Rails 7, Rails 6.1 will no longer receive bug fixes and will operate in the "security issues only" mode. Since it will no longer receive any updates, this will also mark the end of support for Rails 5.2.

In this blog, we will reflect on how to upgrade your Rails application to the most recent version. You will be able to identify your current version and the most recent stable version after completing the read. Together, these two pieces of information will help you plan the process of updating.

What is New in Rails 7

Webpack & Node Not Necessary

That's right! NodeJS or Webpack will no longer be required for JavaScript in Rails 7. NPM packages can still be used.

Babel's ES6 transpilation and Webpack's bundling both require extensive setup. This brought a lot of baggage, was difficult to understand, and was difficult to make any changes to, especially while maintaining upgradability, even though Rails supported it fairly well with the Webpacker gem.

Now, the importmaps-rails gem is used by default for new apps created with rails new to import maps. To pin, unpin, or update dependencies, you can use the ./bin/importmap CLI rather than writing a package.json and installing them with npm or yarn.

Take an example, to install date-fns:

$ ./bin/importmap pin date-fns

A line like this will be added to config/importmap.rb:

pin "date-fns", to: "https://ga.jspm.io/npm:date-fns@2.27.0/esm/index.js"

You can continue to use everything as before in your JavaScript code:

import { formatDistance, subDays } from "date-fns";

formatDistance(subDays(new Date(), 3), new Date(), { addSuffix: true }); //=> "3 days ago"

With this setup, one thing to keep in mind is that no transpiling occurs between what you write and what the browser sees. This is acceptable for the most part because all relevant browsers support ES6 by default.

However, this also means that TypeScript and JSX won't work for you because they need to be converted to JS before they can be used.

Therefore, if you want to use React with JSX, you must still use a different setup (webpack, rollup, or esbuild).

You can do this with Rails 7. With your chosen strategy, you only need to issue one command:

$ ./bin/rails javascript:install:[esbuild|rollup|webpack]

Check for a Presence/Absence of an Association

Now, rather than joining and looking for an ID, we can use where.associated(:association) to see if an association is on a record.

# Before:

account.users.joins(:contact).where.not(contact_id: nil)

# After:

account.users.where.associated(:contact)

Ruby debug

The debug gem has replaced byebug as the new default for debugging.

To enter a debugging session, you must now call debugger in the code rather than byebug.

Use Single Record with sole

When you want to assert that the query should only match a single record, you can now use sole or find_sole_by instead of first or find_by when querying records.

Named Variants Rather than specifying size for each access, you can now name variants in ActiveStorage.

class User < ApplicationRecord
has_one_attached :avatar do |attachable|
attachable.variant :thumb, resize: "100x100"
end
end

Use #Call avatar.variant(:thumb) for getting the thumb variant of an avatar:

<%= image_tag user.avatar.variant(:thumb) %>

Retry Jobs Unlimited Times

ActiveJob now allows passing :unlimited as the retry_on attempts parameter. There is no limit to how many times Rails will attempt to complete the task.

class MyJob < ActiveJob::Base
retry_on(AlwaysRetryException, attempts: :unlimited)

def perform
raise "KABOOM"
end
end

Database Layer Encryption

Using the encrypts method on ActiveRecord::Base , it is possible to mark particular database fields as encrypted in Rails 7. This indicates that you can write code like this following initial setup:

class Message < ApplicationRecord
encrypts :text
end

The encrypted attributes can be used just like any other attribute. It will be encrypted and decrypted between the database and your application by Rails 7 on its own.

However, there is a slight flaw in this: You can't use that field to query the database unless you pass a deterministic: true alternative to the encrypts technique. Use the deterministic mode only for attributes you absolutely need to query because it is less secure than the default non-deterministic mode.

Converting Hash to HTML Attributes

A new tag.attributes method that translates a hash into HTML attributes can be used in views:

<input <%= tag.attributes(type: :text, aria: { label: "Search" }) %>> will produce

<input type="text" aria-label="Search" />

Let’s upgrade to Rails 7

If you haven't yet done so, our team has provided a step-by-step guide on how to move your current application to to Rails 7:

Ruby Version

Rails 7.0 requires Ruby 2.7 or later. Your application minimum needs to be running using Ruby 2.7 in order to upgrade it to the latest version.

Check your dependencies

First, try performing a “dummy” upgrade.

gem 'rails', '~> 7.0'

Then go for checking your dependencies:

bundle update

Update rails in gemfile and upgrade rails packages

In the gemfile you can enter like the following:

#old
gem “rails”, “~> 6.1.4”
#new
gem “rails”, “~> 7.0.1”

You need to update bundler - in the terminal run:

bundle update

Try upgrading all of rails packages one after another so the run commands just like below in package.json:

yarn upgrade @rails/actioncable –latest

yarn upgrade @rails/activestorage –latest

yarn upgrade @rails/request.js –latest

Gems

To ensure that the gems you use for the project are compatible with Rails 7.0, be sure to check their GitHub page. If you are a gem's maintainer, you need to make sure that it supports Rails 7.0 and update it if it does not.

Sprockets to gemfile

While running rails commands, few assets related errors can come up because of utilizing asset pipeline. For instance:

# - don’t know how to build task ‘assets:precompile'

# - ‘method_missing’: undefined method ‘assets’

Thus, in this case you need to add sprockets to the gemfile:

gem “sprockets-rails”

And run in the terminal:

bundle install

Zeitwerk

You will need to switch to zeitwerk mode if your application continues to operate in classic mode. Additionally, there is no configuration point for setting the autoloading mode because config.autoloader= has been removed. If it were set to :zeitwerk , simply get rid of it.

Run updating rails task and check frameworks defaults

Run this in the terminal:

Rails/bin app: update

This command automatically incorporates the new Rails 7 into every project file. Once you enter the rails/bin app, Rails will develop a new_framework_dafaults_7.0.rb file in config/initializers. By enabling the new default settings one at a time across multiple deployments, this file makes a significant upgrade much simpler. Because the application.rb file already has the setting load_defaults set, you can now use the default configuration settings from Rails 6 while Rails 7 is running. This tells you exactly which Rails version settings to load.

# initialize configuration defaults for originally generated Rails config.load_defaults 6.1

Once the new_framework_defaults_7.0.rb is removed, flip the load_defaults version in application.rb to 7.0.1

# initialize configuration defaults for originally generated Rails config.load_defaults 7.0

Spring

If your application makes use of Spring, it needs to be updated to at least version 3.0.0; otherwise, you will receive an error indicating that the undefined method'mechanism=' for ActiveSupport::Dependencies: Module.

Application Code

If you haven't been paying attention to deprecation warnings in previous version jumps and haven't updated them, you might have problems with broken tests or parts of the application. You can look through this list to see if this may be the case if you are having trouble determining the reason why something is broken.

Autoloading during initialization is deprecated

It’s an easy one.

In config/initializers/mode.rb, for an initializer like this:

Example.configuration.mode = ENV['MODE'

Don’t use classes/modules defined in your autoloaded paths like app/services. Rather, to_prepare block is the new way to do it:

Rails.application.config.to_prepare do
Example.configuration.mode = ENV['MODE']
end

Rails introduced the new configuration option config.autoload_once_ paths in case you're looking for alternatives. You will be able to use initializer code from an earlier autoloader with this. For paths like app/services that you will be handling with main autoloader, this is not particularly useful.

The addition of a path to either of them creates a chaos and may lead to an error like the following:

lib/zeitwerk/loader.rb:475:in block (3 levels) in raise_if_conflicting_directory': loader (Zeitwerk::Error) #<Zeitwerk::Loader:0x00007fa8ddccdd18 @tag="rails.main">wants to manage directory/Users/fayris/rails-7-example/app/events, which is already managed by #<Zeitwerk::Loader:0x00007fa8ddccca58 @tag="rails.once">`

There are numerous advantages for using the upgraded version of Rails. It is worthwhile. Your development team will make use of all the new tools and resources that the most recent versions provide. In addition, working in a modern environment makes it simple to hire new developers. Because the project will have fewer technical flaws, the development managers can anticipate an increase in delivery speeds.

Last but not least, keep in mind that your customers will benefit from a more secure, efficient, and effective application. To permit these excellent reasons, the upgrade procedure appears to be an excellent investment.

Click here to schedule a call for outsourcing your Rails app upgrade so you can focus on your customers or if you need help in updating your app running on older Rails versions.

Written by RailsFactory

Largest pure play ruby on rails development company delivering best-in-class web & mobile application development services.

Other blogs

You may also like


Your one-stop shop for expert RoR services

oin 250+ companies achieving top-notch RoR development without increasing your workforce.