2012-03-12 12 views
5

Sto cercando di impostare una variabile locale a una esistente vincolanteIniettando una variabile locale ad un legame

def foo_callback 
    lambda { |name| p name } 
end 
b = foo_callback.binding 

Il legame non ha variabili locali per cominciare:

b.eval("local_variables") # => [] 

Let a impostare una variabile locale primitiva alla rilegatura:

b.eval("age=30") 

tutto funziona come previsto:

b.eval("local_variables") # => ["age"] 
b.eval("age") # => 30 

Ora, cerchiamo di impostare una variabile locale non primitiva alla rilegatura:

country = Country.first 
b.eval("lambda {|v| country = v}").call(country) 

Nota: Il technique per impostare la variabile è preso in prestito dal facet gemma. Ho provato il rubino 1.9 sicuro implementation con gli stessi risultati.

L'associazione non riflette la variabile locale country.

b.eval("local_variables") # => ["age"] 

Come risolvere questo problema? In sostanza, voglio dichiarare una nuova variabile in un'associazione usando il valore di una variabile esistente, non primitiva.

Sono su Ruby 1.8.7.

+0

vincolante() è privata, quindi legame può mai essere chiamato in questo modo: 'b = foo_callback.binding' – 7stud

+0

@ 7stud È possibile. Esegui il codice nella console ruby ​​'def foo_callback lambda {| name | p nome} fine b = foo_callback.binding' –

risposta

5

Si sta creando country al di fuori della rilegatura, quindi lo country all'interno di lambda è valido solo all'interno di tale ambito. Perché non lo è eval se è necessario inserirlo anche nell'associazione?

Aggiornamento

Prova dichiarare una variabile al di fuori del campo di applicazione della lambda ma all'interno del campo di applicazione della rilegatura:

b.eval("country = nil; lambda {|v| country = v}").call(country) 
+0

'eval' prende la stringa come parametro, quindi non posso passare il valore di una variabile esistente per riferimento. Il codice che sto usando è abbastanza simile a quello usato nella gemma 'facet': https://github.com/rubyworks/facets/blob/master/lib/core/facets/binding/op_get.rb –

+0

Il commento su * facet * non funziona in 1.9 non è incoraggiante. I binding sono molto importanti in Ruby ma ho sempre trovato che c'è solo un sistema molto mal concepito per manipolarli e usarli. Ho aggiornato la mia risposta con una dura pugnalata a una soluzione. – tadman

+0

Ho ottenuto gli stessi risultati quando ho utilizzato l'implementazione sicura di Ruby 1.9: https://github.com/rubyworks/facets/blob/master/lib/core/facets/binding/with.rb#L7 –

2

Il problema qui è che si sta creando un nuovo lambda, all'interno di un altro lambda, e questo sta creando un nuovo ambito.

Per essere più chiari, il bind, 'b', avrà nel suo ambito tutte le variabili locali disponibili nell'ambito della funzione #foo_callback e tutte le variabili locali disponibili nel primo lambda. Il secondo lambda che hai creato è un nuovo scope, e quindi le nuove variabili locali create in quell'ambito non sono persistenti al di fuori dell'ambito a meno che non siano inizializzate prima dell'ambito interno.

Ecco perché vedrete molte persone inizializzare le variabili locali come nul prima di entrare in un blocco. È anche possibile fare questo, che fa la stessa cosa: le variabili

country = country 
{...block that sets country to something non-nil...} 
return country 

istanza non hanno questo problema campo di applicazione, e sono disponibili in tutta ambiti interni ed esterni di una funzione.

Es:

b.eval("lambda {|c| @country = c}").call(country) 
b.eval "instance_variables" 

dovrebbe funzionare.

E, qualcuno mi ha battuto alla risposta mentre stavo scrivendo questo :)

Problemi correlati