2012-03-12 21 views
5

Estoy tratando de establecer una variable local a un enlace existenteuna variable local a una consolidación

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

La unión no tiene ninguna de las variables locales, para empezar:

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

Let Establezcamos una variable local primitiva a la unión:

b.eval("age=30") 

todo funciona como se esperaba:

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

Ahora, vamos a tratar de establecer una variable local no primitiva a la unión:

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

Nota: Eltechnique para establecer la variable es tomado de la facet joya. Probé el ruby ​​1.9 safe implementation con los mismos resultados.

El enlace no refleja la variable local country.

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

¿Cómo puedo evitar este problema? Esencialmente, quiero declarar una nueva variable en un enlace utilizando el valor de una variable existente, no primitiva.

Estoy en Ruby 1.8.7.

+0

() es privada, por lo que la unión no se puede llamar así: 'b = foo_callback.binding' – 7stud

+0

@ 7stud Puede. Ejecute el código en la consola de ruby ​​'def foo_callback lambda {| name | p nombre} final b = foo_callback.binding' –

Respuesta

5

Está creando country fuera del enlace, y luego el country dentro del lambda solo es válido dentro de ese ámbito. ¿Por qué no eval si necesita inyectarlo también en el enlace?

actualización

Trate declarar la variable fuera del ámbito de la lambda pero dentro del alcance de la unión:

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

'eval' toma cadena como el parámetro, por lo que no puedo pasar el valor de una variable existente por referencia. El código que estoy usando es bastante similar al que se usa en la gema 'faceta': https://github.com/rubyworks/facets/blob/master/lib/core/facets/binding/op_get.rb –

+0

El comentario sobre * las facetas * que no funcionan en 1.9 no son alentadoras. Los enlaces son muy importantes en Ruby, pero siempre he descubierto que solo hay un sistema muy poco pensado para manipularlos y usarlos. He actualizado mi respuesta con una puñalada en una solución. – tadman

+0

Obtuve los mismos resultados cuando utilicé la implementación segura de ruby ​​1.9: https://github.com/rubyworks/facets/blob/master/lib/core/facets/binding/with.rb#L7 –

2

El problema aquí es que va a crear un nuevo lambda, dentro de otra lambda, y eso está creando un nuevo alcance.

Para ser más claros, el enlace, 'b', tendrá en su alcance todas las variables locales disponibles en el alcance de la función #foo_callback, y todas las variables locales disponibles en la primera lambda. La segunda lambda que creó es un nuevo ámbito y, por lo tanto, las nuevas variables locales creadas en ese ámbito no se conservarán fuera del alcance a menos que se inicialicen primero fuera del alcance interno.

Es por eso que verá mucha gente inicializando variables locales como nulas antes de ingresar a un bloque. También puede hacer esto, lo que hace la misma cosa: las variables

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

de instancia no tienen este problema alcance, y están disponibles en todo los ámbitos interno y externo de una función.

Ex:

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

debería funcionar.

Y, alguien se me adelantó la respuesta mientras estaba escribiendo esto :)

vinculante
Cuestiones relacionadas