Tengo un módulo simple que define una constante y la hace privada:
module Foo Bar = "Bar" private_constant :Bar endPuedo incluirlo en una clase como esta, y funciona como se esperaba:
class User include Foo def self.test Bar end end puts User.test # => Bar begin User::Bar rescue => exception puts "#{exception} as expected" # => private constant Foo::Bar referenced as expected end(Llamémoslo definición de "clase típica")
Luego probé el enfoque Class.new , pero falló miserablemente:
X = Class.new do include Foo def self.test Bar # Line 28 pointed in the stack trace end end begin X::Bar rescue => exception puts "#{exception}" # => private constant Foo::Bar end puts X.test # test.rb:28:in `test': uninitialized constant Bar (NameError) # from test.rb:28:in `<main>' ¿Por qué? Siempre pensé que class Something y Something = Class.new son equivalentes. ¿Cuál es la diferencia real?
Luego tuve un golpe de inspiración y recordé que hay una forma alternativa de definir los métodos de clase, que realmente funcionó:
X = Class.new do class << self include Foo def test Bar end end end begin X::Bar rescue => exception puts "#{exception}" # => uninitialized constant X::Bar end puts X.test # Bar Nuevamente, ¿por qué funciona este y por qué la excepción ahora es diferente: private constant Foo::Bar frente a la uninitialized constant X::Bar ?
Parece que esas 3 formas de inicializar clases difieren de una manera matizada.
¿Qué está pasando exactamente aquí?
Esta es una de las mayores trampas en Ruby: el alcance de la definición constante es parcialmente sintáctico , es decir, depende de cómo esté estructurado el código que lo rodea.
module Foo Bar = "Bar" end Bar está dentro de una definición de module , por lo que se define en ese módulo.
class << self include Foo end Bar se incluye dentro de una definición de class , por lo que se define en esa clase.
Class.new do include Foo end No hay una class o module adjunto (esta es una llamada de método normal con un bloque), por lo que la constante se define en el nivel superior.
En cuanto a su tercer error, creo que se debe a que la constante se definió en la clase singleton (eso es lo que es la class << self ) frente a la clase misma. Son dos objetos de clase separados.