2012-03-12 2 views
5

Я пытаюсь установить локальную переменную для существующего связыванияИнъекционное локальную переменную для привязки

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

Связывание не имеет каких-либо локальных переменных, начинающиеся с:

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

Пусть поставим примитивную локальную переменную для связывания:

b.eval("age=30") 

Все работает, как ожидалось:

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

Теперь давайте попробуем установить непримитивный локальную переменную для связывания:

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

Примечание:technique для установки переменной заимствовано из facet камень. Я попробовал ruby ​​1.9 safe implementation с такими же результатами.

Связывание не отражает местную переменную country.

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

Как мне обойти эту проблему? По сути, я хочу объявить новую переменную в привязке, используя значение существующей, не примитивной переменной.

Я на рубине 1.8.7.

+0

связывания() является частным, поэтому никогда связывание не может называться так: 'б = foo_callback.binding' – 7stud

+0

@ 7stud Вы можете. Запустите код в рубиновой консоли 'def foo_callback lambda {| name | p name} end b = foo_callback.binding' –

ответ

5

Вы создаете country вне привязки, а затем country внутри lambda действителен только в пределах этой области. Почему бы не eval его, если вам нужно также вставить это в привязку?

Update

Попробуйте объявить переменную вне сферы действия lambda, но внутри сферы связывания:

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

'eval' принимает строку как параметр, поэтому я не могу передать значение существующей переменной по ссылке. Код, который я использую, очень похож на тот, который используется в драгоценном камне 'facet': https://github.com/rubyworks/facets/blob/master/lib/core/facets/binding/op_get.rb –

+0

Замечание о * facets * не работает в версии 1.9 не обнадеживает. Привязки очень важны в Ruby, но я всегда обнаружил, что есть только очень продуманная система для их манипулирования и использования. Я обновил свой ответ с грубым ударом по решению. – tadman

+0

Я получил те же результаты, когда использовал безопасную реализацию ruby ​​1.9: https://github.com/rubyworks/facets/blob/master/lib/core/facets/binding/with.rb#L7 –

2

Проблема здесь состоит в том, что вы создаете новый лямбда, в другой лямбда, и это создает новую область.

Чтобы быть более понятным, привязка «b» будет иметь в своей области все локальные переменные, доступные в области функции #foo_callback, и все локальные переменные, доступные в первой лямбда. Вторая лямбда, которую вы создали, является новой областью видимости, поэтому новые локальные переменные, созданные в этой области, не сохраняются за пределами области действия, если только они не инициализируются за пределами внутренней области.

Именно поэтому вы увидите, что многие люди инициализируют локальные переменные как ноль перед входом в блок. Вы также можете сделать это, который делает то же самое:

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

Переменные не имеют этой проблемы области видимости, и доступны по всей функции во внутренней и внешней областях.

Ex:

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

должен работать.

И кто-то бил меня к ответу, пока я писал этот :)

Смежные вопросы