Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

486
Views
En Ruby, en un método definido en la clase << self, ¿por qué no se puede acceder a una constante definida en la superclase sin self?

Estoy tratando de entender mejor los singletons de Ruby y la herencia de clases. leí por todas partes que

 def self.method_name; end`

es equivalente a

 class << self def method_name; end end

Pero si eso fuera cierto, esperaría que print_constant_fails funcionara, pero no es así. ¿Que esta pasando aqui?

 class SuperExample A_CONSTANT = "super example constant" end class SubExample < SuperExample def self.print_constant_works_1 puts A_CONSTANT end class << self def print_constant_works_2 puts self::A_CONSTANT end def print_constant_fails puts A_CONSTANT end end end
 pry(main)> SubExample.print_constant_works_1 super example constant pry(main)> SubExample.print_constant_works_2 super example constant pry(main)> SubExample.print_constant_fails NameError: uninitialized constant #<Class:SubExample>::A_CONSTANT from (pry):13:in `print_constant_fails'
over 4 years ago · Santiago Trujillo
3 answers
Answer question

0

Tiene que ver con el alcance. Cuando está dentro class << self , el alcance es diferente que cuando está dentro class Something . Por lo tanto, dentro class << self , en realidad no hay una constante llamada A_CONSTANT .

over 4 years ago · Santiago Trujillo Report

0

Se ha encontrado con un problema común de Ruby: búsqueda constante.

El concepto más importante en la búsqueda constante es Module.nesting (a diferencia de la búsqueda de métodos, donde el punto de partida principal es self ). Este método le brinda el anidamiento del módulo actual que el intérprete de Ruby usa directamente al resolver el token constante. La única forma de modificar el anidamiento es usar las palabras clave class y module y solo incluye módulos y clases para las que usó esa palabra clave:

 class A Module.nesting #=> [A] class B Module.nesting #=> [A::B, A] end end class A::B Module.nesting #=> [A::B] sic! no A end

En la metaprogramación, un módulo o clase se puede definir dinámicamente usando Class.new o Module.new ; esto no afecta el anidamiento y es una causa extremadamente común de errores (ah, también vale la pena mencionarlo: las constantes se definen en el primer módulo de Module .anidación):

 module A B = Class.new do VALUE = 1 end C = Class.new do VALUE = 2 end end A::B::VALUE #=> uninitialized constant A::B::VALUE A::VALUE #=> 2

El código anterior generará dos advertencias: una para la inicialización doble de la constante A::VALUE y una segunda para la reasignación de la constante.

Si parece "Nunca haría eso", esto también se aplica a todas las constantes definidas dentro de RSpec.describe (que internamente llama a Class.new), por lo que si define una constante dentro de sus pruebas de rspec, sin duda son globales. (a menos que haya indicado explícitamente el módulo en el que se definirá consigo self::

Ahora volvamos a tu código:

 class SubExample < SuperExample puts Module.nesting.inspect #=> [SubExample] class << self puts Module.nesting.inspect #=> [#<Class:SubExample>, SubExample] end end

Al resolver la constante, el intérprete primero itera sobre todos los módulos en Module.nesting y busca esta constante dentro de ese módulo. Entonces, si el anidamiento es [A::B, A] y buscamos la constante con el token C , el intérprete buscará primero A::B::C y luego A::C .

Sin embargo, en su ejemplo, eso fallará en ambos casos :). Luego, el intérprete comienza a buscar los ancestros del primer (y solo el primero) módulo en Module.nesting. SubrExample.singleton_class.ancestors le ofrece:

 [ #<Class:SubExample>, #<Class:SuperExample>, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject ]

Como puede ver, no hay un módulo SuperExample , solo su clase singleton, razón por la cual la búsqueda constante dentro class << self falla ( print_constant_fails ).

Los ancestros de Subclass son:

 [ SubExample, SuperExample, Object, Kernel, BasicObject ]

Tenemos SuperExample allí, por lo que el intérprete logrará encontrar SuperExample::A_CONSTANT dentro de este anidamiento.

Nos quedamos con print_constant_works_2 . Este es un método de instancia en una clase singleton, por lo que self dentro de este método es solo SubExample . Entonces, estamos buscando SubExample::A_CONSTANT : la búsqueda constante primero busca en SubExample y, cuando falla, en todos sus ancestros, incluido SuperExample .

over 4 years ago · Santiago Trujillo Report

0

En Ruby, cada constante de Ruby tiene su propia ruta, comienza desde la principal (raíz) con el signo :: (predeterminado, no necesitamos declarar este signo). Y class no debe considerarse una palabra clave (una especie de estática), sino un método (una especie de dinámica) que asume la responsabilidad de crear un class object y una class name constant de clase que apunta a ese class object , y todas las Constants se definen dentro de una clase sin automáticamente se considerará que una ruta (P::Q::...) pertenece a la clase creada con ruta :: ClassName::A_CONSTANT .

 GLOBAL = 1 class SuperExample A_CONSTANT = "super constant" # <-- ::SuperExample::A_CONSTANT end puts ::GLOBAL # 1 puts ::SuperExample # SuperExample puts ::SuperExample::A_CONSTANT # "super constant"

Parece que las rutas constantes en las clases de niños tienen el mismo nivel que los padres

 class SubExample < SuperExample end puts ::SubExample::A_CONSTANT # "super constant"

Como noté, todas las constantes (sin :: ) dentro del bloque de clase se establecerán en la ruta de acceso de clase, por lo que cuando las obtenga, obtendrá la ruta explícitamente constante o en la clase a la que pertenecen las constantes:

 class SubExample < SuperExample def self.print_constant_works_1 puts A_CONSTANT # ::SubExample::A_CONSTANT end def another puts A_CONSTANT # ::SubExample::A_CONSTANT end def yet_another puts SubExample::A_CONSTANT # ::SubExample::A_CONSTANT end end

Ahora revisa class << self

 class SubExample < SuperExample class << self puts self # <Class:SubExample> def print_constant_works_2 puts self::A_CONSTANT # declare explicitly constant path end def print_constant_fails puts A_CONSTANT # not declare explicitly <-- Class:SubExample::A_CONSTANT end end end

Como puede ver, la clase dentro class << self es diferente, por lo que la ruta de la constante A_CONSTANT dentro del método print_constant_fails apunta a Class:SubExample que no define ninguna constante A_CONSTANT , por lo que un error uninitialized constant #<Class:SubExample>::A_CONSTANT se elevará.

Mientras tanto, print_constant_works_2 funcionará ya que declaramos explícitamente una ruta constante, y self en este caso es en realidad SubExample (llame a SubExample.print_constant_works_2 ).

Ahora intentemos con una ruta explícita ::A_CONSTANT dentro print_constant_fails

 def print_constant_fails puts ::A_CONSTANT end

El error que se genera es la uninitialized constant A_CONSTANT , ::A_CONSTANT se considera una constante global (principal).

over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!