2012-01-23 11 views
5

Tengo un archivo externo: path_to_external_file.rb con un poco de definición de clase:Carga de archivos externos dentro de una clase/módulo

class A 
    some_definitions 
end 

Y quiero cargar que dentro del módulo B para que la clase A definido anteriormente se puede denominar como B::A. Probé:

class B 
    load('path_to_external_file.rb') 
end 

pero A se define en el entorno principal, no en B:

A #=> A 
B.constants # => [] 

¿Cómo puedo cargar archivos externos dentro de alguna clase/módulo?

Editar ¿Debo leer los archivos externos como cadenas, y evaluarlos dentro Class.new{...}, y que include clase dentro B?

+0

¿para qué fin? ¿Por qué no puedes usar la clase A directamente?¿Está obteniendo algún beneficio de modularizarlo? 'load' y' require' en realidad no cargarán una clase en un módulo, solo cargarán el código fuente, por lo que sus clases se definirán exactamente como están en el archivo. ¿No estás seguro de por qué quieres hacer esto? – brad

+1

@brad Debido a que estos archivos externos deben ser escritos por los usuarios, y pueden llamarse arbitrarios. Si defino esas clases dentro del entorno principal, arruinarán el espacio de nombres. – sawa

+0

Tenga cuidado con la alteración de otros espacios de nombres a través de 'ObjectSpace # each_object'. – Reactormonk

Respuesta

4

No puede. Al menos usando load o require, los archivos de Ruby siempre se evaluarán en un contexto superior.

Puede solucionar este problema de dos maneras:

  • Definir class B::A directamente (pero es probable que esté tratando de evitar eso)
  • Uso eval(File.read("path_to_external_file.rb")) dentro de su clase B

Editar : Quizás, esta biblioteca es interesante para usted: https://github.com/dreamcat4/script/blob/master/intro.txt

3

Generalmente, es una mala idea definir una clase como "clase A" pero luego "mágicamente" hacer que esté contenida en el módulo B. Si desea referirse a la clase A como B :: A, debe definirlo usando cualquiera de los siguientes:

module B 
    class A 
    # contents 
    end 
end 

o:

class B::A 
    # contents 
end 

De lo contrario cualquiera que lea el código será confundido. En este caso, no gana nada en claridad, brevedad o conveniencia mediante el uso de "trucos", por lo que el código directo es mejor. Aquí hay una lección: las funciones de metaprogramación de Ruby son geniales, pero no hay necesidad de usarlas gratuitamente. Solo úselas cuando realmente gane algo al hacerlo. De lo contrario, solo hace que su código sea difícil de entender.

PERO, después de leer su comentario, parece que hay realmente una buena razón para hacer algo como esto en su caso. Sugiero que la siguiente solución sería incluso mejor de lo que está visualizando:

m = Module.new 
m.module_eval("class C; end") 
m.constants 
=> [:C] 
m.const_get(:C) 
=> #<Module:0xfd0da0>::C 

¿Lo ve? Si desea un espacio de nombre "garantizado único", puede usar un módulo anónimo. Puede almacenar estos módulos en hash u otra estructura de datos, y extraer las clases de ellos según sea necesario. Esto resuelve el problema que mencionaste, que los usuarios de tu aplicación agregarán sus propias clases y no quieres que los nombres colisionen.