Mais uma sexta-feira e mais um artigo da série Rails Way, onde vou pouco a pouco mostrando a forma como os Rubistas mais experientes costumam programar no Ruby on Rails.
Programadores iniciantes rapidamente ficam fascinados com o Ruby e principalmente com o Rails, e por serem uma linguagem e framework com uma curva de aprendizado muito curta é comum que ao terminarem seu primeiro projeto em Rails, percam um pouco o interesse em entender como determinadas coisas funcionam, assimilando tudo que o Rails faz como pura mágica.
Isto é extremamente prejudicial ao programador iniciante e pode levá-lo a se tornar um programador “experiente” com pouco ou nenhum conhecimento real sobre o funcionamento interno da sua ferramenta de trabalho.
A ideia desta série de artigos é expor algumas boas práticas de desenvolvimento ao mesmo tempo em que tento explicar de uma forma simples como as engrenagens funcionam. Por exemplo, já se perguntou como um controller e uma view compartilham as mesmas variáveis de instância?
Por definição uma variável de instância só existe dentro de uma única instância de um objeto. Mas quando estamos falando de controllers e view, estamos falando de dois objetos diferentes que compartilham as mesmas variáveis.
# books_controller.rb
end
<!-- index.html.erb -->
Nos exemplos acima estou definindo uma variável de instância @books dentro do controller, e eu simplesmente assumo que a variável também estará disponível na view.
Note que meu controller é uma extensão da classe ApplicationController que por sua vez é uma extensão da classe ActionController::Base e por isto é uma instância desta classe. Por outro lado a minha view é uma instância da classe ActionView::Base. Ou seja, meu controller é uma instância de um objeto enquanto minha view é uma instância de um outro objeto totalmente diferente.
Então como é possível compartilhar uma variável de instância quando temos duas instância de objetos diferentes? Será mágica? Não, a resposta é mais simples do que você imagina.
O Rails acrescenta à classe Object um método chamado copy_instance_variables_from que faz exatamente o que o nome diz, copia variáveis de instância de uma classe para outro. Você pode até mesmo brincar um pouco com este método no console. Abra o terminal e após digitar o comando irb, copie e cole o código abaixo e veja o resultado:
attr_reader :x, :y
end
@x = 'valor copiado da classe B'
@y = 11
end
end
a = A.new
b = B.new
puts a.x # => nil
puts a.y # => nil
a.copy_instance_variables_from(b)
puts a.x # => "valor copiado da classe B"
puts a.y # => 11
Acabamos de copiar todas as variáveis de instância de um objeto para outro. É exatamente assim que acontece com controllers e views.
O que acontece por debaixo dos panos é que quando o método render do controller é executado, primeiro ele copia todas as variáveis de instância que você criou para dentro de apenas uma variável chamada @assigns, que funciona como um Hash. E ao criar uma uma nova view, ele faz uma copia desta variável (@assigns com todas as suas variáveis dentro) para o novo objeto. Simples não?
O inferno das variáveis
O que tenho visto muitas vezes é que alguns programadores tem abusado desta facilidade que o Rails oferece, criando inúmeras variáveis de instância no controller para recuperar todo tipo de informação na view.
Manter muitas variáveis complica o gerenciamento do código, e em alguns casos acabamos até mesmo fazendo chamadas repetidas à associações de um modelo sem se dar conta disso, causando um problema de performance em nosso projeto.
O ideal é que você compartilhe apenas uma variável de instância entre seu controller e sua view. Desta forma todas as chamadas para associações serão realizadas “on demand” evitando o consumo indevido de recursos e tornando a manutenção do código mais simples.

Isto não quer dizer que você não possa compartilhar outras variáveis quando necessário. Mas é importante que você analise a real necessidade de se fazer isto. Por exemplo, é mais prático criar um helper current_user para recuperar o usuário atual do que sempre armazená-lo em uma variável @current_user.
Se no primeiro exemplo você precisasse recuperar também todos os livros relacionados ao livro atual, talvez parecesse razoável criar uma segunda variável @related_books contendo estes livros. Mas se você colocar em prática a primeira dica desta série e acrescentar o método related_books ao modelo Book, você poderá usar @book.related_books na sua view. Evitando a criação desnecessária de mais uma variável e deixando seu código mais limpo e organizado.
Usem de bom senso.