2010-07-30 24 views
51

Sto eseguendo un codice Ruby che evalida un file Ruby ogni volta che cambia la data. Nel file, ho definizioni delle costanti, comeCome ridefinire una costante Ruby senza preavviso?

Tau = 2 * Pi 

e, naturalmente, fanno visualizzare l'interprete gli indesiderati "già inizializzato costante" avviso ogni volta, così, mi piacerebbe avere le seguenti funzioni:

def_if_not_defined(:Tau, 2 * Pi) 
redef_without_warning(:Tau, 2 * Pi) 

ho potuto evitare l'avvertimento scrivendo tutte le mie definizioni delle costanti come questo:

Tau = 2 * Pi unless defined?(Tau) 

ma è poco elegante e un po 'bagnato (non DRY).

C'è un modo migliore per def_if_not_defined? E come redef_without_warning?

-

soluzione grazie a Steve:

class Object 
    def def_if_not_defined(const, value) 
    mod = self.is_a?(Module) ? self : self.class 
    mod.const_set(const, value) unless mod.const_defined?(const) 
    end 

    def redef_without_warning(const, value) 
    mod = self.is_a?(Module) ? self : self.class 
    mod.send(:remove_const, const) if mod.const_defined?(const) 
    mod.const_set(const, value) 
    end 
end 

A = 1 
redef_without_warning :A, 2 
fail 'unit test' unless A == 2 
module M 
    B = 10 
    redef_without_warning :B, 20 
end 
fail 'unit test' unless M::B == 20 

-

Questa domanda è vecchia. Il codice sopra riportato è necessario solo per Ruby 1.8. In Ruby 1.9, la risposta di P3t3rU5 non produce alcun avvertimento ed è semplicemente migliore.

+5

Perché si desidera ridefinire una costante? È meglio mantenere le costanti dello spazio dei nomi mantenendole nelle proprie classi o moduli, in questo modo non entreranno mai in conflitto con altre costanti. –

+1

Voglio ridefinire una costante perché voglio usare le costanti in modo naturale come se non stessi usando un caricatore automatico del codice sorgente, quindi non accetterò alcuna risposta "solo non usare una costante". –

+2

Che cosa è inelegante e non SECCO su 'Tau = 2 * Pi se non definito? (Tau)'? – jrdioko

risposta

58

Il seguente modulo può fare ciò che si desidera. Se non può fornire alcune indicazioni per la soluzione

module RemovableConstants 

    def def_if_not_defined(const, value) 
    self.class.const_set(const, value) unless self.class.const_defined?(const) 
    end 

    def redef_without_warning(const, value) 
    self.class.send(:remove_const, const) if self.class.const_defined?(const) 
    self.class.const_set(const, value) 
    end 
end 

E come esempio di utilizzo

class A 
    include RemovableConstants 

    def initialize 
    def_if_not_defined("Foo", "ABC") 
    def_if_not_defined("Bar", "DEF") 
    end 

    def show_constants 
    puts "Foo is #{Foo}" 
    puts "Bar is #{Bar}" 
    end 

    def reload 
    redef_without_warning("Foo", "GHI") 
    redef_without_warning("Bar", "JKL") 
    end 

end 

a = A.new 
a.show_constants 
a.reload 
a.show_constants 

ha pronunciato la seguente uscita

Foo is ABC 
Bar is DEF 
Foo is GHI 
Bar is JKL 

Perdonami se ho rotto qualsiasi tabù rubino qui come sto ancora prendendo in giro alcuni dei moduli: Classe: struttura Eigenclass all'interno di Ruby

+0

Certo, la chiave di questa risposta è semplicemente chiamare 'Object.send (: remove_const, 'Tau') se Object.const_defined? ('Tau')', che non definisce la costante, quindi azzera l'avviso. Ottimo approccio. – ghayes

+0

Sì, o semplicemente 'send (: remove_const,: CONST) se const_defined? (: CONST)' se sei in ambito di classe (non di istanza). – thewoolleyman

4

Se si desidera ridefinire un valore, non utilizzare le costanti, utilizzare invece una variabile globale ($ tau = 2 * Pi), ma non è una buona pratica. Dovresti renderlo una variabile di istanza di una classe appropriata.

Per l'altro caso, Tau = 2 * Pi unless defined?(Tau) è perfettamente a posto e la soluzione più leggibile, quindi la più elegante.

2

A meno che i valori delle costanti sono piuttosto strano (cioè avete costanti impostate nil o false), la scelta migliore sarebbe quella di utilizzare l'operatore di assegnazione condizionale: Tau ||= 2*Pi

Questo imposterà Tau a 2p se è nil, false o non definito, e lasciarlo da solo in caso contrario.

+1

Bella idea ... Sfortunatamente, non è molto portabile: a seconda della versione e dell'implementazione del ruby ​​(ruby/jruby), l'effetto su una costante con || = mi ha dato tre risultati diversi. O funziona tranquillamente come previsto (jruby1.5), o ottengo un errore "costante non inizializzata" (ruby1.8), o ricevo un avviso anche se non si verifica alcuna incidenza (jruby1.2). –

3

Un altro approccio, utilizzando $ VERBOSE, per sopprimere gli avvertimenti, è discusso qui: http://mentalized.net/journal/2010/04/02/suppress_warnings_from_ruby/

+2

Sì. Come menzionato nel tuo link, una migliore implementazione di silence_warnings esiste in Rails: http://api.rubyonrails.org/classes/Kernel.html#M002564 Ma questo approccio è inferiore alla risposta accettata, perché probabilmente ha effetti collaterali su altri filettature. –

1

Che dire di seguito?

TAU ||= 2 * Pi 

Funziona su una gemma su cui sto lavorando.

+5

Questo non ridefinirà mai una costante, quindi non è una risposta alla domanda. Come ha ottenuto anche 8 voti? – jrochkind

+1

Bene, risponde def_if_not_defined (: Tau, 2 * Pi) e ho pensato che fosse la domanda originale, non è sicuro, è passato molto tempo fa – P3t3rU5

+1

Anche ridefinire le costanti è un abuso del linguaggio, anche se è possibile farlo , non significa sempre che dovresti farlo – P3t3rU5