Estaba mirando la regla de Rubocop para las notaciones de especificación de módulo/clase, y existen estas dos opciones:
# Style/ClassAndModuleChildren: EnforcedStyle: compact module M::C # Style/ClassAndModuleChildren: EnforcedStyle: nested module M class CLos siguientes dos archivos, cuando se ejecutan, muestran que los dos no tienen un comportamiento idéntico (consulte el final de cada archivo para ver los resultados de salida):
# nested.rb # Style/ClassAndModuleChildren: EnforcedStyle: nested module OuterModule def foo; puts 'foo'; end end module OuterModule module InnerModule def hello puts 'hello' end end end module OuterModule class C include InnerModule end end # Does not produce any error when run # compact.rb # Style/ClassAndModuleChildren: EnforcedStyle: compact module OuterModule def foo; puts 'foo'; end end module OuterModule module InnerModule def hello puts 'hello' end end end class OuterModule::C include InnerModule end =begin ❯ ruby compact.rb Traceback (most recent call last): 1: from compact.rb:15:in `<main>' compact.rb:16:in `<class:C>': uninitialized constant OuterModule::C::InnerModule (NameError) Did you mean? OuterModule::InnerModule end¿Por qué es esto?
Es debido a un proceso llamado constan lookup . Cuando Ruby encuentra un token constante como InnerModule , necesita encontrar su valor asignado. Para ello, comprueba el "anidamiento" actual con Module.nesting .
nesting devuelve una matriz de módulos en los que estamos anidados actualmente, pero solo se puede modificar con las palabras clave class y module (y no tiene absolutamente nada que ver con self , lo que puede ser un poco confuso). Cada palabra clave antepone el módulo que estamos abriendo (y solo ese módulo) al anidamiento:
Module.nesting #=> [] module A class B Module.nesting #=> [A::B, A] end end module A::C Module.nesting #=> [A::C] sic! no A end module A D = Class.new do Module.nesting #=> [A] sic! no module/class keyword = no nesting end end Entonces, al buscar cualquier constante, Ruby las buscará en los "espacios de nombres" de Module.nesting y sus módulos incluidos (y, si no se encuentra nada, en Object , que es el espacio de nombres superior de Ruby).
Esta es la razón por
class OuterModule::C include InnerModule end No funciona. Ruby solo busca la constante InnerModule en OuterModule::C y el espacio de nombres global.
También es bueno saber que las nuevas constantes siempre se definen en el primer espacio de nombres de Module.nesting , no self . Este es un problema bastante común que veo en las pruebas RSpec:
RSpec.describe SomeClass do EXAMPLES = [1,2,3,4,5] ... end Aunque Rspec.describe crea una nueva clase en la que se evalúa el bloque dado, no hay una palabra clave module/class lo que significa que todas las constantes definidas dentro de ese bloque son globales . Entonces, si alguna vez encontró una gran dosis de "la definición constante / anterior ya inicializada estaba aquí" al ejecutar una prueba, lo más probable es que se deba a eso.