Rails Upgrade
Upgrade from Rails 6.1.7 to Rails 7.0 in an API type application.
Before the upgrade the specs are:
- I have a Mac M1 MacOs ventura
- Rails 6.1.7
- Ruby 2.7.3
- PostgreSQL 13.0 client for the mac
- Redis server 6.2.4
Step by step upgrade:
- There is a recommendation regarding not upgrading from a very old version to a very recent one, for example, it is not recommended to upgrade from Rails 5 to Rails 7, in this case it does not apply, as we are moving from 6.1 to 7 which is fine.
- Copy and paste our Gemfile.lock into RailsBumb so we can check which of our gems are going to generate errors or problems.
- Use the gem "next_rails" to check which gems we need to update, when we have it installed we will just do a
bundle_report compatibility --rails-version=desired_version
, after this we can update the gems that appear in the report. - Updating gems:
- If the report asks us to update gems that are part of Rails such as
ActionMailer
orActionText
these will be updated with the version change to Rails 7. If the gems are installed by us, obviously we must do the update. - It is not advisable to have a fixed version of a gem in our
Gemfile
so it is a good time to remove that.
- If the report asks us to update gems that are part of Rails such as
- After updating the gems we can now in our
Gemfile
update the Rails version and runbundle update
. - When the update is done the first thing to do is to change the
load_defaults
to Rails 7 in ourconfig/application.rb
file. - In my case I use
Spring
this gem is on its 2.1.1 version for some reasonnext-rails
didn't take it into account, but it should be updated to a3X
version.- When I tried to update the gem it told me that I could not because it was locked, for this what I did was to delete my Gemfile.lock.
- Then in
config/environments/test.rb
I left it set tofalse
.
- Configure
Zeitwerk
.- Zeitwerk is the only way in Rails 7 to safely load files and avoid overuse of
require
. - As you have already made the switch to Rails 7 in the application in
config/application.rb
you should remove theconfig.autoloader
line. - Check if everything is already running fine with
bin/rails runner 'p Rails.autoloaders.zeitwerk_enabled?'
should returntrue
or the respective warnings.
- Zeitwerk is the only way in Rails 7 to safely load files and avoid overuse of
- Encryption
- At this point, when starting the server I was getting an
ActiveSupport::MessageEncryptor::InvalidMessage
error because of the encryption. - In the API the attributes are encrypted following this tutorial, but when updating Rails the encryption changes, because Rails 7 uses SHA256 while in the previous versions SHA1 is used, but this protocol is obsolete, to solve this you must specify to continue encrypting with SHA1 while then the change is made to use the encrypted attributes of Rails 7, to solve this it worked for me to add the
hash_digest_class
like this:
- At this point, when starting the server I was getting an
class EncryptionService
KEY = ActiveSupport::KeyGenerator.new(
Rails.application.credentials[:secret_key_base],
hash_digest_class: OpenSSL::Digest::SHA1
).generate_key(
Rails.application.credentials[:ENCRYPTION_SERVICE_SALT],
ActiveSupport::MessageEncryptor.key_len
).freeze
- When running the tests I found the error
RuntimeError: Foreign key violations found in your fixture data
, this is because in previous versions you could havefixtures
with amissing association
, this will no longer be possible in Rails 7.- One option is in
application.rb
add the lineconfig.active_record.verify_foreign_keys_for_fixtures = false
. - The other option is to fix the errors in the fixtures (which is recommended), to detect which tables we have wrong, first we reset and load the test database and then enter in the console like this:
- One option is in
RAILS_ENV=test bin/rails db:reset
RAILS_ENV=test bin/rails db:fixtures:load
RAILS_ENV=test bin/rails c
- Then we run this query to identify if there are tables that are missing an association so we can correct the
fixtures
and the error will be fixed:
ActiveRecord::Base.connection.execute(<<~SQL)
do $$$
declare r record;
BEGIN
FOR r IN (
SELECT FORMAT(
'UPDATE pg_constraint SET convalidated=false WHERE conname = ''%I''; ALTER TABLE %I VALIDATE CONSTRAINT %I;',
constraint_name,
table_name,
constraint_name
) AS constraint_check
FROM information_schema.table_constraints WHERE constraint_type = 'FOREIGN KEY'
)
LOOP
EXECUTE (r.constraint_check);
END LOOP;
END;
$$;
SQL
- The following error to be corrected was
ActionDispatch::Request::Session::DisabledSessionError: Your application has sessions disabled. To write to the session you must first configure a session store
- In
config/application.rb
, I had the lineconfig.middleware.use ActionDispatch::Cookies
to solve this solution from this thread of Github worked for me.
- In
This was the whole process in my case. The support guides and documentation for this update were:
Top comments (0)