2010-10-29 11 views
14

Come si può eseguire questo semplice compito in Ruby?
Ho alcune semplici file di configurazioneruby: come caricare file .rb nel contesto locale

=== config.rb 
config = { 'var' => 'val' } 

voglio caricare file di configurazione da qualche metodo, definito nel main.rb file in modo che le variabili locali da config.rb divennero Vars locali di quel metodo.
Qualcosa di simile a questo:

=== main.rb 
Class App 
    def loader 
     load('config.rb') # or smth like that 
     p config['var'] # => "val" 
    end 
end 

So che posso usare vars globali config.rb e poi ridefinisca quando fatto, ma spero che ci sia un modo rubino)

+3

È possibile farlo con eval, ma questo non è una buona idea (in qualsiasi lingua). –

+0

Esistono sandbox che consentono di eseguire codice in un ambiente controller, quindi voterò per compensare il downvote – Papipo

risposta

1

NON consiglio facendo questo tranne in un ambiente controllato.

Salvare un modulo in un file con un nome predeterminato che definisce i metodi initialize e run_it. Per questo esempio ho usato test.rb come il nome del file:

module Test 
    @@classvar = 'Hello' 
    def initialize 
    @who = 'me' 
    end 

    def get_who 
    @who 
    end 

    def run_it 
    print "#{@@classvar} #{get_who()}" 
    end 
end 

quindi scrivere una semplice applicazione per caricare ed eseguirlo:

require 'test' 

class Foo 
    include Test 
end 

END { 
    Foo.new.run_it 
} 

# >> Hello me 

Solo perché si può fare qualcosa non significa che si dovrebbe. Non riesco a pensare a una ragione per cui lo farei in produzione e lo mostro solo qui come una curiosità e una dimostrazione di concetto. Rendere questo disponibile a persone sconosciute sarebbe un buon modo per far hackerare la macchina perché il codice potrebbe fare qualsiasi cosa l'account proprietario potrebbe fare.

+0

grazie per l'approccio, ma come puoi vedere tu stesso: questa è una soluzione ridondante persino di 'stile php' ... – disfated

+1

Non sono particolarmente entusiasta di metaprogrammazione, ma php è un inferno per me. Ho pensato che uno strumento dinamico così potente come il rubino possa svolgere un compito così primitivo. Cattive notizie. Non può Grazie a tutti per la vostra attenzione! – disfated

+0

Potrebbe essere necessario cambiare la riga 'require 'test'' in' require'./Test'' – Automatico

5

Certamente potrebbe incidere una soluzione che utilizza eval e File.read, ma il fatto che sia difficile dovrebbe darti un segnale che questo non è un modo rubino per risolvere il problema che hai. Due modelli alternativi utilizzerebbero yaml per la tua config api o definiscono un semplice dsl.

Il caso YAML è il più facile, devi semplicemente avere qualcosa di simile in main.rb:

Class App 
    def loader 
     config = YAML.load('config.yml') 
     p config['var'] # => "val" 
    end 
end 

e il file di configurazione sarà simile:

--- 
var: val 
+0

Yaml non funzionerà: config.rb è solo un esempio - ci dovrebbero essere alcuni processi - non solo serializzati dati. In realtà, ho bisogno di un semplice comando "exec_file_as_it_was_typed_here (file)". btw, php può farlo) – disfated

+0

Inoltre, dsl e soprattutto eval non sono varianti. Almeno per ora. Voglio solo mantenere le cose semplici. – disfated

+0

La versione breve è che ruby ​​non può farlo, la versione più lunga è quella che dovresti definire ed eseguire una DSL piuttosto che fare affidamento su un trucco intelligente con scope. – NZKoz

11

Il file di configurazione.

{ 'var' => 'val' } 

Caricamento del file di configurazione

class App 
    def loader 
    config = eval(File.open(File.expand_path('~/config.rb')).read) 
    p config['var'] 
    end 
end 
1

ho dovuto fare una cosa simile come volevo essere in grado di caricare una "Ruby DLL" dove torna una classe anonima (una fabbrica per le istanze di cose) Ho creato questo che tiene traccia degli elementi già caricati e consente al file caricato di restituire un valore che può essere qualsiasi cosa - una Classe, Modulo, dati completamente anonimi. Potrebbe essere un modulo che potresti quindi "includere" in un oggetto dopo che è stato caricato e potrebbe fornire un host di "attributi" o metodi. potresti anche aggiungere un elemento "scarica" ​​per cancellarlo dall'hash caricato e dereferenziare qualsiasi oggetto caricato.

module LoadableModule 

@@loadedByFile_ = {}; 

def self.load(fileName) 
    fileName = File.expand_path(fileName); 
    mod = @@loadedByFile_[fileName]; 
    return mod if mod; 
    begin   
     Thread.current[:loadReturn] = nil; 
     Kernel.load(fileName); 
     mod = Thread.current[:loadReturn]; 
     @@loadedByFile_[fileName] = mod if(mod); 
    rescue => e 
     puts(e); 
     puts(e.backtrace); 
     mod = nil; 
    end 
    Thread.current[:loadReturn] = nil; 
    mod 
end 
def self.onLoaded(retVal) 
    Thread.current[:loadReturn] = retVal; 
end 
end 

all'interno del file caricato:

LoadableModule.onLoaded("a value to return from the loaded file"); 
6

Come altri hanno detto, per la configurazione è meglio usare YAML o JSON. Per eval un file

binding.eval(File.open(File.expand_path('~/config.rb')).read, "config.rb") binding.eval(File.read(File.expand_path('~/config.rb')), "config.rb")

Questa sintassi permetterebbe di vedere il nome del file in backtrace che è importante. Vedi api docs [1].

È stato aggiornato il comando eval per evitare perdite FD (file descriptor). Devo aver dormito o forse avrei dormito in quel momento della notte, invece di scrivere su StackOverflow ..

[1] http://www.ruby-doc.org/core-1.9.3/Binding.html

Problemi correlati