Acessando Ruby Variáveis de Classe com class_eval e instance_eval
-
26-09-2019 - |
Pergunta
Eu tenho o seguinte:
class Test
@@a = 10
def show_a()
puts "a: #{@@a}"
end
class << self
@@b = '40'
def show_b
puts "b: #{@@b}"
end
end
end
Por que o seguinte trabalho:
Test.instance_eval{show_b}
b: 40
=> nil
Mas eu não consigo acessar @@b
diretamente?
Test.instance_eval{ @@b }
NameError: uninitialized class variable @@b in Object
Da mesma forma, as seguintes obras
t = Test.new
t.instance_eval{show_a}
a: 10
=> nil
mas o seguinte falha
t.instance_eval{ @@a }
NameError: uninitialized class variable @@a in Object
Eu não entendo por que eu não consigo acessar as Variáveis de Classe, diretamente a partir do instance_eval
blocos.
Solução
Eu só pedi a mesma pergunta para Matz durante o RubyKaigi festa.Eu estava meio bêbado, mas ele estava perfeitamente sóbria, de modo que você pode tomar isso como a resposta definitiva.
Anton é certo, a razão pela qual você não é possível acessar variáveis de classe através de instance_eval() é "apenas porque".Mesmo class_eval() tem o mesmo problema (Matz si mesmo não estava totalmente certo sobre class_eval() até que eu disse a ele que eu já tentei).Mais especificamente:escopo-sábio, variáveis de classe são mais constantes do que variáveis de instância, de modo de comutação auto (como instance_eval() e class_eval() não) não vai fazer qualquer diferença quando se trata de aceder a eles.
Em geral, pode ser uma boa idéia para evitar variáveis de classe completamente.
Outras dicas
EDITAR: abaixo o código foi testado com 1.8.7 e 1.9.1...parece que a situação é diferente novamente com 1.9.2 :/
A situação realmente não é reta para a frente.Há diferenças no comportamento, dependendo se você estiver usando o 1.8 ou 1.9 e se você está usando class_eval
ou instance_eval
.
Os exemplos a seguir descrevem o comportamento na maioria das situações.
Eu também incluiu o comportamento de constantes, para uma boa medida, como o seu comportamento é semelhante, mas não exatamente o mesmo, variáveis de classe.
Variáveis de classe
class_eval
em Ruby 1.8:
class Hello
@@foo = :foo
end
Hello.class_eval { @@foo } #=> uninitialized class variable
class_eval
no Ruby 1.9:
Hello.class_eval { @@foo } #=> :foo
Assim, variáveis de classe são olhou em 1,9 (mas não em 1,8) quando usar class_eval
instance_eval
em Ruby 1.8 e 1.9
Hello.instance_eval { @@foo } #=> uninitialized class variable
Hello.new.instance_eval { @@foo } #=> uninitialized class variable
Parece variáveis de classe são não olhou em 1,8 ou 1.9 quando utilizar instance_eval
O que é interessante também é o caso para constantes:
Constantes
class_eval
em Ruby 1.8
class Hello
Foo = :foo
end
Hello.class_eval { Foo } #=> uninitialized constant
class_eval
no Ruby 1.9
Hello.class_eval { Foo } #=> :foo
Assim, como com a classe de variáveis, constantes são procurados em 1.9, mas não em 1.8 por class_eval
instance_eval
em Ruby 1.8
Hello.instance_eval { Foo } #=> uninitialized constant
Hello.new.instance_eval { Foo } #=> uninitialized constant
instance_eval
no Ruby 1.9
Hello.instance_eval { Foo } #=> uninitialized constant
Hello.new.instance_eval { Foo } #=> :foo
Parece que a constante de pesquisa não é bastante análoga à variável de classe de olhar para cima para o Ruby 1.9.Um Hello
instância não obtenha acesso a uma constante enquanto a Hello
classe não.
Bem, provavelmente, a melhor resposta é "apenas porque":o instance_eval em poucas palavras cria algum tipo de singleton proc que é invocado com a vinculação de um determinado objeto.Eu concordo que é que soa um pouco estranho, mas é o que é.
Se você executar instance_eval com uma seqüência de caracteres, você ainda vai receber um aviso de que seu método tenta acessar a variável de classe:
irb(main):038:0> Test.new.instance_eval "@@a"
(eval):1: warning: class variable access from toplevel singleton method
NameError: (eval):1:in `irb_binding': uninitialized class variable ...
Ruby 2.1
Este é o mais conciso e semanticamente correto maneira que eu encontrei para acessar uma variável de classe:
class Hello
@@foo = :foo_value
end
Hello.class_variable_get :@@foo #=> :foo_value