NO LEA ESTE ARCHIVO EN GITHUB, LAS GUÍAS ESTÁN PUBLICADAS EN https://guides.rubyonrails.org.

Múltiples Bases de Datos con Active Record

Esta guía cubre el uso de múltiples bases de datos con tu aplicación Rails.

Después de leer esta guía sabrás:


A medida que una aplicación crece en popularidad y uso, necesitarás escalar la aplicación para apoyar a tus nuevos usuarios y sus datos. Una forma en la que tu aplicación puede necesitar escalar es a nivel de base de datos. Rails admite el uso de múltiples bases de datos, por lo que no tienes que almacenar todos tus datos en un solo lugar.

En este momento se admiten las siguientes características:

  • Múltiples bases de datos de escritura y una réplica para cada una
  • Cambio automático de conexión para el modelo con el que estás trabajando
  • Cambio automático entre la base de datos de escritura y la réplica dependiendo del verbo HTTP y escrituras recientes
  • Tareas de Rails para crear, eliminar, migrar e interactuar con las múltiples bases de datos

Las siguientes características no están (todavía) soportadas:

  • Balanceo de carga de réplicas

1 Configurando tu Aplicación

Aunque Rails intenta hacer la mayor parte del trabajo por ti, todavía hay algunos pasos que deberás seguir para preparar tu aplicación para múltiples bases de datos.

Supongamos que tenemos una aplicación con una sola base de datos de escritura, y necesitamos agregar una nueva base de datos para algunas tablas nuevas que estamos agregando. El nombre de la nueva base de datos será "animals".

config/database.yml se ve así:

production:
  database: my_primary_database
  adapter: mysql2
  username: root
  password: <%= ENV['ROOT_PASSWORD'] %>

Agreguemos una segunda base de datos llamada "animals" y réplicas para ambas bases de datos también. Para hacer esto, necesitamos cambiar nuestro config/database.yml de una configuración de 2 niveles a una de 3 niveles.

Si se proporciona una clave de configuración primary, se usará como la configuración "predeterminada". Si no hay una configuración llamada primary, Rails usará la primera configuración como predeterminada para cada entorno. Las configuraciones predeterminadas usarán los nombres de archivo predeterminados de Rails. Por ejemplo, las configuraciones primarias usarán db/schema.rb para el archivo de esquema, mientras que todas las otras entradas usarán db/[CONFIGURATION_NAMESPACE]_schema.rb para el nombre del archivo.

production:
  primary:
    database: my_primary_database
    username: root
    password: <%= ENV['ROOT_PASSWORD'] %>
    adapter: mysql2
  primary_replica:
    database: my_primary_database
    username: root_readonly
    password: <%= ENV['ROOT_READONLY_PASSWORD'] %>
    adapter: mysql2
    replica: true
  animals:
    database: my_animals_database
    username: animals_root
    password: <%= ENV['ANIMALS_ROOT_PASSWORD'] %>
    adapter: mysql2
    migrations_paths: db/animals_migrate
  animals_replica:
    database: my_animals_database
    username: animals_readonly
    password: <%= ENV['ANIMALS_READONLY_PASSWORD'] %>
    adapter: mysql2
    replica: true

Cuando se usan múltiples bases de datos, hay algunas configuraciones importantes.

Primero, el nombre de la base de datos para primary y primary_replica debe ser el mismo porque contienen los mismos datos. Esto también es válido para animals y animals_replica.

Segundo, el nombre de usuario para los escritores y las réplicas debe ser diferente, y los permisos de la base de datos del usuario de la réplica deben estar configurados para solo lectura y no escritura.

Cuando se usa una base de datos réplica, necesitas agregar una entrada replica: true a la réplica en config/database.yml. Esto se debe a que Rails de otro modo no tiene forma de saber cuál es una réplica y cuál es el escritor. Rails no ejecutará ciertas tareas, como migraciones, contra réplicas.

Finalmente, para nuevas bases de datos de escritura, necesitas establecer la clave migrations_paths en el directorio donde almacenarás las migraciones para esa base de datos. Veremos más sobre migrations_paths más adelante en esta guía.

También puedes configurar el archivo de volcado de esquema configurando schema_dump a un nombre de archivo de esquema personalizado o omitiendo completamente el volcado de esquema configurando schema_dump: false.

Ahora que tenemos una nueva base de datos, configuremos el modelo de conexión.

La réplica de la base de datos primaria puede configurarse en ApplicationRecord de esta manera:

class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true

  connects_to database: { writing: :primary, reading: :primary_replica }
end

Si usas una clase con un nombre diferente para tu registro de aplicación, necesitas establecer primary_abstract_class en su lugar, para que Rails sepa con qué clase ActiveRecord::Base debe compartir una conexión.

class PrimaryApplicationRecord < ActiveRecord::Base
  primary_abstract_class

  connects_to database: { writing: :primary, reading: :primary_replica }
end

En ese caso, las clases que se conectan a primary/primary_replica pueden heredar de tu clase abstracta primaria como lo hacen las aplicaciones estándar de Rails con ApplicationRecord:

class Person < PrimaryApplicationRecord
end

Por otro lado, necesitamos configurar nuestros modelos persistidos en la base de datos "animals":

class AnimalsRecord < ApplicationRecord
  self.abstract_class = true

  connects_to database: { writing: :animals, reading: :animals_replica }
end

Esos modelos deben heredar de esa clase abstracta común:

class Car < AnimalsRecord
  # Habla automáticamente con la base de datos animals.
end

Por defecto, Rails espera que los roles de la base de datos sean writing y reading para la primaria y la réplica respectivamente. Si tienes un sistema heredado, es posible que ya tengas roles configurados que no quieras cambiar. En ese caso, puedes establecer un nuevo nombre de rol en la configuración de tu aplicación.

config.active_record.writing_role = :default
config.active_record.reading_role = :readonly

Es importante conectarse a tu base de datos en un solo modelo y luego heredar de ese modelo para las tablas en lugar de conectar múltiples modelos individuales a la misma base de datos. Los clientes de bases de datos tienen un límite en el número de conexiones abiertas que puede haber, y si haces esto, multiplicará el número de conexiones que tienes ya que Rails usa el nombre de la clase del modelo para el nombre de especificación de la conexión.

Ahora que tenemos el config/database.yml y el nuevo modelo configurado, es hora de crear las bases de datos. Rails viene con todos los comandos que necesitas para usar múltiples bases de datos.

Puedes ejecutar bin/rails --help para ver todos los comandos que puedes ejecutar. Deberías ver lo siguiente:

$ bin/rails --help
...
db:create                          # Crea la base de datos desde DATABASE_URL o config/database.yml para el ...
db:create:animals                  # Crea la base de datos animals para el entorno actual
db:create:primary                  # Crea la base de datos primary para el entorno actual
db:drop                            # Elimina la base de datos desde DATABASE_URL o config/database.yml para el cu...
db:drop:animals                    # Elimina la base de datos animals para el entorno actual
db:drop:primary                    # Elimina la base de datos primary para el entorno actual
db:migrate                         # Migra la base de datos (opciones: VERSION=x, VERBOSE=false, SCOPE=blog)
db:migrate:animals                 # Migra la base de datos animals para el entorno actual
db:migrate:primary                 # Migra la base de datos primary para el entorno actual
db:migrate:status                  # Muestra el estado de las migraciones
db:migrate:status:animals          # Muestra el estado de las migraciones para la base de datos animals
db:migrate:status:primary          # Muestra el estado de las migraciones para la base de datos primary
db:reset                           # Elimina y recrea todas las bases de datos desde su esquema para el entorno actual y carga las semillas
db:reset:animals                   # Elimina y recrea la base de datos animals desde su esquema para el entorno actual y carga las semillas
db:reset:primary                   # Elimina y recrea la base de datos primary desde su esquema para el entorno actual y carga las semillas
db:rollback                        # Retrocede el esquema a la versión anterior (especifica pasos con STEP=n)
db:rollback:animals                # Retrocede la base de datos animals para el entorno actual (especifica pasos con STEP=n)
db:rollback:primary                # Retrocede la base de datos primary para el entorno actual (especifica pasos con STEP=n)
db:schema:dump                     # Crea un archivo de esquema de base de datos (ya sea db/schema.rb o db/structure.sql  ...
db:schema:dump:animals             # Crea un archivo de esquema de base de datos (ya sea db/schema.rb o db/structure.sql  ...
db:schema:dump:primary             # Crea un archivo db/schema.rb que es portátil contra cualquier BD admitida  ...
db:schema:load                     # Carga un archivo de esquema de base de datos (ya sea db/schema.rb o db/structure.sql  ...
db:schema:load:animals             # Carga un archivo de esquema de base de datos (ya sea db/schema.rb o db/structure.sql  ...
db:schema:load:primary             # Carga un archivo de esquema de base de datos (ya sea db/schema.rb o db/structure.sql  ...
db:setup                           # Crea todas las bases de datos, carga todos los esquemas e inicializa con los datos de semillas (usa db:reset para también eliminar todas las bases de datos primero)
db:setup:animals                   # Crea la base de datos animals, carga el esquema e inicializa con los datos de semillas (usa db:reset:animals para también eliminar la base de datos primero)
db:setup:primary                   # Crea la base de datos primary, carga el esquema e inicializa con los datos de semillas (usa db:reset:primary para también eliminar la base de datos primero)
...

Ejecutar un comando como bin/rails db:create creará tanto las bases de datos primary como animals. Ten en cuenta que no hay un comando para crear los usuarios de la base de datos, y deberás hacer eso manualmente para apoyar a los usuarios de solo lectura para tus réplicas. Si deseas crear solo la base de datos animals, puedes ejecutar bin/rails db:create:animals.

2 Conectarse a Bases de Datos sin Gestionar Esquemas y Migraciones

Si deseas conectarte a una base de datos externa sin ninguna tarea de gestión de bases de datos, como gestión de esquemas, migraciones, semillas, etc., puedes configurar la opción de configuración por base de datos database_tasks: false. Por defecto está configurado en true.

production:
  primary:
    database: my_database
    adapter: mysql2
  animals:
    database: my_animals_database
    adapter: mysql2
    database_tasks: false

3 Generadores y Migraciones

Las migraciones para múltiples bases de datos deben vivir en sus propias carpetas con el prefijo del nombre de la clave de la base de datos en la configuración.

También necesitas establecer migrations_paths en las configuraciones de la base de datos para decirle a Rails dónde encontrar las migraciones.

Por ejemplo, la base de datos animals buscaría migraciones en el directorio db/animals_migrate y primary buscaría en db/migrate. Los generadores de Rails ahora toman una opción --database para que el archivo se genere en el directorio correcto. El comando se puede ejecutar de la siguiente manera:

$ bin/rails generate migration CreateDogs name:string --database animals

Si estás usando generadores de Rails, los generadores de scaffold y modelo crearán la clase abstracta por ti. Simplemente pasa la clave de la base de datos a la línea de comando.

$ bin/rails generate scaffold Dog name:string --database animals

Se creará una clase con el nombre camelizado de la base de datos y Record. En este ejemplo, la base de datos es "animals", por lo que terminamos con AnimalsRecord:

class AnimalsRecord < ApplicationRecord
  self.abstract_class = true

  connects_to database: { writing: :animals }
end

El modelo generado heredará automáticamente de AnimalsRecord.

class Dog < AnimalsRecord
end

NOTA: Dado que Rails no sabe qué base de datos es la réplica de tu escritor, necesitarás agregar esto a la clase abstracta después de que hayas terminado.

Rails solo generará AnimalsRecord una vez. No será sobrescrito por nuevos scaffolds ni eliminado si el scaffold es eliminado.

Si ya tienes una clase abstracta y su nombre difiere de AnimalsRecord, puedes pasar la opción --parent para indicar que deseas una clase abstracta diferente:

$ bin/rails generate scaffold Dog name:string --database animals --parent Animals::Record

Esto omitirá la generación de AnimalsRecord ya que has indicado a Rails que deseas usar una clase padre diferente.

4 Activando el Cambio Automático de Roles

Finalmente, para usar la réplica de solo lectura en tu aplicación, necesitarás activar el middleware para el cambio automático.

El cambio automático permite que la aplicación cambie del escritor a la réplica o de la réplica al escritor según el verbo HTTP y si hubo una escritura reciente por parte del usuario que realiza la solicitud.

Si la aplicación recibe una solicitud POST, PUT, DELETE o PATCH, la aplicación escribirá automáticamente en la base de datos del escritor. Si la solicitud no es uno de esos métodos, pero la aplicación realizó recientemente una escritura, también se usará la base de datos del escritor. Todas las demás solicitudes usarán la base de datos de la réplica.

Para activar el middleware de cambio automático de conexión, puedes ejecutar el generador de intercambio automático:

$ bin/rails g active_record:multi_db

Y luego descomentar las siguientes líneas:

Rails.application.configure do
  config.active_record.database_selector = { delay: 2.seconds }
  config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
  config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
end

Rails garantiza "leer tu propia escritura" y enviará tu solicitud GET o HEAD al escritor si está dentro de la ventana de delay. Por defecto, el retraso se establece en 2 segundos. Deberías cambiar esto según tu infraestructura de base de datos. Rails no garantiza "leer una escritura reciente" para otros usuarios dentro de la ventana de retraso y enviará solicitudes GET y HEAD a las réplicas a menos que hayan escrito recientemente.

El cambio automático de conexión en Rails es relativamente primitivo y deliberadamente no hace mucho. El objetivo es un sistema que demuestre cómo hacer el cambio automático de conexión que sea lo suficientemente flexible como para ser personalizable por los desarrolladores de aplicaciones.

La configuración en Rails te permite cambiar fácilmente cómo se realiza el cambio y en qué parámetros se basa. Supongamos que deseas usar una cookie en lugar de una sesión para decidir cuándo cambiar las conexiones. Puedes escribir tu propia clase:

class MyCookieResolver < ActiveRecord::Middleware::DatabaseSelector::Resolver
  def self.call(request)
    new(request.cookies)
  end

  def initialize(cookies)
    @cookies = cookies
  end

  attr_reader :cookies

  def last_write_timestamp
    self.class.convert_timestamp_to_time(cookies[:last_write])
  end

  def update_last_write_timestamp
    cookies[:last_write] = self.class.convert_time_to_timestamp(Time.now)
  end

  def save(response)
  end
end

Y luego pasarla al middleware:

config.active_record.database_selector = { delay: 2.seconds }
config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
config.active_record.database_resolver_context = MyCookieResolver

5 Usando el Cambio Manual de Conexión

Hay algunos casos en los que es posible que desees que tu aplicación se conecte a un escritor o a una réplica y el cambio automático de conexión no sea adecuado. Por ejemplo, puedes saber que para una solicitud en particular siempre deseas enviar la solicitud a una réplica, incluso cuando estás en una ruta de solicitud POST.

Para hacer esto, Rails proporciona un método connected_to que cambiará a la conexión que necesitas.

ActiveRecord::Base.connected_to(role: :reading) do
  # Todo el código en este bloque estará conectado al rol de lectura.
end

El "rol" en la llamada connected_to busca las conexiones que están conectadas en ese controlador de conexión (o rol). El controlador de conexión reading mantendrá todas las conexiones que se conectaron a través de connects_to con el nombre de rol de reading.

Ten en cuenta que connected_to con un rol buscará una conexión existente y cambiará usando el nombre de especificación de conexión. Esto significa que si pasas un rol desconocido como connected_to(role: :nonexistent), obtendrás un error que dice ActiveRecord::ConnectionNotEstablished (No connection pool for 'ActiveRecord::Base' found for the 'nonexistent' role.)

Si deseas que Rails garantice que cualquier consulta realizada sea de solo lectura, pasa prevent_writes: true. Esto solo previene que las consultas que parecen escrituras se envíen a la base de datos. También debes configurar tu base de datos réplica para que funcione en modo de solo lectura.

ActiveRecord::Base.connected_to(role: :reading, prevent_writes: true) do
  # Rails verificará cada consulta para asegurarse de que sea una consulta de lectura.
end

6 Fragmentación Horizontal

La fragmentación horizontal es cuando divides tu base de datos para reducir el número de filas en cada servidor de base de datos, pero mantienes el mismo esquema en "fragmentos". Esto se llama comúnmente fragmentación "multi-tenant".

La API para soportar la fragmentación horizontal en Rails es similar a la API de múltiples bases de datos / fragmentación vertical que ha existido desde Rails 6.0.

Los fragmentos se declaran en la configuración de tres niveles de esta manera:

production:
  primary:
    database: my_primary_database
    adapter: mysql2
  primary_replica:
    database: my_primary_database
    adapter: mysql2
    replica: true
  primary_shard_one:
    database: my_primary_shard_one
    adapter: mysql2
    migrations_paths: db/migrate_shards
  primary_shard_one_replica:
    database: my_primary_shard_one
    adapter: mysql2
    replica: true
  primary_shard_two:
    database: my_primary_shard_two
    adapter: mysql2
    migrations_paths: db/migrate_shards
  primary_shard_two_replica:
    database: my_primary_shard_two
    adapter: mysql2
    replica: true

Los modelos se conectan con la API connects_to a través de la clave shards:

class ApplicationRecord < ActiveRecord::Base
  primary_abstract_class

  connects_to database: { writing: :primary, reading: :primary_replica }
end

class ShardRecord < ApplicationRecord
  self.abstract_class = true

  connects_to shards: {
    shard_one: { writing: :primary_shard_one, reading: :primary_shard_one_replica },
    shard_two: { writing: :primary_shard_two, reading: :primary_shard_two_replica }
  }
end

Si estás usando fragmentos, asegúrate de que tanto migrations_paths como schema_dump permanezcan sin cambios para todos los fragmentos. Al generar una migración, puedes pasar la opción --database y usar uno de los nombres de fragmento. Dado que todos establecen la misma ruta, no importa cuál elijas.

$ bin/rails g scaffold Dog name:string --database primary_shard_one

Luego, los modelos pueden cambiar de fragmento manualmente a través de la API connected_to. Si usas fragmentación, se deben pasar tanto un role como un shard:

ActiveRecord::Base.connected_to(role: :writing, shard: :default) do
  @id = Person.create! # Crea un registro en el fragmento llamado ":default"
end

ActiveRecord::Base.connected_to(role: :writing, shard: :shard_one) do
  Person.find(@id) # No puede encontrar el registro, no existe porque fue creado
                   # en el fragmento llamado ":default".
end

La API de fragmentación horizontal también soporta réplicas de lectura. Puedes cambiar el rol y el fragmento con la API connected_to.

ActiveRecord::Base.connected_to(role: :reading, shard: :shard_one) do
  Person.first # Busca el registro desde la réplica de lectura del fragmento uno.
end

7 Activando el Cambio Automático de Fragmentos

Las aplicaciones pueden cambiar automáticamente de fragmento por solicitud usando el middleware proporcionado.

El middleware ShardSelector proporciona un marco para cambiar automáticamente de fragmento. Rails proporciona un marco básico para determinar a qué fragmento cambiar y permite que las aplicaciones escriban estrategias personalizadas para cambiar si es necesario.

ShardSelector toma un conjunto de opciones (actualmente solo se admite lock) que pueden ser usadas por el middleware para alterar el comportamiento. lock es true por defecto y prohibirá que la solicitud cambie de fragmento una vez dentro del bloque. Si lock es false, se permitirá el cambio de fragmento. Para la fragmentación basada en inquilinos, lock siempre debe ser true para evitar que el código de la aplicación cambie erróneamente entre inquilinos.

El mismo generador que el selector de bases de datos puede usarse para generar el archivo para el cambio automático de fragmento:

$ bin/rails g active_record:multi_db

Luego, en el config/initializers/multi_db.rb generado, descomenta lo siguiente:

Rails.application.configure do
  config.active_record.shard_selector = { lock: true }
  config.active_record.shard_resolver = ->(request) { Tenant.find_by!(host: request.host).shard }
end

Las aplicaciones deben proporcionar el código para el resolver ya que depende de modelos específicos de la aplicación. Un ejemplo de resolver se vería así:

config.active_record.shard_resolver = ->(request) {
  subdomain = request.subdomain
  tenant = Tenant.find_by_subdomain!(subdomain)
  tenant.shard
}

8 Cambio Granular de Conexión a la Base de Datos

A partir de Rails 6.1, es posible cambiar conexiones para una base de datos en lugar de todas las bases de datos globalmente.

Con el cambio granular de conexión a la base de datos, cualquier clase de conexión abstracta podrá cambiar conexiones sin afectar a otras conexiones. Esto es útil para cambiar tus consultas de AnimalsRecord para leer desde la réplica mientras aseguras que tus consultas de ApplicationRecord vayan a la primaria.

AnimalsRecord.connected_to(role: :reading) do
  Dog.first # Lee desde animals_replica.
  Person.first  # Lee desde primary.
end

También es posible cambiar conexiones granularmente para fragmentos.

AnimalsRecord.connected_to(role: :reading, shard: :shard_one) do
  # Leerá desde shard_one_replica. Si no existe una conexión para shard_one_replica,
  # se levantará un error de ConnectionNotEstablished.
  Dog.first

  # Leerá desde el escritor primary.
  Person.first
end

Para cambiar solo el clúster de base de datos primario usa ApplicationRecord:

ApplicationRecord.connected_to(role: :reading, shard: :shard_one) do
  Person.first # Lee desde primary_shard_one_replica.
  Dog.first # Lee desde animals_primary.
end

ActiveRecord::Base.connected_to mantiene la capacidad de cambiar conexiones globalmente.

8.1 Manejo de Asociaciones con Uniones entre Bases de Datos

A partir de Rails 7.0+, Active Record tiene una opción para manejar asociaciones que realizarían una unión entre múltiples bases de datos. Si tienes una asociación has many through o has one through que deseas deshabilitar la unión y realizar 2 o más consultas, pasa la opción disable_joins: true.

Por ejemplo:

class Dog < AnimalsRecord
  has_many :treats, through: :humans, disable_joins: true
  has_many :humans

  has_one :home
  has_one :yard, through: :home, disable_joins: true
end

class Home
  belongs_to :dog
  has_one :yard
end

class Yard
  belongs_to :home
end

Anteriormente, llamar a @dog.treats sin disable_joins o @dog.yard sin disable_joins levantaría un error porque las bases de datos no pueden manejar uniones entre clústeres. Con la opción disable_joins, Rails generará múltiples consultas select para evitar intentar unir entre clústeres. Para la asociación anterior, @dog.treats generaría el siguiente SQL:

SELECT "humans"."id" FROM "humans" WHERE "humans"."dog_id" = ?  [["dog_id", 1]]
SELECT "treats".* FROM "treats" WHERE "treats"."human_id" IN (?, ?, ?)  [["human_id", 1], ["human_id", 2], ["human_id", 3]]

Mientras que @dog.yard generaría el siguiente SQL:

SELECT "home"."id" FROM "homes" WHERE "homes"."dog_id" = ? [["dog_id", 1]]
SELECT "yards".* FROM "yards" WHERE "yards"."home_id" = ? [["home_id", 1]]

Hay algunas cosas importantes a tener en cuenta con esta opción:

  1. Puede haber implicaciones de rendimiento ya que ahora se realizarán dos o más consultas (dependiendo de la asociación) en lugar de una unión. Si el select para humans devolviera un alto número de IDs, el select para treats podría enviar demasiados IDs.
  2. Dado que ya no estamos realizando uniones, una consulta con un orden o límite ahora se ordena en memoria ya que el orden de una tabla no puede aplicarse a otra tabla.
  3. Esta configuración debe agregarse a todas las asociaciones donde desees que se deshabiliten las uniones. Rails no puede adivinar esto por ti porque la carga de asociaciones es perezosa, para cargar treats en @dog.treats, Rails ya necesita saber qué SQL debe generarse.

8.2 Caché de Esquema

Si deseas cargar una caché de esquema para cada base de datos, debes establecer schema_cache_path en cada configuración de base de datos y establecer config.active_record.lazily_load_schema_cache = true en tu configuración de aplicación. Ten en cuenta que esto cargará la caché de manera perezosa cuando se establezcan las conexiones de la base de datos.

9 Advertencias

9.1 Balanceo de Carga de Réplicas

Rails no soporta el balanceo de carga automático de réplicas. Esto es muy dependiente de tu infraestructura. Podríamos implementar un balanceo de carga básico y primitivo en el futuro, pero para una aplicación a escala, esto debería ser algo que tu aplicación maneje fuera de Rails.


Comentarios

Se te anima a ayudar a mejorar la calidad de esta guía.

Por favor contribuye si ves algún error tipográfico o errores fácticos. Para comenzar, puedes leer nuestra sección de contribuciones a la documentación.

También puedes encontrar contenido incompleto o cosas que no están actualizadas. Por favor agrega cualquier documentación faltante para main. Asegúrate de revisar Guías Edge primero para verificar si los problemas ya están resueltos o no en la rama principal. Revisa las Guías de Ruby on Rails para estilo y convenciones.

Si por alguna razón detectas algo que corregir pero no puedes hacerlo tú mismo, por favor abre un issue.

Y por último, pero no menos importante, cualquier tipo de discusión sobre la documentación de Ruby on Rails es muy bienvenida en el Foro oficial de Ruby on Rails.