Ruby on Rails - 8 read
How To Upgrade to Rails 7
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.
How Railsfactory helps in upgrading ruby on rails 7
With a team of experienced developers skilled in RoR upgrades, Railsfactory ensures a smooth transition to Rails 7. We assess and optimize your codebase, enhance compatibility, and improve security and performance, providing seamless deployment and ongoing support for long-term efficiency.
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.