Ruby e as variáveis de classe

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 😉

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s