2010-10-29 29 views
14

Comment cette tâche simple peut-elle être effectuée dans Ruby?
J'ai quelques fichier de configuration simple,ruby: comment charger le fichier .rb dans le contexte local

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

Je veux charger le fichier config à partir d'une méthode, définie dans le fichier main.rb afin que les variables locales de config.rb sont devenues vars locales de cette méthode.
Quelque chose comme ceci:

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

Je sais que je peux utiliser vars globales dans config.rb puis les annuler la définition lorsque vous avez terminé, mais je l'espère, il y a un moyen de rubis)

+3

Vous pouvez le faire avec eval, mais ce n'est pas une bonne idée (dans n'importe quelle langue). –

+0

Il y a des sandboxs qui vous permettent d'exécuter du code dans un environnement de contrôleur, donc je vais voter pour compenser votre downvote – Papipo

Répondre

1

Je ne recommande pas ce faisant, sauf dans un environnement contrôlé. Enregistrez un module dans un fichier avec un nom prédéterminé qui définit les méthodes initialize et run_it. Pour cet exemple, je test.rb comme le nom du fichier:

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

    def get_who 
    @who 
    end 

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

Puis écrire une application simple pour charger et exécuter:

require 'test' 

class Foo 
    include Test 
end 

END { 
    Foo.new.run_it 
} 

# >> Hello me 

Juste parce que vous pouvez faire quelque chose ne signifie pas que vous devriez. Je ne peux pas penser à une raison pour laquelle je le ferais en production et ne le montrerais ici que comme une curiosité et une preuve de concept. Le rendre accessible à des personnes inconnues serait un bon moyen de pirater votre machine car le code pourrait faire tout ce que le compte propre pourrait faire.

+0

merci pour l'approche, mais comme vous pouvez vous voir - c'est une solution plus redondante que même 'style php' ... – disfated

+1

Je ne suis pas particulièrement enthousiaste à propos de la métaprogrammation, mais php est un enfer pour moi. Je pensais que l'outil dynamique si puissant comme ruby ​​peut faire une telle tâche primitive. Mauvaises nouvelles. Ça ne peut pas. Merci à tous pour votre attention! – disfated

+0

Vous devrez peut-être changer la ligne 'require 'test'' à' require'./Test'' – Automatico

5

Vous pouvez certainement pirater une solution à l'aide eval et File.read, mais le fait que ce soit difficile devrait vous donner un signal que ce n'est pas un moyen rubis pour résoudre le problème que vous avez. Deux conceptions alternatives utiliseraient yaml pour votre API de configuration, ou définir un simple DSL.

Le cas YAML est le plus facile, vous auriez simplement quelque chose comme ceci dans main.rb:

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

et votre fichier de configuration ressemblerait à ceci:

--- 
var: val 
+0

Yaml ne fonctionnera pas: config.rb est juste un exemple - il devrait y avoir quelques processus - pas seulement sérialisé Les données. En fait, j'ai besoin d'une simple commande "exec_file_as_it_was_typed_here (fichier)". btw, php peut le faire) – disfated

+0

Aussi, dsl et surtout eval ne sont pas des variantes. Au moins pour l'instant. Je veux juste garder les choses simples. – disfated

+0

La version courte est que ruby ​​ne peut pas faire cela, la version plus longue est que vous devriez définir et exécuter un DSL plutôt que de compter sur un hack intelligent avec la portée. – NZKoz

11

Le fichier de configuration.

{ 'var' => 'val' } 

Chargement du fichier de configuration

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

Je devais faire quelque chose de semblable que je voulais être en mesure de charger une « DLL Ruby » où elle retourne une classe anonyme (une usine pour les instances des choses) J'ai créé ceci qui garde la trace des éléments déjà chargés et permet au fichier chargé de renvoyer une valeur qui peut être quelque chose - une classe, un module, des données totalement anonymes, etc. Il pourrait s'agir d'un module un objet après qu'il soit chargé et il pourrait fournir une foule d'attributs ou de méthodes. vous pouvez également ajouter un élément "décharger" pour le supprimer du hachage chargé et déréférencer tout objet chargé.

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 

dans le fichier chargé:

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

Comme d'autres ont dit, pour la configuration, il est préférable d'utiliser YAML ou JSON. Pour eval un fichier

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

Cette syntaxe vous permettra de voir le nom de fichier dans backtraces ce qui est important. Voir les documents api [1].

Mise à jour eval commande pour éviter les fuites FD (descripteur de fichier). Je devais avoir dormir ou devrais peut-être avoir dormi à ce moment-là de la nuit au lieu d'écrire sur stackoverflow ..

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