2014-11-08 15 views
5
class Foo 
    def self.run(n,code) 
    foo = self.new(n) 
    @env = foo.instance_eval{ binding } 
    @env.eval(code) 
    end 
    def initialize(n) 
    @n = n 
    end 
end 

Foo.run(42, "p @n, defined? foo") 
#=> 42 
#=> "local-variable" 

Il programma di esempio riportato sopra intende valutare codice arbitrario nell'ambito di un'istanza Foo. Lo fa, ma il binding è "inquinato" con le variabili locali dal metodo code. Non voglio che foo, n o code siano visibili al codice eval'd. L'uscita desiderata è:Crea associazione vuota nell'ambito di un oggetto

#=> 42 
#=> nil 

Come si crea un legame che è (a) nell'ambito dell'istanza dell'oggetto, ma (b) privo di variabili locali?


La ragione per cui sto creando un legame invece di utilizzare instance_eval(code) è che nel real usage ho bisogno di keep the binding around for later usage, per preservare le variabili locali creati in esso.

risposta

4

così simile? o mi sono perso qualcosa di importante qui?

class Foo 
    attr_reader :b 

    def initialize(n) 
    @n = n 
    @b = binding 
    end 

    def self.run(n, code) 
    foo = self.new(n) 
    foo.b.eval(code) 
    end 
end 

Foo.run(42, "p @n, defined?(foo)") 
# 42 
# nil 

o spostarlo più in basso per avere ancora meno contesto

class Foo 
    def initialize(n) 
    @n = n 
    end 

    def b 
    @b ||= binding 
    end 

    def self.run(n, code) 
    foo = self.new(n) 
    foo.b.eval(code) 
    end 
end 

Foo.run(42, "p @n, defined?(foo), defined?(n)") 
# 42 
# nil 
# nil 
+0

Hai rimosso alcune delle variabili locali, ma non tutte: 'defined? (N)' è '" local-variable "' a causa di dove hai chiamato 'binding'. È più pulito, ma non è vuoto. – Phrogz

+0

la versione aggiornata è abbastanza bianca? – phoet

+0

Sì, la versione aggiornata funziona. Grazie! Notare, tuttavia, che crea un nuovo bind vuoto ogni volta che viene chiamato 'b'. Se questo è quello che vuoi, la risposta è perfetta. Se invece, come me, vuoi mantenere lo stesso legame per accumulare le variabili locali da ogni corsa, dovresti fare qualcosa come "def b; @b || = vincolante; FINE'.Ti darò l'accettazione perché in qualche modo utilizzo l'istanza in quanto l'archiviazione è più elegante di un oggetto separato (come nella mia risposta); in altri modi questo forza una nuova variabile di istanza sul tuo oggetto che potresti non desiderare di avere. – Phrogz

1

Risposta:

module BlankBinding 
    def self.for(object) 
    @object = object 
    create 
    end 
    def self.create 
    @object.instance_eval{ binding } 
    end 
end 

Descrizione:

al fine di ottenere un legame con nessuna variazione locale bles, devi chiamare binding in uno scope senza nessuno di essi. Chiamare un metodo reimposta le variabili locali, quindi dobbiamo farlo. Tuttavia, se facciamo qualcosa di simile:

def blank_binding_for(obj) 
    obj.instance_eval{ binding } 
end 

... il conseguente legame avrà una variabile locale obj. È possibile nascondere questo fatto in questo modo:

def blank_binding_for(_) 
    _.instance_eval{ binding }.tap{ |b| b.eval("_=nil") } 
end 

... ma questo rimuove solo il valore della variabile locale. (Al momento non è disponibile il metodo remove_local_variable in Ruby.) Questo è sufficiente se si intende utilizzare l'associazione in un luogo come IRB o ripl in cui la variabile _ viene impostata dopo ogni valutazione e quindi verrà eseguita sull'ombra.

Tuttavia, come mostrato nella risposta in alto, c'è un altro modo per passare un valore a un metodo, e cioè attraverso una variabile di istanza (o variabile di classe o variabile globale). Dato che stiamo usando instance_eval per spostare il self nel nostro oggetto, tutte le variabili di istanza che creiamo per invocare il metodo non saranno disponibili nell'associazione.

Problemi correlati