Во-первых, @user
не является "закрытой переменной" в Ruby; это переменная экземпляра . Переменные экземпляра доступны в области действия текущего объекта (к чему относится self
). Я изменил название вашего вопроса, чтобы более точно отразить ваш вопрос.
Блок похож на функцию, набор кода, который будет выполнен позже. Часто этот блок будет выполняться в области действия , где был определен блок , но также возможно оценить блок в другом контексте:
class Foo
def initialize( bar )
# Save the value as an instance variable
@bar = bar
end
def unchanged1
yield if block_given? # call the block with its original scope
end
def unchanged2( &block )
block.call # another way to do it
end
def changeself( &block )
# run the block in the scope of self
self.instance_eval &block
end
end
@bar = 17
f = Foo.new( 42 )
f.unchanged1{ p @bar } #=> 17
f.unchanged2{ p @bar } #=> 17
f.changeself{ p @bar } #=> 42
Таким образом, либо вы определяете блок вне области действия, в которой установлено значение @user
, либо реализация client.request
приводит к тому, что блок будет оцениваться позже в другой области действия. Вы можете узнать, написав:
client.request("createSession"){ p [self.class,self] }
, чтобы получить представление о том, какого рода объект является текущим self
в вашем блоке.
Причина, по которой они «исчезают» в вашем случае - вместо выдачи ошибки - в том, что Ruby разрешает вам запрашивать значение любой переменной экземпляра, даже если значение никогда не было установлено для текущего объекта. Если переменная никогда не была установлена, вы просто получите обратно nil
(и предупреждение, если они у вас включены):
$ ruby -e "p @foo"
nil
$ ruby -we "p @foo"
-e:1: warning: instance variable @foo not initialized
nil
Как вы обнаружили, блоки также замыкания . Это означает, что при запуске они имеют доступ к локальным переменным, определенным в той же области видимости, что и блок. Вот почему ваш второй набор кода работал как хотелось бы. Замыкания - это отличный способ зафиксировать значение для последующего использования, например, в обратном вызове.
Продолжая пример кода выше, вы можете видеть, что локальная переменная доступна независимо от области, в которой оценивается блок, и имеет приоритет над методами с тем же именем в этой области (если вы не предоставите явный получатель):
class Foo
def x
123
end
end
x = 99
f.changeself{ p x } #=> 99
f.unchanged1{ p x } #=> 99
f.changeself{ p self.x } #=> 123
f.unchanged1{ p self.x } #=> Error: undefined method `x' for main:Object