sintax rails escape ruby lazy-loading autoload

ruby - rails - haml to html



Clases de carga automática en Ruby sin su `autoload` (3)

Esto se planteó en un boleto de Rails hace algún tiempo y cuando lo investigué no parecía haber forma de evitarlo. El problema es que Ruby buscará los ancestros antes de llamar a const_missing y, como todas las clases tienen a Object como ancestro, siempre se encontrarán las constantes de nivel superior. Si puede limitarse a usar solo módulos para el espacio de nombres, entonces funcionará ya que no tienen un Object como antecesor, por ejemplo:

>> class A; end >> class B; end >> B::A (irb):3: warning: toplevel constant A referenced by B::A >> B.ancestors => [B, Object, Kernel, BasicObject] >> module C; end >> module D; end >> D::C NameError: uninitialized constant D::C >> D.ancestors => [D]

Me encanta la funcionalidad de carga automática de Ruby ; sin embargo, desaparecerá en futuras versiones de Ruby ya que nunca fue seguro para subprocesos.

Así que ahora mismo me gustaría fingir que ya no está y escribir mi código sin él, implementando el mecanismo de carga lenta . Me gustaría implementarlo de la manera más simple posible (no me importa la seguridad de los hilos en este momento). Ruby debería permitirnos hacer esto.

Comencemos por aumentar una clase '' const_missing :

class Dummy def self.const_missing(const) puts "const_missing(#{const.inspect})" super(const) end end

Ruby llamará a este método especial cuando intentemos hacer referencia a una constante debajo de "Dummy" que falta, por ejemplo, si intentamos hacer referencia a "Dummy :: Hello", llamará const_missing con el Símbolo :Hello . Esto es exactamente lo que necesitamos, así que vamos más lejos:

class Dummy def self.const_missing(const) if :OAuth == const require ''dummy/oauth'' const_get(const) # warning: possible endless loop! else super(const) end end end

Ahora, si hacemos referencia a "Dummy :: OAuth", requerirá el archivo "dummy / oauth.rb" que se espera que defina la constante "Dummy :: OAuth". Existe la posibilidad de un bucle sin fin cuando llamamos a const_get (ya que puede llamar a const_missing internamente), pero protegerse contra eso está fuera del alcance de esta pregunta.

El gran problema es que toda esta solución se descompone si existe un módulo llamado "OAuth" en el espacio de nombres de nivel superior. La referencia a "Dummy :: OAuth" omitirá su const_missing y simplemente devolverá "OAuth" desde el nivel superior. La mayoría de las implementaciones de Ruby también harán una advertencia sobre esto:

warning: toplevel constant OAuth referenced by Dummy::OAuth

Esto fue reportado como un problema en el año 2003, pero no pude encontrar evidencia de que el equipo central de Ruby haya estado preocupado por esto. Hoy en día, las implementaciones más populares de Ruby tienen el mismo comportamiento.

El problema es que const_missing se omite silenciosamente a favor de una constante en el espacio de nombres de nivel superior. Esto no sucedería si "Dummy :: OAuth" se declarara con la funcionalidad de autoload de Ruby. ¿Alguna idea de cómo trabajar alrededor de esto?


La carga perezosa es un patrón de diseño muy común, puede implementarlo de muchas maneras. me gusta :

class Object def bind(key, &block) @hooks ||= Hash.new{|h,k|h[k]=[]} @hooks[key.to_sym] << [self,block] end def trigger(key) @hooks[key.to_sym].each { |context,block| block.call(context) } end end

Entonces tú puedes

bind :json do require ''json'' end begin JSON.parse("[1,2]") rescue trigger :json retry end


Obtengo su problema en ree 1.8.7 (no menciona una versión específica) si uso const_get dentro de const_missing , pero no si uso :: . No me encanta usar eval , pero funciona aquí:

class Dummy def self.const_missing(const) if :OAuth == const require ''dummy/oauth'' eval "self::#{const}" else super(const) end end end module Hello end Dummy.const_get :Hello # => ::Hello Dummy::Hello # => Dummy::Hello

Me gustaría que el Module tuviera un :: método para que pudieras hacer self.send :"::", const .