Recentemente percebi algo muito peculiar no comportamento do Ruby quando trata-se de variáveis de classe. Sabemos que:
class Foo
@@bar = 'baz'
def self.bar
@@bar
end
end
p Foo.bar #baz
Porém, certa vez, aconteceu um caso peculiar.
class User < ActiveRecord::Base
@@default_user_locale = :en
def default_user_locale
# …
end
end
p User.first.default_user_locale
Isso, conforme o esperado, deveria retornar en, mas acredite, retornou outra coisa.
O fato
Até não ter ideia do que ocorrera, resolvi pesquisar. Encontrei pessoas afirmando que: "@@default_user_locale
era uma palavra reservada do Rails" =S
Pode ser qualquer coisa, menos isto, afinal estamos falando de uma classe!
Daí, lendo o livro Eloquent Ruby, me deparei com o tal problema. Não é culpa do Rails (ou POG do Valim), e sim, um comportamento maluco do Ruby. O dito cujo, trata variáveis de classe como variáveis globais. Logo, se você possuir uma herança, onde em ambas classes a constante for definida, o valor da última classe a ser carregada definirá o valor da variável da classe.
Em termos práticos:
foo.rb
class Foo
@@size = :small
def self.size
@@size
end
end
bar.rb
class Bar small
A solução
A sugestão para sanar este problema, de acordo com o autor do livro é utilizar variáveis de instância com métodos de acesso estáticos. Desta forma, o valor nunca será sobreescrito caso uma classe pai tenha definido a mesma variável internamente. Ficaria assim:
class Foo
@size = :small
def self.size
@size
end
end
Fica o alerta para evitar uma noite perdida 😉