1 Introduction
Esta guía documenta la carga automática en el modo clásico
, que es el tradicional. Si desea leer sobre el modo zeitwerk
en su lugar, el nuevo en Rails 6, verifique Constantes de carga y carga automática (modo Zeitwerk).
Ruby on Rails permite que las aplicaciones se escriban como si su código estuviera precargado.
En un programa normal de Ruby, las clases necesitan cargar sus dependencias:
require "application_controller"
require "post"
class PostsController < ApplicationController
def index
@posts = Post.all
end
end
Nuestro instinto rubyista ve rápidamente algo de redundancia allí: si las clases fueran definido en archivos que coincidan con su nombre, ¿no podría automatizarse su carga? ¿de algun modo? Podríamos ahorrar escaneando el archivo en busca de dependencias, lo cual es frágil.
Además, Kernel#require
carga archivos una vez, pero el desarrollo es mucho más fluido
si el código se actualiza cuando cambia sin reiniciar el servidor. Sería
Sería bueno poder usar Kernel#load
en desarrollo, y Kernel#require
en
producción.
De hecho, esas funciones las proporciona Ruby on Rails, donde simplemente escribimos
class PostsController < ApplicationController
def index
@posts = Post.all
end
end
Esta guía documenta cómo funciona.
2 Constants Refresher
Si bien las constantes son triviales en la mayoría de los lenguajes de programación, son una rica tema en Ruby.
Está más allá del alcance de esta guía documentar las constantes de Ruby, pero estamos no obstante, voy a destacar algunos temas clave. Verdaderamente captando lo siguiente secciones es fundamental para comprender la carga y la recarga automáticas constantes.
2.1 Nesting
Las definiciones de clases y módulos se pueden anidar para crear espacios de nombres:
module XML
class SAXParser
# (1)
end
end
El anidamiento en cualquier lugar dado es la colección de clases anidadas adjuntas y
los objetos del módulo hacia afuera. El anidamiento en cualquier lugar dado se puede inspeccionar con
Module.nesting
. Por ejemplo, en el ejemplo anterior, el anidamiento en
(1) es
[XML::SAXParser, XML]
Es importante comprender que el anidamiento se compone de clase y módulo objetos, no tiene nada que ver con las constantes utilizadas para acceder a ellos, y es también sin relación con sus nombres.
Por ejemplo, si bien esta definición es similar a la anterior:
class XML::SAXParser
# (2)
end
el anidamiento en (2) es diferente:
[XML::SAXParser]
XML
no le pertenece.
Podemos ver en este ejemplo que el nombre de una clase o módulo que pertenece a un cierto anidamiento no se correlaciona necesariamente con los espacios de nombres en el lugar.
Más aún, son totalmente independientes, tomemos por ejemplo
module X
module Y
end
end
module A
module B
end
end
module X::Y
module A::B
# (3)
end
end
El anidamiento en (3) consta de dos objetos de módulo:
[A::B, X::Y]
Entonces, no solo no termina en A
, que ni siquiera pertenece al anidamiento,
pero también contiene X::Y
, que es independiente de A::B
.
El anidamiento es una pila interna mantenida por el intérprete, y se obtiene modificado de acuerdo con estas reglas:
El objeto de clase que sigue a una palabra clave
class
se inserta cuando su cuerpo es ejecutado, y apareció tras él.El objeto de módulo que sigue a una palabra clave
module
se envía cuando su cuerpo ejecutado, y apareció tras él.Una clase singleton abierta con
class << object
es empujada y aparece más tarde.Cuando se llama a
instance_eval
usando un argumento de cadena, la clase singleton del receptor se empuja al anidamiento del eval'ed código. Cuando se llama aclass_eval
omodule_eval
usando un argumento de cadena, el receptor es empujado al anidamiento del código evaluado.El anidamiento en el nivel superior de código interpretado por
Kernel#load
está vacío a menos que la llamadaload
reciba un valor verdadero como segundo argumento, en cuyo caso Ruby envía un módulo anónimo recién creado.
Es interesante observar que los bloques no modifican la pila. En particular
los bloques que se pueden pasar a Class.new
y Module.new
no obtienen el
clase o módulo que se define empujado a su anidamiento. Ese es uno de los
diferencias entre definir clases y módulos de una forma u otra.
2.2 Class and Module Definitions are Constant Assignments
Supongamos que el siguiente fragmento crea una clase (en lugar de volver a abrirla):
class C
end
Ruby crea una constante C
en Object
y almacena en esa constante una clase
objeto. El nombre de la instancia de la clase es "C", una cadena que recibe el nombre de
constante.
Es decir,
class Project < ApplicationRecord
end
realiza una asignación constante equivalente a
Project = Class.new(ApplicationRecord)
including setting the name of the class as a side-effect:
Project.name # => "Project"
La asignación constante tiene una regla especial para que eso suceda: si el objeto que se asigna es una clase o módulo anónimo, Ruby establece el nombre del objeto en el nombre de la constante.
A partir de entonces, lo que sucede con la constante y la instancia no importar. Por ejemplo, la constante podría eliminarse, el objeto de clase podría ser asignado a una constante diferente, ya no se almacenará en una constante, etc. el nombre está establecido, no cambia.
De manera similar, la creación de módulos usando la palabra clave module
como en
module Admin
end
realiza una asignación constante equivalente a
Admin = Module.new
including setting the name as a side-effect:
Admin.name # => "Admin"
El contexto de ejecución de un bloque pasado a Class.new
o Module.new
no es enteramente equivalente a la del cuerpo de las definiciones utilizando el
Palabras clave class
y module
. Pero ambos modismos resultan en la misma constante
asignación.
Por lo tanto, una expresión informal como "la clase String
" significa técnicamente la
objeto de clase almacenado en la constante llamada "Cadena". Esa constante, a su vez,
pertenece al objeto de clase almacenado en la constante denominada "Objeto".
String
es una constante ordinaria, y todo lo relacionado con ellos, como
se le aplican algoritmos de resolución.
Asimismo, en el controlador
class PostsController < ApplicationController
def index
@posts = Post.all
end
end
Post
no es la sintaxis de una clase. Más bien, Post
es una constante de Ruby regular. Si
todo es bueno, la constante se evalúa a un objeto que responde a "todo".
Por eso hablamos de carga automática * constante *, Rails tiene la capacidad de cargar constantes sobre la marcha.
2.3 Constants are Stored in Modules
Las constantes pertenecen a módulos en un sentido muy literal. Las clases y los módulos tienen una tabla constante; Piense en ello como una tabla hash.
Analicemos un ejemplo para entender realmente lo que eso significa. Aunque es común
los abusos del lenguaje como "la clase String
"son convenientes, la exposición es
va a ser preciso aquí con fines didácticos.
Consideremos la siguiente definición de módulo:
module Colors
RED = '0xff0000'
end
Primero, cuando se procesa la palabra clave module
, el intérprete crea una nueva
entrada en la tabla de constantes del objeto de clase almacenado en la constante Object
.
Dicha entrada asocia el nombre "Colores" a un objeto de módulo recién creado.
Además, el intérprete establece el nombre del nuevo objeto de módulo como el
cadena "Colores".
Posteriormente, cuando se interpreta el cuerpo de la definición del módulo, se crea una nueva entrada
creado en la tabla constante del objeto módulo almacenado en los Colors
constante. Esa entrada asigna el nombre "RED" a la cadena "0xff0000".
En particular, Colors::RED
no tiene ninguna relación con ninguna otra constante de RED
que puede vivir en cualquier otro objeto de clase o módulo. Si hubiera alguno, ellos
tendría entradas separadas en sus respectivas tablas de constantes.
Preste especial atención en los párrafos anteriores a la distinción entre Objetos de clase y módulo, nombres de constantes y objetos de valor asociados a ellos. en tablas constantes.
2.4 Resolution Algorithms
2.4.1 Resolution Algorithm for Relative Constants
En cualquier lugar del código, definamos cref como el primer elemento de
el anidamiento si no está vacío, o el Object
en caso contrario.
Sin entrar demasiado en los detalles, el algoritmo de resolución para las referencias constantes son así:
Si el anidamiento no está vacío, la constante se busca en sus elementos y en orden. Los antepasados de esos elementos se ignoran.
Si no se encuentra, entonces el algoritmo recorre la cadena de ancestros de la cref.
Si no se encuentra y el cref es un módulo, la constante se busca en "Objeto".
Si no se encuentra, se invoca
const_missing
en la cref. El valor por defecto la implementación deconst_missing
generaNameError
, pero puede ser anulado.
La carga automática de rieles ** no emula este algoritmo **, pero su punto de partida es el nombre de la constante que se va a cargar automáticamente y la cref. Ver más en Relativo Referencias.
2.4.2 Resolution Algorithm for Qualified Constants
Las constantes calificadas tienen este aspecto:
Billing::Invoice
Billing::Invoice
se compone de dos constantes: Billing
es relativo y es
resuelto utilizando el algoritmo del apartado anterior.
Los dos puntos iniciales harían que el primer segmento sea absoluto en lugar de
relativo: ::Billing::Invoice
. Eso obligaría a buscar "Facturación"
solo como una constante de nivel superior.
Invoice
por otro lado está calificado por Billing
y vamos a ver
su resolución a continuación. Definamos * parent * como esa clase o módulo calificado
objeto, es decir, "Facturación" en el ejemplo anterior. El algoritmo para calificado
constantes va así:
La constante se busca en el padre y sus antepasados. En Ruby> = 2.5,
Object
se omite si está presente entre los antepasados.Kernel
yBasicObject
aunque todavía están marcados.Si la búsqueda falla, se invoca
const_missing
en el padre. El valor por defecto la implementación deconst_missing
generaNameError
, pero puede ser anulado.
En Ruby <2.5 String::Hash
se evalúa comoHash
y el intérprete
emite una advertencia: "Hash constante de nivel superior referenciado por String :: Hash". Comenzando
con 2.5, String::Hash
genera NameError
porque se omite Object
.
Como ves, este algoritmo es más simple que el de las constantes relativas. En
En particular, el anidamiento no juega ningún papel aquí, y los módulos no tienen una carcasa especial,
si ni ellos ni sus antepasados tienen las constantes, Object
es ** no **
comprobado.
La carga automática de rieles ** no emula este algoritmo **, pero su punto de partida es el nombre de la constante que se va a cargar automáticamente y el padre. Ver más en Referencias calificadas.
3 Vocabulary
3.1 Parent Namespaces
Dada una cadena con una ruta constante, definimos su * espacio de nombres principal * como el cadena que resulta de eliminar su segmento más a la derecha.
Por ejemplo, el espacio de nombres principal de la cadena "A::B::C" es la cadena "A::B", el espacio de nombres principal de "A::B" es "A", y el espacio de nombres principal de "A" es "".
La interpretación de un espacio de nombres principal al pensar en clases y módulos aunque es complicado. Consideremos un módulo M llamado "A::B":
El espacio de nombres principal, "A", puede no reflejar el anidamiento en un lugar determinado.
Es posible que la constante
A
ya no exista, algún código podría haberla eliminado deObjeto
.Si existe "A", es posible que la clase o módulo que originalmente estaba en "A" no esté allí nunca más. Por ejemplo, si después de una remoción constante hubo otra constante asignación, generalmente habría un objeto diferente allí.
En tal caso, incluso podría suceder que el "A" reasignado tuviera una nueva clase o módulo llamado también "A"!
En los escenarios anteriores, M ya no sería accesible a través de
A::B
pero el objeto del módulo en sí podría estar vivo en algún lugar y su nombre sería seguirá siendo "A::B".
La idea de un espacio de nombres principal es el núcleo de los algoritmos de carga automática y ayuda a explicar y comprender su motivación de manera intuitiva, pero como ve esa metáfora se filtra fácilmente. Dado un caso límite sobre el que razonar, tenga siempre en cuenta Tenga en cuenta que por "espacio de nombres principal" la guía se refiere exactamente a esa cadena específica derivación.
3.2 Loading Mechanism
Rails carga automáticamente archivos con Kernel#load
cuando config.cache_classes
es falso,
el valor predeterminado en el modo de desarrollo, y con Kernel#require
de lo contrario, el
predeterminado en modo de producción.
Kernel#load
permite a Rails ejecutar archivos más de una vez si constante
recarga está habilitada.
Esta guía utiliza la palabra "cargar" libremente para indicar que se interpreta un archivo determinado, pero
el mecanismo real puede ser Kernel#load
o Kernel#require
dependiendo de eso
bandera.
4 Autoloading Availability
Rails siempre se puede cargar automáticamente siempre que su entorno esté en su lugar. por
ejemplo, el comando runner
se carga automáticamente:
`` bash $ bin / rails runner 'p User.column_names' ["id", "email", "created_at", "updated_at"] ''
La consola se carga automáticamente, la suite de pruebas se carga automáticamente y, por supuesto, la aplicación cargas automáticas.
De forma predeterminada, Rails ansioso carga los archivos de la aplicación cuando arranca en producción modo, por lo que la mayor parte de la carga automática que ocurre en el desarrollo no ocurre. Pero la carga automática aún puede activarse durante la carga ansiosa.
Por ejemplo, dado
class BeachHouse < House
end
si todavía se desconoce House
cuando app/models/beach_house.rb
está ansioso
cargado, Rails lo carga automáticamente.
5 autoload_paths and eager_load_paths
Como probablemente sepa, cuando require
obtiene un nombre de archivo relativo:
require "erb"
Ruby busca el archivo en los directorios listados en $LOAD_PATH
. Es decir, ruby
itera sobre todos sus directorios y para cada uno de ellos comprueba si
tener un archivo llamado "erb.rb", o "erb.so", o "erb.o", o "erb.dll". Si encuentra
cualquiera de ellos, el intérprete lo carga y finaliza la búsqueda. De lo contrario, intenta
nuevamente en el siguiente directorio de la lista. Si la lista se agota, LoadError
es elevado.
Más adelante cubriremos cómo funciona la carga automática constante con más detalle, pero
la idea es que cuando una constante como Post
se activa y falta, si hay una
El archivo post.rb
por ejemplo en app/models
Rails lo encontrará, evalúe
y tener Post
definido como un efecto secundario.
Muy bien, Rails tiene una colección de directorios similar a $LOAD_PATH
en la que
para buscar post.rb
. Esa colección se llama autoload_paths
y por
por defecto contiene:
Todos los subdirectorios de
app
en la aplicación y motores presentes en el arranque hora. Por ejemplo,app / controllers
. No es necesario que sean los predeterminados unos, cualquier directorio personalizado comoapp/workers
pertenece automáticamente aautoload_paths
.Cualquier directorio de segundo nivel existente llamado
app/*/concern
en el aplicación y motores.El directorio
test/mailers/previews
.
eager_load_paths
es inicialmente las rutas de la app
anteriores
La forma en que se cargan automáticamente los archivos depende de los ajustes de configuración de eager_load
y cache_classes
que normalmente varían en los modos de desarrollo, producción y prueba:
- En desarrollo, desea un inicio más rápido con carga incremental del código de la aplicación. Por lo tanto,
eager_load
debe establecerse enfalse
, y Rails cargará automáticamente los archivos según sea necesario (consulte Algoritmos de carga automática a continuación) y luego los volverá a cargar cuando cambien (consulte Constant Reloading a continuación). - En producción, sin embargo, desea consistencia y seguridad de subprocesos y puede vivir con un tiempo de arranque más largo. Entonces,
eager_load
se establece entrue
, y luego, durante el arranque (antes de que la aplicación esté lista para recibir solicitudes), Rails carga todos los archivos eneager_load_paths
y luego desactiva la carga automática (NB: la carga automática puede ser necesaria durante la carga ansiosa ). No cargar automáticamente después del arranque es algo bueno, ya que la carga automática puede hacer que la aplicación tenga problemas de seguridad de subprocesos. - En prueba, para la velocidad de ejecución (de pruebas individuales),
eager_load
esfalse
, por lo que Rails sigue el comportamiento de desarrollo.
Lo que se describe arriba son los valores predeterminados con una aplicación Rails recién generada. Hay varias formas en que esto se puede configurar de manera diferente (consulte Configuración de aplicaciones de Rails.
). Pero usando autoload_paths
por sí solo en el pasado (antes de Rails 5), los desarrolladores podían configurar autoload_paths
para agregar ubicaciones adicionales (por ejemplo, lib
, que solía ser una lista de rutas de autocarga hace años, pero ya no lo es). Sin embargo, esto ahora se desaconseja para la mayoría de los propósitos, ya que es probable que dé lugar a errores solo de producción. Es posible agregar nuevas ubicaciones tanto a config.eager_load_paths
como a config.autoload_paths
pero utilícelo bajo su propio riesgo.
Ver también Carga Automática en el Entorno de Prueba.
Se puede inspeccionar el valor de autoload_paths
. En una aplicación recién generada
es (editado):
$ bin/rails runner 'puts ActiveSupport::Dependencies.autoload_paths'
.../app/assets
.../app/channels
.../app/controllers
.../app/controllers/concerns
.../app/helpers
.../app/jobs
.../app/mailers
.../app/models
.../app/models/concerns
.../activestorage/app/assets
.../activestorage/app/controllers
.../activestorage/app/javascript
.../activestorage/app/jobs
.../activestorage/app/models
.../actioncable/app/assets
.../actionview/app/assets
.../test/mailers/previews
autoload_paths
se calcula y almacena en caché durante el proceso de inicialización.
La aplicación debe reiniciarse para reflejar cualquier cambio en el directorio.
estructura.
6 Autoloading Algorithms
6.1 Relative References
Una referencia constante relativa puede aparecer en varios lugares, por ejemplo, en
class PostsController < ApplicationController
def index
@posts = Post.all
end
end
las tres referencias constantes son relativas.
6.1.1 Constants after the class
and module
Keywords
Ruby realiza una búsqueda de la constante que sigue a una class
o un module
palabra clave porque necesita saber si la clase o módulo se va a crear
o reabierto.
Si la constante no está definida en ese punto, no se considera una Falta constante, la carga automática ** no ** se activa.
Entonces, en el ejemplo anterior, si PostsController
no está definido cuando el archivo
se interpreta que la carga automática de Rails no se activará, Ruby simplemente
definir el controlador.
6.1.2 Top-Level Constants
Por el contrario, si se desconoce ApplicationController
, la constante es
se considera perdido y Rails intentará una carga automática.
Para cargar ApplicationController
, Rails itera sobre autoload_paths
.
Primero verifica si existe app/assets/application_controller.rb
. Si no es así,
que es normalmente el caso, continúa y encuentra
app/controllers/application_controller.rb
.
Si el archivo define la constante ApplicationController
, todo está bien, de lo contrario
Se genera LoadError
:
''
no se puede cargar automáticamente ApplicationController constante, esperado
Rails no requiere que el valor de las constantes autocargadas sea una clase o
objeto de módulo. Por ejemplo, si el archivo app/models/max_clients.rb
define
MAX_CLIENTS = 100
la carga automática de MAX_CLIENTS
funciona bien.
6.1.3 Namespaces
La carga automática de ApplicationController
busca directamente debajo de los directorios de
autoload_paths
porque el nido en ese lugar está vacío. La situación de
Post
es diferente, el anidamiento en esa línea es [PostsController]
y support
para espacios de nombres entra en juego.
La idea básica es que dada
module Admin
class BaseController < ApplicationController
@@all_roles = Role.all
end
end
para autocargar Role
vamos a comprobar si está definido en el actual o
espacios de nombres de los padres, uno a la vez. Entonces, conceptualmente queremos intentar cargar automáticamente
cualquiera de
Admin::BaseController::Role
Admin::Role
Role
en ese orden. Esa es la idea. Para hacerlo, Rails busca en autoload_paths
respectivamente para nombres de archivo como estos:
admin/base_controller/role.rb
admin/role.rb
role.rb
módulo algunas búsquedas de directorio adicionales que vamos a cubrir pronto.
'Constant::Name'.undercore
da la ruta relativa sin extensión de
el nombre del archivo donde se espera que se defina Constant::Name
.
Veamos cómo Rails carga automáticamente la constante Post
en el PostsController
arriba asumiendo que la aplicación tiene un modelo Post
definido en
app/models/post.rb
.
Primero busca posts_controller/post.rb
en autoload_paths
:
app/assets/posts_controller/post.rb
app/controllers/posts_controller/post.rb
app/helpers/posts_controller/post.rb
...
test/mailers/previews/posts_controller/post.rb
Dado que la búsqueda se agota sin éxito, una búsqueda similar de un directorio se realiza, vamos a ver por qué en la next section:
app/assets/posts_controller/post
app/controllers/posts_controller/post
app/helpers/posts_controller/post
...
test/mailers/previews/posts_controller/post
Si todos esos intentos fallan, Rails vuelve a iniciar la búsqueda en el archivo principal. espacio de nombres. En este caso, solo queda el nivel superior:
app/assets/post.rb
app/controllers/post.rb
app/helpers/post.rb
app/mailers/post.rb
app/models/post.rb
Se encuentra un archivo coincidente en app/models/post.rb
. La búsqueda se detiene allí y el
se carga el archivo. Si el archivo realmente define "Publicar", todo está bien, de lo contrario
Se genera LoadError
.
6.2 Qualified References
Cuando falta una constante calificada, Rails no la busca en el padre espacios de nombres. Pero hay una advertencia: cuando falta una constante, Rails es incapaz de saber si el disparador era una referencia relativa o calificada.
Por ejemplo, considere
module Admin
User
end
y
Admin::User
Si falta User
, en cualquier caso, todo lo que Rails sabe es que una constante llamada
Faltaba "Usuario" en un módulo llamado "Admin".
Si hay un User
de nivel superior, Ruby lo resolvería en el ejemplo anterior, pero
no lo haría en el último. En general, Rails no emula la constante de Ruby
algoritmos de resolución, pero en este caso intenta utilizar la siguiente heurística:
Si ninguno de los espacios de nombres principales de la clase o módulo tiene el constante, Rails asume que la referencia es relativa. De lo contrario calificado.
Por ejemplo, si este código activa la carga automática
Admin::User
y la constante User
ya está presente en Object
, no es posible que
la situación es
module Admin
User
end
porque de lo contrario Ruby habría resuelto User
y ninguna carga automática habría
se ha activado en primer lugar. Por lo tanto, Rails asume una referencia calificada y
considera que el archivo admin/user.rb
y el directorio admin/user
son los únicos
opciones válidas.
En la práctica, esto funciona bastante bien siempre que el anidamiento coincida con todos los padres espacios de nombres respectivamente y las constantes que hacen que la regla se aplique se conocen en ese momento.
Sin embargo, la carga automática se realiza a pedido. Si por casualidad el User
de nivel superior
aún no cargado, Rails asume una referencia relativa por contrato.
Los conflictos de nombres de este tipo son raros en la práctica, pero si ocurre alguno,
require_dependency
proporciona una solución asegurando que la constante necesaria
desencadenar la heurística se define en el lugar conflictivo.
6.3 Automatic Modules
Cuando un módulo actúa como un espacio de nombres, Rails no requiere que la aplicación definir un archivo para él, un directorio que coincida con el espacio de nombres es suficiente.
Suponga que una aplicación tiene un back office cuyos controladores se almacenan en
app/controllers/admin
. Si el módulo Admin
aún no está cargado cuando
Admin::UsersController
se activa, Rails necesita primero cargar automáticamente la constante
Admin
.
Si autoload_paths
tiene un archivo llamado admin.rb
Rails lo cargará
uno, pero si no existe tal archivo y se encuentra un directorio llamado admin
, Rails
crea un módulo vacío y lo asigna a la constante Admin
sobre la marcha.
6.4 Generic Procedure
Se informa que faltan referencias relativas en la cuadrilla donde fueron golpeadas, y se informa que faltan referencias calificadas en su padre (ver Algoritmo de resolución para relativo Constants al comienzo de esta guía para la definición de * cref , y algoritmo de resolución para calificados Constantes para la definición de *padre).
El procedimiento para cargar automáticamente la constante C
en una situación arbitraria es el siguiente:
if the class or module in which C is missing is Object
let ns = ''
else
let M = the class or module in which C is missing
if M is anonymous
let ns = ''
else
let ns = M.name
end
end
loop do
# Look for a regular file.
for dir in autoload_paths
if the file "#{dir}/#{ns.underscore}/c.rb" exists
load/require "#{dir}/#{ns.underscore}/c.rb"
if C is now defined
return
else
raise LoadError
end
end
end
# Look for an automatic module.
for dir in autoload_paths
if the directory "#{dir}/#{ns.underscore}/c" exists
if ns is an empty string
let C = Module.new in Object and return
else
let C = Module.new in ns.constantize and return
end
end
end
if ns is empty
# We reached the top-level without finding the constant.
raise NameError
else
if C exists in any of the parent namespaces
# Qualified constants heuristic.
raise NameError
else
# Try again in the parent namespace.
let ns = the parent namespace of ns and retry
end
end
end
7 require_dependency
La carga automática constante se activa a pedido y, por lo tanto, el código que utiliza un cierta constante puede tenerlo ya definido o puede activar una carga automática. Ese depende de la ruta de ejecución y puede variar entre ejecuciones.
Sin embargo, hay ocasiones en las que desea asegurarse de que cierta constante
conocido cuando la ejecución alcanza algún código. require_dependency
proporciona una forma
para cargar un archivo usando el mecanismo de carga, y
realizar un seguimiento de las constantes definidas en ese archivo como si estuvieran cargadas automáticamente para
Hágalos recargar según sea necesario.
require_dependency
rara vez se necesita, pero vea un par de casos de uso en
Autoloading y STI y Cuando las constantes no son
Activado.
A diferencia de la carga automática, require_dependency
no espera que el archivo
definir cualquier constante particular. Explotar este comportamiento sería una mala práctica
sin embargo, las rutas de archivo y constantes deben coincidir.
8 Constant Reloading
Cuando config.cache_classes
es falso, Rails puede recargar autocargado
constantes.
Por ejemplo, si está en una sesión de consola y edita algún archivo detrás del
escenas, el código se puede recargar con el comando reload!
:
> reload!
Cuando se ejecuta la aplicación, el código se vuelve a cargar cuando algo relevante para esto cambios de lógica. Para hacer eso, Rails monitorea una serie de cosas:
config/routes.rb
.Locales.
Archivos Rubydebajo
autoload_paths
.db/schema.rb
ydb/structure.sql
.
Si algo cambia allí, hay un middleware que lo detecta y vuelve a cargar el código.
La carga automática realiza un seguimiento de las constantes de carga automática. La recarga es implementada por
eliminándolos todos de sus respectivas clases y módulos usando
Módulo#remove_const
. De esa forma, cuando el código continúa, esas constantes son
volverá a ser desconocido y los archivos se volverán a cargar a pedido.
Esta es una operación de todo o nada, Rails no intenta recargar solo lo que cambió desde las dependencias entre clases lo hace realmente complicado. En cambio, todo se borra.
9 Common Gotchas
9.1 Nesting and Qualified Constants
Consideremos
module Admin
class UsersController < ApplicationController
def index
@users = User.all
end
end
end
y
class Admin::UsersController < ApplicationController
def index
@users = User.all
end
end
Para resolver User
Ruby verifica Admin
en el primer caso, pero no lo hace en
el último porque no pertenece al anidamiento (ver Anidamiento
y Algoritmos de resolución).
Desafortunadamente, la carga automática de Rails no conoce el anidamiento en el lugar donde
Faltaba una constante y, por lo tanto, no puede actuar como lo haría Ruby. En particular,
Admin::User
se cargará automáticamente en cualquier caso.
Aunque las constantes calificadas con palabras clave class
y module
pueden técnicamente
trabajar con carga automática en algunos casos, es preferible utilizar constantes relativas
en lugar:
module Admin
class UsersController < ApplicationController
def index
@users = User.all
end
end
end
9.2 Defining vs Reopening Namespaces
Let's consider:
# app/models/blog.rb
module Blog
def self.table_name_prefix
"blog_"
end
end
# app/models/blog/post.rb
module Blog
class Post < ApplicationRecord
end
end
El nombre de la tabla para Blog::Post
debería ser blog_posts
debido a la existencia de
el método Blog.table_name_prefix
. Sin embargo, si app/models/blog/post.rb
es
ejecutado antes de que app/models/blog.rb
sea, Active Record no es consciente del
existencia de tal método, y asume que la tabla es "posts".
Para resolver una situación como esta, es útil pensar claramente en qué archivo
define el módulo Blog
(app/models/blog.rb
), y cuál reabre
(app/models/blog/post.rb
). Luego, te aseguras de que la definición se ejecute
primero usando require_dependency
:
# app/models/blog/post.rb
require_dependency "blog"
module Blog
class Post < ApplicationRecord
end
end
9.3 Autoloading and STI
La herencia de tabla única (STI) es una función de Active Record que permite almacenar una jerarquía de modelos en una sola tabla. La API de tales modelos es consciente de la jerarquía y encapsula algunas necesidades comunes. Por ejemplo, dado estas clases:
# app/models/polygon.rb
class Polygon < ApplicationRecord
end
# app/models/triangle.rb
class Triangle < Polygon
end
# app/models/rectangle.rb
class Rectangle < Polygon
end
Triangle.create
crea una fila que representa un triángulo, y
Rectangle.create
crea una fila que representa un rectángulo. Si id
es el
ID de un registro existente, Polygon.find(id)
devuelve un objeto de la correcta
tipo.
Los métodos que operan en colecciones también conocen la jerarquía. por
ejemplo, Polygon.all
devuelve todos los registros de la tabla, porque todos
los rectángulos y los triángulos son polígonos. Active Record se encarga de regresar
instancias de su clase correspondiente en el conjunto de resultados.
Los tipos se cargan automáticamente según sea necesario. Por ejemplo, si "Polygon.first" es un rectángulo
y Rectangle
aún no se ha cargado, Active Record lo carga automáticamente y el
el registro está instanciado correctamente.
Todo bien, pero si en lugar de realizar consultas basadas en la clase raíz necesitamos para trabajar en alguna subclase, las cosas se ponen interesantes.
Mientras trabaja con Polygon
, no es necesario que conozca todos sus descendientes,
porque cualquier cosa en la tabla es por definición un polígono, pero cuando se trabaja con
Las subclases Active Record deben poder enumerar los tipos que busca.
para. Veamos un ejemplo.
Rectangle.all
solo carga rectángulos agregando una restricción de tipo a la consulta:
SELECT "polygons".* FROM "polygons"
WHERE "polygons"."type" IN ("Rectangle")
Introduzcamos ahora una subclase de Rectangle
:
# app/models/square.rb
class Square < Rectangle
end
Rectangle.all
ahora debería devolver rectángulos y cuadrados:
SELECT "polygons".* FROM "polygons"
WHERE "polygons"."type" IN ("Rectangle", "Square")
Pero hay una advertencia aquí: ¿Cómo sabe Active Record que la clase Square
existe en absoluto?
Incluso si el archivo app/models/square.rb
existe y define la clase Square
,
si aún no se ha utilizado ningún código de esa clase, Rectangle.all
emite la consulta
SELECT "polygons".* FROM "polygons"
WHERE "polygons"."type" IN ("Rectangle")
Eso no es un error, la consulta incluye todos los descendientes * conocidos * de Rectangle
.
Una forma de asegurarse de que esto funcione correctamente independientemente del orden de ejecución es cargue manualmente las subclases directas en la parte inferior del archivo que define cada clase intermedia:
# app/models/rectangle.rb
class Rectangle < Polygon
end
require_dependency 'square'
Esto debe suceder para cada clase intermedia (no raíz y no hoja). los la clase raíz no abarca la consulta por tipo y, por lo tanto, no necesariamente Hay que conocer a todos sus descendientes.
9.4 Autoloading and require
Los archivos que definen las constantes que se van a cargar automáticamente nunca deben ser require
d:
require "user" # DO NOT DO THIS
class UsersController < ApplicationController
...
end
Hay dos posibles errores aquí en el modo de desarrollo:
Si
User
se carga automáticamente antes de llegar alrequire
,app/models/user.rb
se ejecuta de nuevo porqueload
no actualiza$ LOADED_FEATURES
.Si el
require
se ejecuta primero, Rails no marca alUser
como autocargado constante y los cambios enapp/models/user.rb
no se vuelven a cargar.
Simplemente siga el flujo y use la carga automática constante siempre, nunca mezcle
carga automática y require
. Como último recurso, si algún archivo necesita absolutamente
cargar un determinado archivo use require_dependency
para jugar bien con constante
carga automática. Sin embargo, esta opción rara vez se necesita en la práctica.
Por supuesto, el uso de require
en archivos autocargados para cargar un tercero ordinario
bibliotecas está bien, y Rails es capaz de distinguir sus constantes, son
no marcado como autocargado.
9.5 Autoloading and Initializers
Considere esta asignación en config/initializers/set_auth_service.rb
:
AUTH_SERVICE = if Rails.env.production?
RealAuthService
else
MockedAuthService
end
El propósito de esta configuración sería que la aplicación use la clase que
corresponde al entorno a través de AUTH_SERVICE
. En modo de desarrollo
MockedAuthService
se carga automáticamente cuando se ejecuta el inicializador. Supongamos
hacemos algunas solicitudes, cambiamos su implementación y volvemos a activar la aplicación.
Para nuestra sorpresa, los cambios no se reflejan. ¿Por qué?
Como vimos anteriormente, Rails elimina las constantes cargadas automáticamente,
pero AUTH_SERVICE
almacena el objeto de clase original. Rancio, no accesible
utilizando la constante original, pero perfectamente funcional.
El siguiente código resume la situación:
class C
def quack
'quack!'
end
end
X = C
Object.instance_eval { remove_const(:C) }
X.new.quack # => quack!
X.name # => C
C # => uninitialized constant C (NameError)
Por eso, no es una buena idea cargar automáticamente constantes en la aplicación inicialización.
En el caso anterior podríamos implementar un punto de acceso dinámico:
# app/models/auth_service.rb
class AuthService
if Rails.env.production?
def self.instance
RealAuthService
end
else
def self.instance
MockedAuthService
end
end
end
and have the application use AuthService.instance
instead. AuthService
would be loaded on demand and be autoload-friendly.
9.6 require_dependency
and Initializers
Como vimos antes, require_dependency
carga archivos en un formato compatible con la carga automática.
camino. Sin embargo, normalmente esta llamada no tiene sentido en un inicializador.
Se podría pensar en hacer algo de require_dependency
llamadas en un inicializador para asegurarse de que ciertas constantes se cargan por adelantado, para
ejemplo como un intento de abordar el problema con las ITS.
El problema es que en el modo de desarrollo las constantes cargadas automáticamente se borran si hay algún cambio relevante en el sistema de archivos. Si eso sucede entonces ¡Estamos en la misma situación que el inicializador quería evitar!
Las llamadas a require_dependency
deben estar escritas estratégicamente en autocargado
lugares.
9.7 When Constants aren't Missed
9.7.1 Relative References
Consideremos un simulador de vuelo. La aplicación tiene un modelo de vuelo predeterminado
# app/models/flight_model.rb
class FlightModel
end
que puede ser anulado por cada avión, por ejemplo
# app/models/bell_x1/flight_model.rb
module BellX1
class FlightModel < FlightModel
end
end
# app/models/bell_x1/aircraft.rb
module BellX1
class Aircraft
def initialize
@flight_model = FlightModel.new
end
end
end
El inicializador quiere crear un BellX1::FlightModel
y el anidamiento tiene
"BellX1", se ve bien. Pero si se carga el modelo de vuelo predeterminado y el
uno para el Bell-X1 no lo es, el intérprete puede resolver el nivel superior
Por tanto, FlightModel
y la carga automática no se activan paraBellX1::FlightModel
.
Ese código depende de la ruta de ejecución.
Este tipo de ambigüedades a menudo se pueden resolver utilizando constantes calificadas:
module BellX1
class Plane
def flight_model
@flight_model ||= BellX1::FlightModel.new
end
end
end
Además, require_dependency
es una solución:
require_dependency 'bell_x1/flight_model'
module BellX1
class Plane
def flight_model
@flight_model ||= FlightModel.new
end
end
end
9.7.2 Qualified References
Este problema solo es posible en Ruby <2.5.
Dado
# app/models/hotel.rb
class Hotel
end
# app/models/image.rb
class Image
end
# app/models/hotel/image.rb
class Hotel
class Image < Image
end
end
la expresión Hotel::Image
es ambigua porque depende de la ejecución
camino.
Como vimos antes, Ruby parece
hasta la constante en Hotel
y sus antepasados. Si app/models/image.rb
tiene
se ha cargado pero app/models/hotel/image.rb
no lo ha hecho, Ruby no encuentra Image
en Hotel
, pero lo hace en Object
:
$ bin/rails runner 'Image; p Hotel::Image' 2>/dev/null
Image # NOT Hotel::Image!
El código que evalúa Hotel::Image
debe asegurarse
app/models/hotel/image.rb
se ha cargado, posiblemente con
require_dependency
.
En estos casos, el intérprete emite una advertencia:
warning: toplevel constant Image referenced by Hotel::Image
Esta sorprendente resolución constante se puede observar con cualquier clase clasificatoria:
2.1.5 :001 > String::Array
(irb):1: warning: toplevel constant Array referenced by String::Array
=> Array
Para encontrar este problema, el espacio de nombres calificado debe ser una clase,
Object
no es un antepasado de módulos.
9.8 Autoloading within Singleton Classes
Supongamos que tenemos estas definiciones de clase:
# app/models/hotel/services.rb
module Hotel
class Services
end
end
# app/models/hotel/geo_location.rb
module Hotel
class GeoLocation
class << self
Services
end
end
end
Si Hotel::Services
se conoce por el tiempo app/models/hotel/geo_location.rb
se está cargando, "Servicios" es resuelto por Ruby porque "Hotel" pertenece al
anidando cuando se abre la clase singleton de Hotel::GeoLocation
.
Pero si no se conoce Hotel::Services
, Rails no puede cargarlo automáticamente,
la aplicación genera "NameError".
La razón es que se activa la carga automática para la clase singleton, que es anónimo, y como vimos antes, Rails solo verifica espacio de nombres de nivel superior en ese caso de borde.
Una solución fácil a esta advertencia es calificar la constante:
module Hotel
class GeoLocation
class << self
Hotel::Services
end
end
end
9.9 Autoloading in BasicObject
Los descendientes directos de BasicObject
no tienen Object
entre sus antepasados
y no puede resolver constantes de nivel superior:
class C < BasicObject
String # NameError: uninitialized constant C::String
end
Cuando se trata de la carga automática, la trama tiene un giro. Consideremos:
class C < BasicObject
def user
User # WRONG
end
end
Dado que Rails comprueba el espacio de nombres de nivel superior, User
se carga automáticamente
primera vez que se invoca el método user
. Solo obtiene la excepción si el
La constante User
se conoce en ese punto, en particular en una * segunda * llamada a
user
:
c = C.new
c.user # surprisingly fine, User
c.user # NameError: uninitialized constant C::User
porque detecta que un espacio de nombres principal ya tiene la constante (consulte Qualified Referencias).
Al igual que con Ruby puro, dentro del cuerpo de un descendiente directo del uso de BasicObject
siempre caminos constantes absolutos:
class C < BasicObject
::String # RIGHT
def user
::User # RIGHT
end
end
9.10 Autoloading in the Test Environment
Al configurar el entorno de "prueba" para la carga automática, puede considerar varios factores.
Por ejemplo, podría valer la pena ejecutar sus pruebas con una configuración idéntica a la de producción (config.eager_load = true
, config.cache_classes = true
) para detectar cualquier problema antes de que llegue a producción (esto es una compensación por la falta de paridad dev-prod). Sin embargo, esto ralentizará el tiempo de arranque para las pruebas individuales en una máquina de desarrollo (y no es inmediatamente compatible con Spring, ver más abajo). Así que una posibilidad es hacer esto en un
CI solo máquina (que debería funcionar sin resorte).
En una máquina de desarrollo, puede ejecutar sus pruebas con lo que sea más rápido (idealmente config.eager_load = false
).
Con el precargador de Spring (incluido con las nuevas aplicaciones de Rails), lo ideal es mantener config.eager_load = false
según el desarrollo. A veces puede terminar con una configuración híbrida (config.eager_load = true
,config.cache_classes = true
Y config.enable_dependency_loading = true
), consulte edición de primavera. Sin embargo, podría ser más sencillo mantener la misma configuración que el desarrollo y resolver lo que sea que esté causando que falle la carga automática (tal vez por los resultados de las pruebas de CI).
De vez en cuando puede necesitar explícitamente eager_load usando Rails
.application.eager_load!
en la configuración de sus pruebas; esto podría ocurrir si sus pruebas involucran subprocesos múltiples.
10 Troubleshooting
10.1 Tracing Autoloads
Active Support is able to report constants as they are autoloaded. To enable these traces in a Rails application, put the following two lines in some initializer:
ActiveSupport::Dependencies.logger = Rails.logger
ActiveSupport::Dependencies.verbose = true
10.2 Where is a Given Autoload Triggered?
Si se está cargando automáticamente Foo
constante, y le gustaría saber de dónde viene esa carga automática, simplemente arroje
puts caller
en la parte superior de foo.rb
e inspeccione el seguimiento de la pila impresa.
10.3 Which Constants Have Been Autoloaded?
En cualquier momento dado,
ActiveSupport::Dependencies.autoloaded_constants
tiene la colección de constantes que se han cargado automáticamente hasta ahora.
Comentarios Sobre el Contenido
Las guías de rieles se administran y publican en latinadeveloper/railsguides.es en GitHub.
Si lee esta guía y encuentra algún texto o código incorrecto que le interese, no dude en enviar una solicitud de extracción en el repositorio anterior. Consulte el archivo README en GitHub para saber cómo enviar una solicitud de extracción. Please contribute if you see any typos or factual errors.