1 Introducción
Esta guía documenta la autocarga, recarga y carga anticipada en aplicaciones Rails.
En un programa Ruby ordinario, cargas explícitamente los archivos que definen las clases y módulos que deseas usar. Por ejemplo, el siguiente controlador se refiere a ApplicationController
y Post
, y normalmente emitirías llamadas require
para ellos:
# NO HAGAS ESTO.
require "application_controller"
require "post"
# NO HAGAS ESTO.
class PostsController < ApplicationController
def index
@posts = Post.all
end
end
Este no es el caso en las aplicaciones Rails, donde las clases y módulos de la aplicación están disponibles en todas partes sin llamadas require
:
class PostsController < ApplicationController
def index
@posts = Post.all
end
end
Rails los autocarga en tu nombre si es necesario. Esto es posible gracias a un par de cargadores Zeitwerk que Rails configura en tu nombre, los cuales proporcionan autocarga, recarga y carga anticipada.
Por otro lado, esos cargadores no gestionan nada más. En particular, no gestionan la biblioteca estándar de Ruby, las dependencias de gemas, los componentes de Rails, ni siquiera (por defecto) el directorio lib
de la aplicación. Ese código debe cargarse como de costumbre.
2 Estructura del Proyecto
En una aplicación Rails, los nombres de archivo deben coincidir con las constantes que definen, con los directorios actuando como espacios de nombres.
Por ejemplo, el archivo app/helpers/users_helper.rb
debería definir UsersHelper
y el archivo app/controllers/admin/payments_controller.rb
debería definir Admin::PaymentsController
.
Por defecto, Rails configura Zeitwerk para inflexionar los nombres de archivo con String#camelize
. Por ejemplo, espera que app/controllers/users_controller.rb
defina la constante UsersController
porque eso es lo que "users_controller".camelize
devuelve.
La sección Personalización de Inflexiones a continuación documenta formas de anular este comportamiento predeterminado.
Por favor, consulta la documentación de Zeitwerk para más detalles.
3 config.autoload_paths
Nos referimos a la lista de directorios de la aplicación cuyo contenido debe ser autocargado y (opcionalmente) recargado como rutas de autocarga. Por ejemplo, app/models
. Dichos directorios representan el espacio de nombres raíz: Object
.
Las rutas de autocarga se llaman directorios raíz en la documentación de Zeitwerk, pero usaremos "ruta de autocarga" en esta guía.
Dentro de una ruta de autocarga, los nombres de archivo deben coincidir con las constantes que definen como se documenta aquí.
Por defecto, las rutas de autocarga de una aplicación consisten en todos los subdirectorios de app
que existen cuando la aplicación se inicia ---excepto assets
, javascript
y views
--- además de las rutas de autocarga de los motores de los que pueda depender.
Por ejemplo, si UsersHelper
se implementa en app/helpers/users_helper.rb
, el módulo es autocargable, no necesitas (y no deberías escribir) una llamada require
para él:
$ bin/rails runner 'p UsersHelper'
UsersHelper
Rails agrega directorios personalizados bajo app
a las rutas de autocarga automáticamente. Por ejemplo, si tu aplicación tiene app/presenters
, no necesitas configurar nada para autocargar presentadores; funciona automáticamente.
El array de rutas de autocarga predeterminadas puede extenderse agregando a config.autoload_paths
, en config/application.rb
o config/environments/*.rb
. Por ejemplo:
module MyApplication
class Application < Rails::Application
config.autoload_paths << "#{root}/extras"
end
end
Además, los motores pueden agregar en el cuerpo de la clase del motor y en sus propios config/environments/*.rb
.
ADVERTENCIA. Por favor, no muten ActiveSupport::Dependencies.autoload_paths
; la interfaz pública para cambiar las rutas de autocarga es config.autoload_paths
.
ADVERTENCIA: No puedes autocargar código en las rutas de autocarga mientras la aplicación se inicia. En particular, directamente en config/initializers/*.rb
. Por favor, consulta Autoloading when the application boots más abajo para formas válidas de hacerlo.
Las rutas de autocarga son gestionadas por el autocargador Rails.autoloaders.main
.
4 config.autoload_lib(ignore:)
Por defecto, el directorio lib
no pertenece a las rutas de autocarga de aplicaciones o motores.
El método de configuración config.autoload_lib
agrega el directorio lib
a config.autoload_paths
y config.eager_load_paths
. Debe invocarse desde config/application.rb
o config/environments/*.rb
, y no está disponible para motores.
Normalmente, lib
tiene subdirectorios que no deberían ser gestionados por los autocargadores. Por favor, pasa su nombre relativo a lib
en el argumento requerido ignore
. Por ejemplo:
config.autoload_lib(ignore: %w(assets tasks))
¿Por qué? Mientras que assets
y tasks
comparten el directorio lib
con código Ruby regular, su contenido no está destinado a ser recargado o cargado anticipadamente.
La lista ignore
debe tener todos los subdirectorios de lib
que no contienen archivos con extensión .rb
, o que no deberían ser recargados o cargados anticipadamente. Por ejemplo,
config.autoload_lib(ignore: %w(assets tasks templates generators middleware))
config.autoload_lib
no está disponible antes de la versión 7.1, pero aún puedes emularlo siempre que la aplicación use Zeitwerk:
# config/application.rb
module MyApp
class Application < Rails::Application
lib = root.join("lib")
config.autoload_paths << lib
config.eager_load_paths << lib
Rails.autoloaders.main.ignore(
lib.join("assets"),
lib.join("tasks"),
lib.join("generators")
)
# ...
end
end
5 config.autoload_once_paths
Puede que desees poder autocargar clases y módulos sin recargarlos. La configuración autoload_once_paths
almacena código que puede ser autocargado, pero no será recargado.
Por defecto, esta colección está vacía, pero puedes extenderla agregando a config.autoload_once_paths
. Puedes hacerlo en config/application.rb
o config/environments/*.rb
. Por ejemplo:
module MyApplication
class Application < Rails::Application
config.autoload_once_paths << "#{root}/app/serializers"
end
end
Además, los motores pueden agregar en el cuerpo de la clase del motor y en sus propios config/environments/*.rb
.
Si app/serializers
se agrega a config.autoload_once_paths
, Rails ya no considera esto como una ruta de autocarga, a pesar de ser un directorio personalizado bajo app
. Esta configuración anula esa regla.
Esto es clave para clases y módulos que están en caché en lugares que sobreviven a las recargas, como el propio marco de Rails.
Por ejemplo, los serializadores de Active Job se almacenan dentro de Active Job:
# config/initializers/custom_serializers.rb
Rails.application.config.active_job.custom_serializers << MoneySerializer
y Active Job en sí no se recarga cuando hay una recarga, solo el código de la aplicación y motores en las rutas de autocarga lo es.
Hacer que MoneySerializer
sea recargable sería confuso, porque recargar una versión editada no tendría efecto en ese objeto de clase almacenado en Active Job. De hecho, si MoneySerializer
fuera recargable, a partir de Rails 7 dicho inicializador lanzaría un NameError
.
Otro caso de uso es cuando los motores decoran clases del marco:
initializer "decorate ActionController::Base" do
ActiveSupport.on_load(:action_controller_base) do
include MyDecoration
end
end
Allí, el objeto del módulo almacenado en MyDecoration
en el momento en que se ejecuta el inicializador se convierte en un ancestro de ActionController::Base
, y recargar MyDecoration
es inútil, no afectará esa cadena de ancestros.
Las clases y módulos de las rutas de autocarga única pueden ser autocargados en config/initializers
. Así que, con esa configuración esto funciona:
# config/initializers/custom_serializers.rb
Rails.application.config.active_job.custom_serializers << MoneySerializer
Técnicamente, puedes autocargar clases y módulos gestionados por el autocargador once
en cualquier inicializador que se ejecute después de :bootstrap_hook
.
Las rutas de autocarga única son gestionadas por Rails.autoloaders.once
.
6 config.autoload_lib_once(ignore:)
El método config.autoload_lib_once
es similar a config.autoload_lib
, excepto que agrega lib
a config.autoload_once_paths
en su lugar. Debe invocarse desde config/application.rb
o config/environments/*.rb
, y no está disponible para motores.
Al llamar a config.autoload_lib_once
, las clases y módulos en lib
pueden ser autocargados, incluso desde inicializadores de la aplicación, pero no serán recargados.
config.autoload_lib_once
no está disponible antes de la versión 7.1, pero aún puedes emularlo siempre que la aplicación use Zeitwerk:
# config/application.rb
module MyApp
class Application < Rails::Application
lib = root.join("lib")
config.autoload_once_paths << lib
config.eager_load_paths << lib
Rails.autoloaders.once.ignore(
lib.join("assets"),
lib.join("tasks"),
lib.join("generators")
)
# ...
end
end
7 Recarga
Rails recarga automáticamente las clases y módulos si cambian los archivos de la aplicación en las rutas de autocarga.
Más precisamente, si el servidor web está ejecutándose y los archivos de la aplicación han sido modificados, Rails descarga todas las constantes autocargadas gestionadas por el autocargador main
justo antes de que se procese la siguiente solicitud. De esa manera, las clases o módulos de la aplicación utilizados durante esa solicitud serán autocargados nuevamente, recogiendo así su implementación actual en el sistema de archivos.
La recarga puede habilitarse o deshabilitarse. La configuración que controla este comportamiento es config.enable_reloading
, que es true
por defecto en modo development
, y false
por defecto en modo production
. Por compatibilidad con versiones anteriores, Rails también admite config.cache_classes
, que es equivalente a !config.enable_reloading
.
Rails utiliza un monitor de archivos basado en eventos para detectar cambios en los archivos por defecto. Puede configurarse en su lugar para detectar cambios de archivos recorriendo las rutas de autocarga. Esto se controla mediante la configuración config.file_watcher
.
En una consola de Rails no hay un monitor de archivos activo independientemente del valor de config.enable_reloading
. Esto se debe a que, normalmente, sería confuso tener código recargado en medio de una sesión de consola. Similar a una solicitud individual, generalmente deseas que una sesión de consola sea atendida por un conjunto consistente y no cambiante de clases y módulos de la aplicación.
Sin embargo, puedes forzar una recarga en la consola ejecutando reload!
:
irb(main):001:0> User.object_id
=> 70136277390120
irb(main):002:0> reload!
Reloading...
=> true
irb(main):003:0> User.object_id
=> 70136284426020
Como puedes ver, el objeto de clase almacenado en la constante User
es diferente después de recargar.
7.1 Recarga y Objetos Obsoletos
Es muy importante entender que Ruby no tiene una forma de recargar verdaderamente clases y módulos en memoria, y que eso se refleje en todas partes donde ya se usan. Técnicamente, "descargar" la clase User
significa eliminar la constante User
mediante Object.send(:remove_const, "User")
.
Por ejemplo, revisa esta sesión de consola de Rails:
irb> joe = User.new
irb> reload!
irb> alice = User.new
irb> joe.class == alice.class
=> false
joe
es una instancia de la clase User
original. Cuando hay una recarga, la constante User
entonces evalúa a una clase recargada diferente. alice
es una instancia de la recién cargada User
, pero joe
no lo es — su clase está obsoleta. Puedes definir joe
nuevamente, iniciar una subsesión de IRB, o simplemente lanzar una nueva consola en lugar de llamar a reload!
.
Otra situación en la que puedes encontrar este inconveniente es al subclasificar clases recargables en un lugar que no se recarga:
# lib/vip_user.rb
class VipUser < User
end
si User
se recarga, dado que VipUser
no lo es, la superclase de VipUser
es el objeto de clase original obsoleto.
Conclusión: no caches clases o módulos recargables.
8 Autocarga Cuando la Aplicación se Inicia
Mientras se inicia, las aplicaciones pueden autocargar desde las rutas de autocarga única, que son gestionadas por el autocargador once
. Por favor, consulta la sección config.autoload_once_paths
arriba.
Sin embargo, no puedes autocargar desde las rutas de autocarga, que son gestionadas por el autocargador main
. Esto se aplica al código en config/initializers
así como a los inicializadores de la aplicación o motores.
¿Por qué? Los inicializadores solo se ejecutan una vez, cuando la aplicación se inicia. No se ejecutan nuevamente en las recargas. Si un inicializador usara una clase o módulo recargable, las ediciones de ellos no se reflejarían en ese código inicial, volviéndose obsoletas. Por lo tanto, referirse a constantes recargables durante la inicialización no está permitido.
Veamos qué hacer en su lugar.
8.1 Caso de Uso 1: Durante el Inicio, Cargar Código Recargable
8.1.1 Autocargar al Inicio y en Cada Recarga
Imaginemos que ApiGateway
es una clase recargable y necesitas configurar su endpoint mientras la aplicación se inicia:
# config/initializers/api_gateway_setup.rb
ApiGateway.endpoint = "https://example.com" # NameError
Los inicializadores no pueden referirse a constantes recargables, necesitas envolver eso en un bloque to_prepare
, que se ejecuta al inicio y después de cada recarga:
# config/initializers/api_gateway_setup.rb
Rails.application.config.to_prepare do
ApiGateway.endpoint = "https://example.com" # CORRECTO
end
NOTA: Por razones históricas, este callback puede ejecutarse dos veces. El código que ejecuta debe ser idempotente.
8.1.2 Autocargar Solo al Inicio
Las clases y módulos recargables también pueden ser autocargados en bloques after_initialize
. Estos se ejecutan al inicio, pero no se ejecutan nuevamente en la recarga. En algunos casos excepcionales esto puede ser lo que deseas.
Las verificaciones previas son un caso de uso para esto:
# config/initializers/check_admin_presence.rb
Rails.application.config.after_initialize do
unless Role.where(name: "admin").exists?
abort "El rol de administrador no está presente, por favor siembra la base de datos."
end
end
8.2 Caso de Uso 2: Durante el Inicio, Cargar Código que Permanece en Caché
Algunas configuraciones toman un objeto de clase o módulo, y lo almacenan en un lugar que no se recarga. Es importante que estos no sean recargables, porque las ediciones no se reflejarían en esos objetos obsoletos almacenados en caché.
Un ejemplo es el middleware:
config.middleware.use MyApp::Middleware::Foo
Cuando recargas, la pila de middleware no se ve afectada, por lo que sería confuso que MyApp::Middleware::Foo
sea recargable. Los cambios en su implementación no tendrían efecto.
Otro ejemplo son los serializadores de Active Job:
# config/initializers/custom_serializers.rb
Rails.application.config.active_job.custom_serializers << MoneySerializer
Cualquiera que sea el objeto que MoneySerializer
evalúe durante la inicialización se agrega a los serializadores personalizados, y ese objeto permanece allí en las recargas.
Otro ejemplo más son las railties o motores que decoran clases del marco incluyendo módulos. Por ejemplo, turbo-rails
decora ActiveRecord::Base
de esta manera:
initializer "turbo.broadcastable" do
ActiveSupport.on_load(:active_record) do
include Turbo::Broadcastable
end
end
Eso agrega un objeto de módulo a la cadena de ancestros de ActiveRecord::Base
. Los cambios en Turbo::Broadcastable
no tendrían efecto si se recargan, la cadena de ancestros aún tendría el original.
Corolario: Esas clases o módulos no pueden ser recargables.
Una forma idiomática de organizar estos archivos es colocarlos en el directorio lib
y cargarlos con require
donde sea necesario. Por ejemplo, si la aplicación tiene middleware personalizado en lib/middleware
, emite una llamada require
regular antes de configurarlo:
require "middleware/my_middleware"
config.middleware.use MyMiddleware
Además, si lib
está en las rutas de autocarga, configura el autocargador para ignorar ese subdirectorio:
# config/application.rb
config.autoload_lib(ignore: %w(assets tasks ... middleware))
ya que estás cargando esos archivos tú mismo.
Como se mencionó anteriormente, otra opción es tener el directorio que los define en las rutas de autocarga única y autocargar. Por favor, consulta la sección sobre config.autoload_once_paths para más detalles.
8.3 Caso de Uso 3: Configurar Clases de Aplicación para Motores
Supongamos que un motor trabaja con la clase de aplicación recargable que modela los usuarios, y tiene un punto de configuración para ella:
# config/initializers/my_engine.rb
MyEngine.configure do |config|
config.user_model = User # NameError
end
Para jugar bien con el código de la aplicación recargable, el motor en su lugar necesita que las aplicaciones configuren el nombre de esa clase:
# config/initializers/my_engine.rb
MyEngine.configure do |config|
config.user_model = "User" # OK
end
Entonces, en tiempo de ejecución, config.user_model.constantize
te da el objeto de clase actual.
9 Carga Anticipada
En entornos de producción o similares, generalmente es mejor cargar todo el código de la aplicación cuando la aplicación se inicia. La carga anticipada pone todo en memoria listo para atender solicitudes de inmediato, y también es amigable con CoW.
La carga anticipada se controla mediante la bandera config.eager_load
, que está deshabilitada por defecto en todos los entornos excepto production
. Cuando se ejecuta una tarea de Rake, config.eager_load
se anula mediante config.rake_eager_load
, que es false
por defecto. Por lo tanto, por defecto, en entornos de producción las tareas de Rake no cargan anticipadamente la aplicación.
El orden en el que se cargan anticipadamente los archivos es indefinido.
Durante la carga anticipada, Rails invoca Zeitwerk::Loader.eager_load_all
. Eso asegura que todas las dependencias de gemas gestionadas por Zeitwerk también se carguen anticipadamente.
10 Herencia de Tabla Única
La Herencia de Tabla Única no se lleva bien con la carga diferida: Active Record tiene que estar al tanto de las jerarquías de STI para funcionar correctamente, pero cuando se carga diferida, las clases se cargan precisamente solo bajo demanda.
Para abordar esta incompatibilidad fundamental, necesitamos precargar los STIs. Hay algunas opciones para lograr esto, con diferentes compensaciones. Veámoslas.
10.1 Opción 1: Habilitar Carga Anticipada
La forma más fácil de precargar los STIs es habilitar la carga anticipada configurando:
config.eager_load = true
en config/environments/development.rb
y config/environments/test.rb
.
Esto es simple, pero puede ser costoso porque carga anticipadamente toda la aplicación al inicio y en cada recarga. Sin embargo, la compensación puede valer la pena para aplicaciones pequeñas.
10.2 Opción 2: Precargar un Directorio Colapsado
Almacena los archivos que definen la jerarquía en un directorio dedicado, lo que también tiene sentido conceptualmente. El directorio no está destinado a representar un espacio de nombres, su único propósito es agrupar el STI:
app/models/shapes/shape.rb
app/models/shapes/circle.rb
app/models/shapes/square.rb
app/models/shapes/triangle.rb
En este ejemplo, aún queremos que app/models/shapes/circle.rb
defina Circle
, no Shapes::Circle
. Esto puede ser tu preferencia personal para mantener las cosas simples, y también evita refactorizaciones en bases de código existentes. La característica de colapsar de Zeitwerk nos permite hacer eso:
# config/initializers/preload_stis.rb
shapes = "#{Rails.root}/app/models/shapes"
Rails.autoloaders.main.collapse(shapes) # No es un espacio de nombres.
unless Rails.application.config.eager_load
Rails.application.config.to_prepare do
Rails.autoloaders.main.eager_load_dir(shapes)
end
end
En esta opción, cargamos anticipadamente estos pocos archivos al inicio y recarga incluso si el STI no se usa. Sin embargo, a menos que tu aplicación tenga muchos STIs, esto no tendrá ningún impacto medible.
El método Zeitwerk::Loader#eager_load_dir
fue agregado en Zeitwerk 2.6.2. Para versiones anteriores, aún puedes listar el directorio app/models/shapes
e invocar require_dependency
en sus contenidos.
ADVERTENCIA: Si se agregan, modifican o eliminan modelos del STI, la recarga funciona como se espera. Sin embargo, si se agrega una nueva jerarquía STI separada a la aplicación, necesitarás editar el inicializador y reiniciar el servidor.
10.3 Opción 3: Precargar un Directorio Regular
Similar a la anterior, pero el directorio está destinado a ser un espacio de nombres. Es decir, se espera que app/models/shapes/circle.rb
defina Shapes::Circle
.
Para este, el inicializador es el mismo excepto que no se configura colapsar:
# config/initializers/preload_stis.rb
unless Rails.application.config.eager_load
Rails.application.config.to_prepare do
Rails.autoloaders.main.eager_load_dir("#{Rails.root}/app/models/shapes")
end
end
Mismas compensaciones.
10.4 Opción 4: Precargar Tipos desde la Base de Datos
En esta opción no necesitamos organizar los archivos de ninguna manera, pero accedemos a la base de datos:
# config/initializers/preload_stis.rb
unless Rails.application.config.eager_load
Rails.application.config.to_prepare do
types = Shape.unscoped.select(:type).distinct.pluck(:type)
types.compact.each(&:constantize)
end
end
ADVERTENCIA: El STI funcionará correctamente incluso si la tabla no tiene todos los tipos, pero métodos como subclasses
o descendants
no devolverán los tipos que faltan.
ADVERTENCIA: Si se agregan, modifican o eliminan modelos del STI, la recarga funciona como se espera. Sin embargo, si se agrega una nueva jerarquía STI separada a la aplicación, necesitarás editar el inicializador y reiniciar el servidor.
11 Personalización de Inflexiones
Por defecto, Rails utiliza String#camelize
para saber qué constante debería definir un nombre de archivo o directorio dado. Por ejemplo, posts_controller.rb
debería definir PostsController
porque eso es lo que "posts_controller".camelize
devuelve.
Podría ser el caso de que algún nombre de archivo o directorio particular no se inflexione como deseas. Por ejemplo, html_parser.rb
se espera que defina HtmlParser
por defecto. ¿Qué pasa si prefieres que la clase sea HTMLParser
? Hay algunas formas de personalizar esto.
La forma más fácil es definir acrónimos:
ActiveSupport::Inflector.inflections(:en) do |inflect|
inflect.acronym "HTML"
inflect.acronym "SSL"
end
Hacer esto afecta cómo Active Support inflexiona globalmente. Eso puede estar bien en algunas aplicaciones, pero también puedes personalizar cómo camelizar nombres de base individuales independientemente de Active Support pasando una colección de sobrescrituras a los inflectores predeterminados:
Rails.autoloaders.each do |autoloader|
autoloader.inflector.inflect(
"html_parser" => "HTMLParser",
"ssl_error" => "SSLError"
)
end
Esa técnica aún depende de String#camelize
, sin embargo, porque eso es lo que los inflectores predeterminados usan como respaldo. Si en su lugar prefieres no depender de las inflexiones de Active Support en absoluto y tener control absoluto sobre las inflexiones, configura los inflectores para que sean instancias de Zeitwerk::Inflector
:
Rails.autoloaders.each do |autoloader|
autoloader.inflector = Zeitwerk::Inflector.new
autoloader.inflector.inflect(
"html_parser" => "HTMLParser",
"ssl_error" => "SSLError"
)
end
No hay configuración global que pueda afectar dichas instancias; son deterministas.
Incluso puedes definir un inflector personalizado para una flexibilidad total. Por favor, consulta la documentación de Zeitwerk para más detalles.
11.1 ¿Dónde Debería Ir la Personalización de Inflexiones?
Si una aplicación no usa el autocargador once
, los fragmentos de código anteriores pueden ir en config/initializers
. Por ejemplo, config/initializers/inflections.rb
para el caso de uso de Active Support, o config/initializers/zeitwerk.rb
para los otros.
Las aplicaciones que usan el autocargador once
tienen que mover o cargar esta configuración desde el cuerpo de la clase de la aplicación en config/application.rb
, porque el autocargador once
usa el inflector temprano en el proceso de inicio.
12 Espacios de Nombres Personalizados
Como vimos anteriormente, las rutas de autocarga representan el espacio de nombres de nivel superior: Object
.
Consideremos app/services
, por ejemplo. Este directorio no se genera por defecto, pero si existe, Rails lo agrega automáticamente a las rutas de autocarga.
Por defecto, se espera que el archivo app/services/users/signup.rb
defina Users::Signup
, pero ¿qué pasa si prefieres que todo ese subárbol esté bajo un espacio de nombres Services
? Bueno, con la configuración predeterminada, eso se puede lograr creando un subdirectorio: app/services/services
.
Sin embargo, dependiendo de tu gusto, eso podría no parecerte correcto. Podrías preferir que app/services/users/signup.rb
simplemente defina Services::Users::Signup
.
Zeitwerk admite espacios de nombres raíz personalizados para abordar este caso de uso, y puedes personalizar el autocargador main
para lograrlo:
# config/initializers/autoloading.rb
# El espacio de nombres tiene que existir.
#
# En este ejemplo, definimos el módulo en el lugar. También podría crearse
# en otro lugar y su definición cargarse aquí con un `require` ordinario. En
# cualquier caso, `push_dir` espera un objeto de clase o módulo.
module Services; end
Rails.autoloaders.main.push_dir("#{Rails.root}/app/services", namespace: Services)
Rails < 7.1 no admitía esta característica, pero aún puedes agregar este código adicional en el mismo archivo y hacerlo funcionar:
# Código adicional para aplicaciones que se ejecutan en Rails < 7.1.
app_services_dir = "#{Rails.root}/app/services" # tiene que ser una cadena
ActiveSupport::Dependencies.autoload_paths.delete(app_services_dir)
Rails.application.config.watchable_dirs[app_services_dir] = [:rb]
Los espacios de nombres personalizados también son compatibles con el autocargador once
. Sin embargo, dado que ese se configura más temprano en el proceso de inicio, la configuración no se puede hacer en un inicializador de aplicación. En su lugar, colócalo en config/application.rb
, por ejemplo.
13 Autocarga y Motores
Los motores se ejecutan en el contexto de una aplicación principal, y su código es autocargado, recargado y cargado anticipadamente por la aplicación principal. Si la aplicación se ejecuta en modo zeitwerk
, el código del motor se carga en modo zeitwerk
. Si la aplicación se ejecuta en modo classic
, el código del motor se carga en modo classic
.
Cuando Rails se inicia, los directorios de los motores se agregan a las rutas de autocarga, y desde el punto de vista del autocargador, no hay diferencia. Las entradas principales de los autocargadores son las rutas de autocarga, y si pertenecen al árbol de código fuente de la aplicación o al árbol de código fuente de algún motor es irrelevante.
Por ejemplo, esta aplicación usa Devise:
$ bin/rails runner 'pp ActiveSupport::Dependencies.autoload_paths'
[".../app/controllers",
".../app/controllers/concerns",
".../app/helpers",
".../app/models",
".../app/models/concerns",
".../gems/devise-4.8.0/app/controllers",
".../gems/devise-4.8.0/app/helpers",
".../gems/devise-4.8.0/app/mailers"]
Si el motor controla el modo de autocarga de su aplicación principal, el motor puede escribirse como de costumbre.
Sin embargo, si un motor admite Rails 6 o Rails 6.1 y no controla sus aplicaciones principales, tiene que estar listo para ejecutarse en modo classic
o zeitwerk
. Cosas a tener en cuenta:
Si el modo
classic
necesitaría una llamadarequire_dependency
para asegurar que alguna constante se cargue en algún momento, escríbela. Aunquezeitwerk
no lo necesitaría, no hará daño, funcionará en modozeitwerk
también.El modo
classic
subraya los nombres de las constantes ("User" -> "user.rb"), y el modozeitwerk
cameliza los nombres de archivo ("user.rb" -> "User"). Coinciden en la mayoría de los casos, pero no lo hacen si hay series de letras mayúsculas consecutivas como en "HTMLParser". La forma más fácil de ser compatible es evitar tales nombres. En este caso, elige "HtmlParser".En modo
classic
, el archivoapp/model/concerns/foo.rb
tiene permitido definir tantoFoo
comoConcerns::Foo
. En modozeitwerk
, solo hay una opción: tiene que definirFoo
. Para ser compatible, defineFoo
.
14 Pruebas
14.1 Pruebas Manuales
La tarea zeitwerk:check
verifica si el árbol del proyecto sigue las convenciones de nomenclatura esperadas y es útil para verificaciones manuales. Por ejemplo, si estás migrando de modo classic
a zeitwerk
, o si estás corrigiendo algo:
$ bin/rails zeitwerk:check
Hold on, I am eager loading the application.
All is good!
Puede haber salida adicional dependiendo de la configuración de la aplicación, pero el último "All is good!" es lo que estás buscando.
14.2 Pruebas Automatizadas
Es una buena práctica verificar en el conjunto de pruebas que el proyecto se carga anticipadamente correctamente.
Eso cubre el cumplimiento de la nomenclatura de Zeitwerk y otras posibles condiciones de error. Por favor, consulta la sección sobre pruebas de carga anticipada en la guía Testing Rails Applications.
15 Solución de Problemas
La mejor manera de seguir lo que están haciendo los cargadores es inspeccionar su actividad.
La forma más fácil de hacer eso es incluir
Rails.autoloaders.log!
en config/application.rb
después de cargar los valores predeterminados del marco. Eso imprimirá trazas en la salida estándar.
Si prefieres registrar en un archivo, configura esto en su lugar:
Rails.autoloaders.logger = Logger.new("#{Rails.root}/log/autoloading.log")
El registrador de Rails aún no está disponible cuando se ejecuta config/application.rb
. Si prefieres usar el registrador de Rails, configura esta opción en un inicializador en su lugar:
# config/initializers/log_autoloaders.rb
Rails.autoloaders.logger = Rails.logger
16 Rails.autoloaders
Las instancias de Zeitwerk que gestionan tu aplicación están disponibles en
Rails.autoloaders.main
Rails.autoloaders.once
El predicado
Rails.autoloaders.zeitwerk_enabled?
aún está disponible en aplicaciones Rails 7, y devuelve true
.
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.