Update Rails 6.1 a Rails 7
Tags: rails, ruby, tutorial, programming
Published: 2023-01-31 23:18:15

Rails Upgrade

Actualización de Rails 6.1.7 a Rails 7.0 en una aplicación tipo API.

Antes de la actualización las especificaciones son:

- Tengo un Mac M1 MacOs ventura
- Rails 6.1.7
- Ruby 2.7.3
- Cliente para el mac PostgreSQL 13.0  
- Redis server 6.2.4

## Paso a paso de la actualización:

- Hay una recomendación respecto a no actualizar de una versión muy antigua a una muy reciente, por ejemplo, no es recomendable actualizar de Rails 5 a Rails 7, en este caso no aplica, ya que estamos pasando de 6.1 a 7 lo cual está bien.
- Copiar y pegar nuestro Gemfile.lock en [RailsBumb](https://railsbump.org/) para poder revisar cuáles de nuestras gemas nos van a generar errores o problemas.
- Usar la gema “next_rails” para revisar cuáles gemas tenemos que actualizar, cuando ya la tengamos instalada solo haremos un `bundle_report compatibility --rails-version=version_deseada`, después de esto ya podemos actualizar las gemas que nos aparezcan en el reporte.
- Actualización de gemas:
  - Si el reporte nos pide actualizar gemas que hacen parte de Rails como `ActionMailer` o `ActionText` estas se van a actualizar con el cambio de versión a Rails 7. Si las gemas son instaladas por nosotros, obviamente debemos hacer la actualización.
  - No es aconsejable tener una versión fija de una gema en nuestro `Gemfile` así que es un buen momento para ir eliminando eso.
- Luego de actualizar las gemas ahora podemos en nuestro `Gemfile` actualizar la versión de Rails y correr `bundle update`.
- Cuando se haga la actualización lo primero es cambiar el `load_defaults` a Rails 7 en nuestro archivo `config/application.rb`.
- En mi caso uso `Spring` esta gema está en su versión 2.1.1 por alguna razón `next-rails` no la tuvo en cuenta, pero debe ser actualizada a una versión `3X`.
  - Cuando intenté actualizar la gema me decía que no podía porque estaba bloqueada, para esto lo que hice fue eliminar mi Gemfile.lock.
  - Luego en `config/environments/test.rb` lo dejé configurado en `false`.
- Configurar `Zeitwerk`.
  - `Zeitwerk` es la única forma en Rails 7 para cargar de forma segura archivos y evitar el uso excesivo de `require`.
  - Como ya se hizo el cambio a Rails 7 en la aplicación en `config/application.rb` se debe eliminar la línea `config.autoloader`.
  - Verificar si ya está corriendo todo bien con `bin/rails runner 'p Rails.autoloaders.zeitwerk_enabled?'` debe retornar `true` o en su defecto los respectivos warnings.
- Encriptación
  - En este punto, al iniciar el server estaba obteniendo un error `ActiveSupport::MessageEncryptor::InvalidMessage` esto es por la encriptación.
  - En la API se encripta los atributos siguiendo este [tutorial](https://pawelurbanek.com/rails-secure-encrypt-decrypt), pero al actualizar Rails la encriptación cambia, porque Rails 7 usa SHA256 mientras que en las versiones anteriores se usa SHA1, pero este protocolo está obsoleto, para solucionar esto se debe especificar que siga encriptando con SHA1 mientras que luego se hace la el cambio para poder usar los atributos encriptados de Rails 7, para solucionarlo me funcionó agregar el `hash_digest_class` así:
```Ruby
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
```

- Al correr los test encontré el error `RuntimeError: Foreign key violations found in your fixture data`, esto se debe a que en versiones anteriores se podían tener `fixtures` con una `missing association`, esto ya no será posible en Rails 7.
  - Una opción es en `application.rb` agregar la línea `config.active_record.verify_foreign_keys_for_fixtures = false`.
  - La otra opción es arreglar los errores en los fixtures(Lo cual es lo recomendado), para detectar qué tablas tenemos mal, primero reseteamos y cargamos la base datos de test para luego entrar en la consola así:
```
RAILS_ENV=test bin/rails db:reset
RAILS_ENV=test bin/rails db:fixtures:load
RAILS_ENV=test bin/rails c 
```
  - Luego corremos esta consulta para identificar si hay tablas que les falta una asociación para poder corregir los `fixtures` y ya con esto el error se solucionará:
```
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
```

- El siguiente error a corregir fue `ActionDispatch::Request::Session::DisabledSessionError: Your application has sessions disabled. To write to the session you must first configure a session store`:
  - En `config/application.rb` tenía la línea `config.middleware.use ActionDispatch::Cookies` para solucionar me funcionó esta respuesta de este [hilo](https://github.com/waiting-for-dev/devise-jwt/issues/235)

![Github Response](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yt996664q7veabj50fv1.png)
  

Este fue todo el proceso en mi caso, las guías y documentos de apoyo para esta actualización fueron:

- [Next Rails](https://github.com/fastruby/next_rails) 
- [Documentación FastRuby.io](https://www.fastruby.io/blog/rails/upgrades/upgrade-rails-6-1-to-7-0.html)
- [Guías de Rails](https://edgeguides.rubyonrails.org/upgrading_ruby_on_rails.html#upgrading-from-rails-6-1-to-rails-7-0)
- [Principales cambios de Rails 7](https://edgeguides.rubyonrails.org/7_0_release_notes.html) 
- [Cambio a Zeitwerk](https://guides.rubyonrails.org/classic_to_zeitwerk_howto.html)
- [Más de Zeitwerk ](https://medium.com/cedarcode/understanding-zeitwerk-in-rails-6-f168a9f09a1f)