2012-01-23 13 views
5

Ho un file esterno: path_to_external_file.rb con una certa definizione della classe:Caricamento di file esterni all'interno di un/modulo di classe

class A 
    some_definitions 
end 

E voglio caricare che all'interno del modulo B modo che la classe A sopra definito può essere definito come B::A. Ho provato:

class B 
    load('path_to_external_file.rb') 
end 

ma A è definito nell'ambiente principale, non in B:

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

Come posso caricare i file esterni all'interno di una certa classe/modulo?

Modifica Dovrei leggere i file esterni come stringhe, e valutarle nell'ambito Class.new{...}, e include quella classe all'interno B?

+0

a quale fine? Perché non puoi usare direttamente la classe A?Stai ottenendo qualche beneficio dalla modularizzazione? 'load' e' require' non caricheranno effettivamente una classe in un modulo, caricheranno semplicemente il codice sorgente, quindi le classi sono definite esattamente come sono nel file. Non sei sicuro del motivo per cui vorresti farlo? – brad

+1

@brad Perché questi file esterni devono essere scritti dagli utenti e possono essere nominati arbitrari. Se definisco queste classi all'interno dell'ambiente principale, rovineranno lo spazio dei nomi. – sawa

+0

Attenzione alla manomissione di altri spazi dei nomi tramite 'ObjectSpace # each_object'. – Reactormonk

risposta

4

Non è possibile. Almeno utilizzando load o require, i file di Ruby verranno sempre valutati in un contesto top.

È possibile aggirare il problema in due modi:

  • Definire class B::A direttamente (ma probabilmente si sta cercando di evitare che)
  • Usa eval(File.read("path_to_external_file.rb")) all'interno della vostra classe B

Edit : Forse questa libreria è interessante per te: https://github.com/dreamcat4/script/blob/master/intro.txt

3

Generalmente, è una cattiva idea definire una classe come "classe A" ma poi "magicamente" renderla contenuta dal modulo B. Se vuoi fare riferimento alla classe A come B :: A, devi definirla usando:

module B 
    class A 
    # contents 
    end 
end 

o:

class B::A 
    # contents 
end 

Altrimenti chiunque legga il codice sarà confuso. In questo caso, non ottieni nulla in termini di chiarezza, brevità o praticità usando "trucchi", quindi il codice semplice è migliore. C'è una lezione qui: le funzionalità di metaprogrammazione di Ruby sono grandiose, ma non c'è bisogno di usarle gratuitamente. Usali solo quando ottieni davvero qualcosa dal farlo. Altrimenti basta rendere il tuo codice difficile da capire.

MA, dopo aver letto il tuo commento, sembra che ci sia davvero una buona ragione per fare qualcosa del genere nel tuo caso. Suggerisco che la seguente soluzione sarebbe anche migliore di quello che state immaginando:

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

Vedete? Se si desidera uno spazio dei nomi "garantito unico", è possibile utilizzare un modulo anonimo. È possibile archiviare questi moduli in un hash o in un'altra struttura di dati e estrarre le classi da esse se necessario. Questo risolve il problema che hai menzionato, che gli utenti della tua app aggiungeranno le loro classi e non vuoi che i nomi entrino in collisione.

Problemi correlati