Ruby parece haber sufrido cambios varias veces con respecto a la accesibilidad constante. Para Ruby 1.9.2, hay una descripción sobre la accesibilidad constante en la pregunta y respuesta aquí, pero lo que está escrito allí ya no es cierto.

En Ruby 2.3, si tengo una constante definida en una clase:

class A
  Foo = :a
end

No puedo acceder a él a través de instance_eval o class_eval:

A.new.instance_eval{Foo} # => error
A.class_eval{Foo} # => error
A.instance_eval{Foo} # => error

Aunque puedo acceder a él desde el cuerpo de la clase:

class A; Foo end # => :a

¿Cómo funciona la accesibilidad constante a partir de Ruby 2.3? Si es posible, explique los cambios históricos de accesibilidad constante en Ruby y los argumentos que los llevaron.

8
sawa 26 ene. 2016 a las 05:11

2 respuestas

La mejor respuesta

La búsqueda constante en Ruby usa alcance léxico que atraviesa el alcance actual y los módulos que lo contienen. La ruta de búsqueda se puede ver a través de Module.nesting

> module Out
>   module In
>     puts Module.nesting
>   end
> end
Out::In # gets searched for a given constant first
Out 

La búsqueda sigue siendo la misma dentro de un bloque para permitir el comportamiento de cierre, incluso para class_eval.

Sin embargo, en Ruby 1.9, el receptor de instance_eval, instance_exec, class_eval y class_exec se antepusieron a la ruta de búsqueda, lo que significa que todas las referencias constantes se buscarían primero en el alcance del receptor.

Yehuda Katz planteó un problema citando roturas importantes:

Considere el caso de RSpec:

describe "Date" do   
  it "equals itself" do
    Date.today.should == Date.today   
  end 
end

Si usamos el alcance dinámico primero, entonces si RSpec agrega Spec :: Date, romperá repentinamente esta especificación. El alcance léxico es más intuitivo y lo esperan muchos usos normales en la actualidad.

Posteriormente, el comportamiento se revirtió a 1.8.

3
Community 23 may. 2017 a las 12:23

Nota: la respuesta a continuación se basa en Ruby 2.2, no se ha verificado en 2.3

Según la documentación de Module#class_eval

Evalúa la cadena o el bloque en el contexto de mod, excepto que cuando se proporciona un bloque, la búsqueda de variable constante / clase no se ve afectada.

Aunque el código siguiente tiene errores,

A.class_eval{Foo} # uninitialized constant Foo (NameError)

Los siguientes trabajos donde se está eval ed una cadena.

A.class_eval("Foo") #=> :a

También parece que Constant requiere el nombre del módulo o el nombre de la clase para que se evalúe correctamente. A continuación funciona:

A.class_eval{self::Foo} #=> :a

Entonces, ¿estos:

A.new.instance_eval {self.class::Foo} #=> :a
A.instance_eval {self::Foo} #=> :a
4
Wand Maker 26 ene. 2016 a las 13:37