In the last weekend, I attended to DevDay 2015, in Belo Horizonte, where I presented a talk about "evolution of a distributed architecture" (in portuguese).

The last talk of the event presented Stack Overflow architecture. This talk was very controversial, and I felt I needed to write something to show my point of view on this subject.

A lot is being said in the community about the value of software engineering best practices. For the last decades, people like Uncle Bob, Kent Beck, Martin Fowler and many others have been doing a great effort promoting these practices, through books, posts and talks. These professionals made and still make a great job orienting new developers, so we can have well tested software projects and well designed architectures, focusing on aspects like maintainability, scalability, security and quality.

Despite this, we know that, in many companies, management makes pressure on developers to deliver as fast as possible, forgetting about software quality and maintainability - even knowing they will be responsible for the project maintenance, at least in short term. This habit goes against every best practice described above, and is very harmful to new developers. These ones that start their career in companies with this idea in mind, even if they've read about best practices, end up believing that this is utopian, and that in practice it's impossible to design a sustainable architecture, refactor legacy code or automate tests, because of pressures for fast delivery of new features. That's why it's essential to spread the word about best practices, to show these new developers that it's not only possible to focus on quality, but also essential for the project evolution and maintenance.

In the talk that closed DevDay 2015, Stack Overflow architecture was presented. The displayed numbers are impressive: it's one of the 50 top sites in the world, with millions of page views per day, and all that supported by only 9 physical servers, each one working at around 5% load, plus 2 database servers. With this structure, the average load time is only 18 ms. How is this possible?

The secret, according to this talk, is their obsession with performance. Every new feature must have the best possible performance. When a library or tool they use isn't considered fast enough, they rewrite it from scratch. As layered architectures are slow, every database query is manually written, and directly in the controller.

Stack Overflow code has low testability, because you can't create mocks to replace the database connection, for instance. That's why the project has very few automated tests (and it was mentioned that some developers don't even run the tests). As they have a massive number of active and engaged users, any bug that's introduced after a deploy is quickly found and reported at Meta Stack Overflow. To update the operating system version, they just remove a server from the pool, apply the update and put the server back live. They assume that, if a new bug arises, users will soon find and report it.

The speaker let it very clear that modeling, architecture and tests are good stuff, but they're not for everyone. I personally disagree.

Stack Overflow is a very particular case. As their audience is made of developers, and they're very engaged and passionate about the product, bugs in production are considered acceptable, because the main focus is performance. But what's the point of performance without quality? Would you buy the fastest car in the world even knowing it doesn't have seat belts and air bags, and that it doesn't support replacing a flat tire or a defective part? The analogy is very exaggerated - a bug in the site doesn't involve life risk, but what I mean is, if you focus only and exclusively on performance, you give up other aspects like quality and security. It's the same line of thought from those managers I mentioned before, who make a lot of pressure for fast delivery, regardless of quality.

Fast food
Does every fast food need to be like this?

In my opinion, even if your project focuses on performance, quality can't be abandoned. The other extreme - over-engineering - is also bad; if you have a small application, with only a couple of users, it doesn't make sense to create a complex architecture, thinking about the possibility of maybe one day it may expand. This would be creating a solution for a problem that doesn't exist.

I have a real example for this: I register all my expenses in Google Spreadsheet. I wanted to share these data with my wife, but as the spreadsheet is very large, I created a small app that extracts these data and displays a very simple dashboard, only with information that she is interested in. This app has only two users - me and her -, and there isn't a chance that this grows up. In this case, it doesn't make sense for me to think about a scalable architecture. But when we talk about a product with tens of millions of users, the situation is very different. Currently, Stack Overflow doesn't have a competitor to match, but if one rises with a better user experience or new features, they will have a hard time to follow.

My main concern while watching this talk was the impact that these ideas can have on the audience. The majority of them were very young, probably students or professionals starting their career. A talk like this, spreading the word about optional software quality, can be very harmful to them. And it seems like Facebook has a similar issue with code quality.

To wrap up, I want to make it clear that I'm not doing a personal attack against Stack Overflow or the speaker. I admire her courage to take the stage and present such controversial ideas, even though I disagree on them. I'm a Stack Overflow user and will continue being after all.

After a few years maintaining this blog, I decided to start writing in english. I feel like I can reach a wider audience with this. Sometimes when I make a comment in a blog post or github issue, or answer a question in Stack Overflow, I want to link to something I wrote in my blog, but as I wrote in portuguese, I can't do that.

I don't think this is going to be a problem for most developers - at least I hope so; if you are a software developer and don't understand english, you should!

Eventually I may write something in portuguese again, if I have a reason for that, so I tagged every old post with portuguese. And every post in english will be tagged with english. I also added direct links in the sidebar, for easy access.

O Jasmine é uma das ferramentas de teste para Javascript mais usadas atualmente. A sintaxe estilo BDD lembra bastante o RSpec, o que facilita a vida de quem já tem experiência com este.

Uma das dificuldades ao realizar testes de Javascript é como simular a passagem do tempo. Existem duas situações básicas onde isso acontece:

  1. Quando o código executa alguma animação, como fade in e slide down, por exemplo
  2. Quando definimos uma função que só será executada após um período de tempo determinado

O primeiro caso pode ser ilustrado com este exemplo básico:

  <button id="button">Show Menu</button>
  <div id="menu" style="display: none">Menu</div>

    function example() {
      $("#button").click(function() {


Um clique no botão faz com que o menu apareça usando a função jQuery.fadeIn. O teste para este código, a princípio, poderia ser algo assim:

describe("example test", function() {
  beforeEach(function() {

  it("shows the menu after clicking the button", function() {


O problema é que, como a animação do fade in leva um pequeno período de tempo para executar (400 ms por padrão), o menu ainda não está visível no momento em que a expectativa é executada. Uma solução inocente, mas pouco eficiente, para este problema seria executar a expectativa num setTimeout.

Neste caso específico, como a animação é feita usando jQuery, há uma propriedade que permite desabilitar todas as animações. Desta forma, todas as transições são feitas instantaneamente, fazendo com que o teste original funcione:

describe("example test", function() {
  var jQueryFxOff;

  beforeEach(function() {
    jQueryFxOff = $;
    $ = true;

  afterEach(function() {
    $ = jQueryFxOff;

  it("shows the menu after clicking the button", function() {


Note que o valor original da propriedade é armazenado numa variável e restaurado após o teste, para evitarmos que esta configuração afete outros testes que serão executados em sequencia.

O segundo caso é quando temos algum código que só é executado após um período de tempo - usando setTimeout, por exemplo:

module = {
  someRandomCode: function() {

  waitForIt: function() {
    setTimeout(this.someRandomCode, 5000);


A melhor forma de testar este código é "fakeando" a passagem do tempo, para que o teste não precise aguardar. Uma boa ferramenta para isto são os os fake timers do Sinon.JS:

describe("my random test", function() {
  var clock;

  beforeEach(function() {
    clock = sinon.useFakeTimers();
    spyOn(module, "someRandomCode");


  afterEach(function() {

  it("tests my random code", function() {

Outra boa opção é usar o Jasmine Clock:

describe("my random test", function() {
  beforeEach(function() {
    spyOn(module, "someRandomCode");


  afterEach(function() {

  it("tests my random code", function() {

Ferramentas para avaliar a qualidade do seu código existem aos montes, para qualquer linguagem de programação. Eu já utilizei algumas para Java, mas nunca tinha testado nenhuma para Ruby, apesar de ser a linguagem que mais uso há alguns anos. Por isso, resolvi testar todas as ferramentas que pude encontrar. Separei a avaliação entre serviços e ferramentas.


Classifiquei como serviços as ferramentas onde, em vez de instalar e executar localmente, você libera acesso ao seu repositório de código a elas, que coletam métricas a partir do código e geram algum relatório. Os dois serviços que avaliei são gratuitos para projetos open source e pagos para projetos com código fechado.

Code Climate

Este é certamente o serviço mais conhecido para avaliação de qualidade de código Ruby. Seu foco principal é gerar métricas baseadas em complexidade de código, mas ele também é capaz de identificar algumas falhas de segurança e cobertura de código dos testes.


O Coveralls funciona de forma semelhante ao Code Climate, porém tem um foco maior em testes. Ele exibe o histórico de cobertura e a diferença para cada arquivo a cada commit.


As ferramentas abaixo são open source e distribuídas através de gems. Para utilizá-las, basta instalar a gem e executar um comando, que analisa o código e gera relatórios ou dados brutos para serem analisados.


O Brakeman é uma ferramenta focada em localizar potenciais falhas de segurança no seu código. Ele também exibe alertas especificamente relacionados ao Rails, como falhas de segurança que já foram corrigidas numa versão do Rails mais recente do que a que você usa.

Como ele tem foco em segurança, é muito importante manter esta gem sempre atualizada, para que ele possa detectar falhas descobertas mais recentemente.


O foco do RuboCop é localizar más práticas de programação no seu código, com base no Ruby Style Guide. Algumas das regras são: uso de aspas simples ou duplas para definir strings, tamanho máximo da linha, número de linhas em cada método e uso de espaços na definição de hashes.

Todas as regras do RuboCop podem ser configuradas ou desabilitadas. Basta criar um arquivo .rubocop.yml na raiz do projeto com as configurações desejadas. Ele também possui regras específicas para projetos usando Rails.

Se você usa o editor Atom, também é possível executar o RuboCop automaticamente com o plugin atom-lint. Assim, ao salvar um arquivo, o RuboCop é automaticamente executado, e os alertas são exibidos no próprio editor, ao lado de cada linha.


O RubyCritic foi criado com o objetivo de se tornar uma alternativa gratuita ao Code Climate. Ele gera um relatório bem semelhante ao deste serviço, reportando os trechos mais complexos do seu código.

Metric Fu

O Metric Fu é um agregador de ferramentas de análise de código. Ele executa diversas ferramentas e gera uma página com links para os resultados de cada uma. É uma das ferramentas mais completas para análise de código Ruby, e uma das mais antigas.


O foco do Ruby-lint é localizar erros como variáveis não utilizadas ou não inicializadas, ou número errado de argumentos ao executar um método. O problema é que quando extendemos ou incluimos classes ou módulos definidos fora do projeto (em gems), ele não encontra as dependências e dá erro de constante indefinida. Apesar disso, parece que vem sendo bastante atualizado.


Esta ferramenta é um detector de bad smells no código, como Long Parameter List e Feature Envy. Possui plugins para rodar alguns editores, como Vim e TextMate.


Roodi significa "Ruby Object Oriented Design Inferometer". Ele executa algumas métricas de complexidade de código, mas é bem básico. A maioria das métricas já são calculadas por outras ferramentas apresentadas aqui.


Gera um score baseado na complexidade de código. Bem básico.

Rails Best Practices

Ferramenta bem útil, gera métricas de qualidade baseadas no Rails Best Practices. Como diz o nome, é específico para projetos Rails. Também é disponibilizado como um serviço online gratuito, mas somente para projetos públicos no Github. Para projetos privados, é possível instalar uma instância própria, pois o servidor é open source.


Mais um gerador de métricas de qualidade. As métricas são parecidas com as do RuboCop.


Este projeto é semelhante ao Ruby-lint, mas não consegui executar. Ele só funciona com Rubinius, mas é incompatível com a versão mais recente (apesar de o projeto não informar quais são as versões compatíveis).


Esta ferramenta procura similaridades na estrutura do seu código (ex: dois métodos que possuem o código muito semelhante).


Gera métricas de complexidade ciclomática.

Laser e Nitpick

Mais duas ferramentas semelhantes ao Ruby-lint. Como estão há anos sem atualizações, nem testei.


Entre as ferramentas que testei, gostei mais do Brakeman, do RuboCop e do RubyCritic. Acredito que são complementares, e, se usadas em conjunto, ajudam bastante a encontrar falhas de segurança, os pontos mais complexos do seu código e a seguir boas práticas de programação Ruby.

Além destas 3, também gosto do Metric Fu, mas por executar muitas ferramentas, acho que ele gera informação demais. Usando ferramentas e métricas em excesso, geramos tanta informação que acabamos ignorando-as. Por isso, preferi focar nas 3 ferramentas que citei, pois já consigo ter um panorama bem completo do status do meu código com elas.

Usando na prática

Todas as ferramentas que testei são executadas via linha de comando e geram como saída algum tipo de relatório. Apesar de poder executá-las manualmente, na minha opinião, é mais interessante executá-las no servidor de integração contínua (CI). Desta forma, garantimos que essas ferramentas serão executadas com frequencia, e todo o time tem acesso aos relatórios gerados, assim como gráficos de evolução a cada execução. Com isso, podemos analisar se a "saúde" do projeto está melhorando ou piorando, basta acompanhar se o número de warnings de uma determinada ferramenta estão aumentando ou diminuindo.

No caso específico do RuboCop, como ele analisa o uso de boas práticas de programação, acho mais útil executar no editor, pois, ao salvar um arquivo, tenho a resposta imediata, e posso fazer os ajustes no mesmo momento. Mas isso é uma questão de preferência.

Executando no Jenkins

Como uso o Jenkins, descrevi como configurar as ferramentas acima neste servidor. O processo é bem simples, acredito que não seja difícil reproduzí-lo em outros servidores de integração contínua.

Cada ferramenta pode ser executada por um job à parte ou no mesmo job que roda o build e os testes do projeto. Optei pela 1a opção, por 2 motivos:

  1. O tempo de execução de cada ferramenta de análise de código pode ser razoavelmente longo, o que deixaria o job de build muito lento
  2. Caso ocorra algum problema na execução de alguma destas ferramentas, não quero que o job de build do meu projeto apareça quebrado no CI. Se o build e todos os testes foram executados com sucesso, o job que executa o build deve ter sucesso

Além disso, optei por instalar as gems diretamente no CI. Desta forma, além de não precisar configurá-las no Gemfile do projeto, garanto que as gems estarão sempre atualizadas, o que é muito importante, principalmente no caso do Brakeman, pois novas falhas de segurança são encontradas diariamente.

Criando um novo job

Ao criar um novo job no Jenkins, você pode configurá-lo para executar automaticamente após cada build do projeto ou para executar periodicamente. Apesar de a primeira opção garantir que os relatórios de análise de código estarão atualizados a cada build, a execução é um pouco demorada. Além disso, achei que um relatório por dia seria suficiente para acompanhar o status do projeto. Sendo assim, configurei o job de relatórios para executar diariamente, de segunda a sexta-feira. Para isso, na configuração do job, basta selecionar a opção Build periodically, e no campo Schedule, digitar H 0 * * 1-5, por exemplo (o formato é o mesmo usado no crontab). Este valor configura o job para ser executado em qualquer minuto da hora zero, em qualquer dia do mês, todos os meses, de segunda a sexta-feira (dias 1 a 5).

Eu optei por criar um único job para executar todas as ferramentas, pois desta forma, tenho todos os relatórios centralizados num único local. A principal desvantagem é que, desta forma, um erro na execução de uma ferramenta fará o job encerrar com status de erro, e as ferramentas seguintes não serão executadas.

Para configurar cada ferramenta dentro do job, o processo é o mesmo:

  1. Selecionar em Build a opção Execute shell, com os comandos para instalar e executar a gem
  2. Adicionar uma Post-build Action para exibir os resultados

A configuração de cada ferramenta é a seguinte:


Configure a execução da ferramenta digitando os seguintes comandos no campo Execute shell da configuração do job:

mkdir -p tmp
gem install brakeman --no-ri --no-rdoc && brakeman -o tmp/brakeman-output.tabs --no-progress --separate-models --quiet

Para visualizar os resultados, instale o Brakeman Plugin no Jenkins e selecione em Post-build Actions a opção Publish Brakeman warnings. Em Brakeman Output File, digite tmp/brakeman-output.tabs.


Adicione o seguinte comando no campo Execute shell:

gem install rubycritic --no-ri --no-rdoc && rubycritic app lib

Para visualizar os resultados, é necessário o plugin HTML Publisher. Após instalá-lo, selecione em Post-build Actions a opção Publish HTML reports e digite os seguintes valores:

  • HTML directory to archive: tmp/rubycritic
  • Index page[s]: overview.html
  • Report title: RubyCritic Reports


Digite os comandos abaixo no campo Execute shell:

mkdir -p tmp
gem install rubocop --no-ri --no-rdoc && rubocop --fail-level E --rails --out tmp/rubocop.out app lib spec

O plugin para exibir o resultado desta ferramenta é o Warnings. Após instalá-lo, é necessário configurar um parser para os warnings do RuboCop. Vá até a configuração do Jenkins (Manage Jenkins -> Configure System). Em Compiler Warnings, adicione um novo parser com os seguintes valores:

  • Name: RuboCop
  • Link name: RuboCop
  • Trend report name: RuboCop Warnings
  • Regular Expression:
^([^:]+):(\d+):\d+: ([^:]): ([^:]+)$
  • Mapping Script:
import hudson.plugins.warnings.parser.Warning

String fileName =
String lineNumber =
String category =
String message =

return new Warning(fileName, Integer.parseInt(lineNumber), "RuboCop Warnings", category, message);
  • Example Log Message:
attributes/default.rb:21:78: C: Use %r only for regular expressions matching more than 1 '/' character.

Após salvar esta configuração, volte até a configuração do job e selecione em Post-build Actions a opção Scan for compiler warnings. Em File pattern digite tmp/rubocop.out, e no campo Parser, selecione o parser recém-criado, RuboCop.

Na última semana aconteceu o evento Abril Pro Ruby 2014, em Porto de Galinhas. O evento foi excelente, muito bem organizado e com várias palestras e workshops de excelente nível técnico.

No dia de palestras, fiz uma apresentação sobre Arquitetura Distribuída, onde mostrei uma pequena parte da arquitetura do projeto Globotv.

Os vídeos das apresentações foram disponibilizados no Youtube. Seguem abaixo os links para cada apresentação e workshop. Alguns estão faltando, mas assim que conseguir, eu atualizo os links do post.



Por ser uma linguagem orientada a objetos, Ruby possui variáveis de instância e de classe. As primeiras se referem a cada instância de uma determinada classe e as segundas à própria classe:

class Funcionario
  @@dias_de_ferias = 30

  def salario=(valor)
    @salario = valor

  def salario

Como esperado, cada instância da classe Funcionario possui uma instância da variável @salario, e a variável @@dias_de_ferias possui uma única instância:

Funcionario.class_variable_get(:@@dias_de_ferias)  # 30

funcionario1 =
funcionario1.salario = 2000
funcionario1.salario  # 2000

funcionario2 =
funcionario2.salario = 2500
funcionario2.salario  # 2500

Até aqui nada de novo. Porém, um tipo de variável menos conhecida e usada é a variável de instância de classe.

Como tudo em Ruby é um objeto, todas as classes (tanto as classes padrão do Ruby quanto as criadas pelo usuário) são objetos - instâncias da classe Class:

String.class  # Class

Funcionario.class  # Funcionario

E, como são objetos, as classes também podem ter variáveis de instância. Para definí-las e acessá-las, basta prefixar o nome da variável com "@", da mesma forma como é feito com variáveis de instância, porém no escopo de classe (ou num método de classe):

class Funcionario
  @bonus = 1000

  def self.atualiza_bonus
    @bonus = 2000

O comportamento de variáveis de instância de classe é semelhante às variáveis de classe, com uma diferença: quando usamos herança, as variáveis de classe são compartilhadas entre todas as classes da hierarquia, e as variáveis de instância de classe tem uma instância para cada classe:

class Funcionario
  @@dias_de_ferias = 30
  @bonus = 1000

class Gerente < Funcionario
  @bonus = 5000

Funcionario.class_variable_get(:@@dias_de_ferias)  # 30
Funcionario.instance_variable_get(:@bonus)  # 1000

Gerente.class_variable_get(:@@dias_de_ferias)  # 30
Gerente.instance_variable_get(:@bonus)  # 5000

Gerente.class_variable_set(:@@dias_de_ferias, 45)
Gerente.class_variable_get(:@@dias_de_ferias)  # 45
Funcionario.class_variable_get(:@@dias_de_ferias)  # 45

No exemplo acima, a variável de classe @@dias_de_ferias é compartilhada entre as classes Funcionario e Gerente. Por isso, ao alterar o valor desta variável na subclasse, o valor na superclasse também mudou. Para confirmar que a instância é a mesma, basta verificar que o object_id da variável em ambas as classes:

Funcionario.class_variable_get(:@@dias_de_ferias).object_id  # 70139308064800
Gerente.class_variable_get(:@@dias_de_ferias).object_id  # 70139308064800

No caso da variável de instância de classe @bonus, há uma instância para a classe Funcionario e outra para a classe Gerente:

Funcionario.instance_variable_get(:@bonus).object_id  # 70139307998300
Gerente.instance_variable_get(:@bonus).object_id  # 70139308064780

Na última semana aconteceu a última edição do FISL, o Fórum Internacional de Software Livre. No segundo dia do evento, fiz uma palestra sobre Design Patterns em Ruby, a qual foi baseada neste post. Todas as palestras do evento foram transmitidas ao vivo, e os vídeos já estão disponíveis.

Disponibilizei o vídeo da minha apresentação no Vimeo e os slides no Slideshare.

UPDATE: atualizei os slides após apresentar esta mesma palestra no evento RS on Rails.

Ao criar um novo projeto Rails, o generator cria uma estrutura padrão de diretórios. Dentro de app, ele cria os diretórios models, controllers, views e helpers. Os três primeiros tem papéis bem definidos, mas mesmo assim há uma certa confusão quando surge algum arquivo "fora do padrão".

Numa aplicação típica, um model geralmente estende a classe ActiveRecord::Base ou inclui um módulo, como Mongoid::Document, no caso do Mongoid, por exemplo, para mapear a estrutura do banco de dados. Além disso, o model contém as regras de negócio associadas a ele. O controller tem a responsabilidade de mapear a ação atual numa view - por exemplo, ao submeter um formulário para criação de um novo objeto, um controller típico renderiza uma view exibindo uma mensagem de sucesso, ou renderiza a mesma view do formulário com as mensagens de erro, caso haja algum. Já a view é responsável por exibir os dados correspondentes à página atual.

Essa estrutura básica funciona bem numa aplicação simples. O problema é quando a view começa a conter muita lógica. Por exemplo, uma view para exibir dados de um usuário poderia ser simples assim:


Porém, se o conteúdo muda dependendo do tipo de usuário (ex: usuário comum e admin), precisamos de um if dentro da view:

<% if @usuario.admin? %>
<% else %>
<% end %>

Quando mais diferenças houver, mais complexa fica a view. Consequentemente, fica mais difícil de gerenciar. Além disso, normalmente fazemos testes unitários para o model e o controller, e testamos a view somente com testes de aceitação, que são muito mais lentos (é preciso carregar todo o ambiente, Rails, banco de dados, e dependendo do teste, abrir um browser). Fica impraticável testar todos os fluxos de uma view cheia de if's usando testes de aceitação.

Uma solução comum no mundo Rails é usar os helpers. No exemplo acima, eu poderia ter o seguinte helper:

class UsuarioHelper
  def titulo_usuario(usuario)
    usuario.admin? ? "admin" : usuario.nome

Isso deixa o código da view mais simples:


E, além disso, posso testar a lógica num teste unitário do helper. Mas essa solução também tem problemas: o helper não está associado diretamente ao objeto em questão. Isso ficou claro no exemplo acima, onde precisei passar o usuário como parâmetro para o método do helper. Isso se repetiria para cada método.

Uma boa solução para este caso é utilizar o design pattern Decorator. Para isso, criamos uma classe que recebe o model como parâmetro no construtor e implementa todos os métodos necessários para lógicas de visualização (ou seja, que não estão associados ao negócio e não devem ficar no model). Um exemplo de decorator é o seguinte:

class UsuarioDecorator
  attr_reader :usuario

  def initialize(usuario)
    @usuario = usuario

  def titulo
    @usuario.admin? ? "admin" : @usuario.nome

Outra opção é criar um decorator que também implementa o padrão Delegation. Neste caso, quando é chamado um método que não existe, o Decorator delega a chamada para o model. Segue um exemplo de implementação:

module Decorator
  attr_reader :model

  def initialize(model)
    @model = model

  def method_missing(meth, *args)
    if @model.respond_to?(meth)
      @model.send(meth, *args)

  def respond_to?(meth)

  def self.included(base)

  module ClassMethods
    def decorate(object)
      if object.is_a? Enumerable {|obj|}

class UsuarioDecorator
  include Decorator

  def titulo
    @model.admin? ? "admin" : @model.nome

Desta forma, temos uma classe que recebe o model no construtor ou no método de classe decorate. A implementação do método titulo_usuario no Decorator ficou muito mais simples. Para utilizá-la, basta decorar o model no controller:

class UsuariosController
  def show
    @usuario = UsuarioDecorator.decorate(Usuario.find(params[:id]))

A implementação da view fica assim:


Em casos mais simples, o Decorator atende bem. Mas e quando temos uma página mais complexa, envolvendo diversos objetos? Precisaríamos criar um Decorator para cada model, e lembrar de decorar cada objeto na criação, assim como fizemos com o usuário no exemplo anterior. Outro problema é que podemos ter visualizações diferentes de um objeto em cada tela da aplicação. Como tratar este caso? Poderíamos criar métodos diferentes no Decorator, mas com o tempo o Decorator poderia virar um monstro. Outra opção é criar vários Decorators para aquele model, onde cada um se aplica a uma página. Ou podemos usar um outro padrão, o Presenter.

O Presenter é um padrão também conhecido por outros nomes, como View Object, mas na comunidade Ruby o nome Presenter se popularizou com um post de Jay Fields. O Presenter é muito parecido com o Decorator, mas envolve vários objetos. O contexto do Presenter é uma página específica da aplicação, e recebe como parâmetro todos os objetos necessários à exibição daquela página. Desta forma, toda a lógica de apresentação fica numa única classe. Segue um exemplo de uso do Presenter:

class PedidoPresenter
  def initialize(usuario, pedidos)
    @usuario = usuario
    @pedidos = pedidos

  def titulo
    "Usuário #{@usuario.nome} - #{@pedidos.size} pedidos"

  def links { |pedido| link_to pedido.nome, pedido_url(pedido) }

Ainda há uma outra opção além do Presenter, que foi apresentada no livro Objects on Rails. É o padrão Exhibit. A diferença em relação ao Presenter é que, enquanto o Presenter disponibiliza métodos para serem chamados pela view (como no exemplo acima), o Exhibit é responsável pela renderização. Para isso, ele precisa receber um contexto:

class Exhibit
  def initialize(obj, context)
    @obj = obj
    @context = context

  def render_header
    @context.render :partial => "header", :locals => {:obj => @obj}

Este contexto pode ser o view_context do controller:

class Controller
  def show
    @usuario =[:id]), view_context)

Outra maneira de instanciar o Exhibit é através de um helper, como mostrado no livro The Rails View:

class Helper
  def exhibit[:id]), self)

São muitos padrões que tem a mesma função: encapsular a lógica de visualização num único local, que seja facilmente testável. E qual é a melhor opção entre os três? A resposta depende da situação. Não adianta querer encontrar um padrão perfeito para todos os casos. Na minha opinião, o Decorator funciona bem em páginas mais simples, que envolvem apenas um model. Quando a página é mais complexa e envolve vários models, o Presenter e o Exhibit são mais adequados. E a diferença entre os dois é uma questão de gosto.

Links relacionados:

Um dos temas mais atuais no desenvolvimento web é a otimização de sites. A motivação para reduzir o tempo de carregamento das páginas vem não só de estudos que mostram que quando maior o tempo de carregamento, maior o número de usuários que abandonam o site, mas também do fato de que o Google considera o tempo de resposta na criação do PageRank.

Um dos itens mais importantes ao otimizar um site, de acordo com Steve Souders, é diminuir o número de requests. Um recurso muito útil para isso é a criação de sprites, ou seja, um único arquivo contendo várias imagens que são utilizadas no site. Nos locais que fazem referência a estas imagens, são definidos a largura, altura e offset do sprite. Desta forma, é feito um único request para obter todas as imagens. O uso de sprites é muito comum em grandes sites como Twitter, Facebook, Google e Yahoo.

A criação de um sprite manualmente é uma tarefa bem trabalhosa. É necessário criar uma imagem utilizando uma ferramenta como o Photoshop, por exemplo, e colar cada imagem uma abaixo (ou ao lado) da outra, deixando alguns pixels entre imagens. Sempre que se quiser adicionar uma nova imagem, será necessário abrir o sprite novamente no Photoshop e repetir o processo para inserir a nova imagem.

Para simplificar esta tarefa, é possível utilizar uma ferramenta para geração automática de sprites. Uma das melhores ferramentas para isso é o Compass. Após instalar e configurar, basta colocar as imagens num diretório e o sprite será gerado automaticamente. A instalação num projeto Rails é extremamente simples, e está bem explicada no help do Compass.

A configuração básica do Compass para geração de sprites segue o padrão abaixo:

$imagens-spacing: 2px
$imagens-sprite-dimensions: true
@import "imagens/*.png"

No exemplo acima, todas as propriedades são configuradas com o prefixo imagens. O sprite é configurado com espaçamento de 2 pixels entre cada imagem, para evitar sobreposição no limite entre as imagens. A segunda linha habilita a inclusão das dimensões das imagens no CSS gerado, o que é útil para manter fixo o tamanho ocupado pela imagem enquanto ela é carregada. A terceira linha informa quais imagens serão adicionadas. Neste caso, são todas as imagens com extensão png que estão no diretório imagens. É importante lembrar que o nome deste diretório deve ser igual ao prefixo utilizado nas propriedades.

Além de gerar o sprite, o Compass cria classes CSS para referenciar cada imagem. Os nomes das classes começam com o prefixo utilizado acima, seguido por hífen e o nome da imagem sem extensão. Por exemplo, para uma imagem chamada excluir.png, a classe teria o nome imagens-excluir.

O uso as imagens do sprite no seu CSS pode ser feito de duas formas: usando diretamente as classes criadas pelo Compass (como imagens-excluir, no exemplo anterior) ou utilizando um mixin do Compass no seu arquivo Sass:

.minha-classe { @include imagens-sprite(excluir); }

Ao utilizar uma destas configurações, a imagem será configurada como background do elemento.

Para criar um segundo sprite, para a parte administrativa da aplicação, por exemplo, é necessário utilizar um prefixo diferente, como no exemplo abaixo:

$imagens-admin-spacing: 2px
$imagens-admin-sprite-dimensions: true
@import "admin/imagens-admin/*.png"

Neste exemplo, as imagens do sprite estão no diretório admin/imagens-admin, e o prefixo segue o nome do último diretório (imagens-admin). Isso significa que, no exemplo acima, não seria possível manter o sprite do admin em admin/imagens, pois haveria conflito de nomes com o outro sprite.

Os sprites gerados pelo Compass são arquivos png que tem como nome o prefixo utilizado na configuração seguido por um hash (ex: imagens-b03bdb7a79370e7ff107e7b37fe7df6e.png). Quando o sprite é modificado (em ambiente de desenvolvimento o Compass verifica automaticamente a cada request se alguma imagem foi adicionada ou removida, e em produção é necessário executar um rake task para isso), o Compass gera um novo hash para o nome do arquivo. Isto é feito para evitar que o sprite seja cacheado pelo browser. Se isso acontecesse, o browser não buscaria o sprite atualizado, mantendo o arquivo anterior.

Os exemplos descritos acima descrevem apenas as configurações básicas para geração de sprites. A documentação de sprites traz mais detalhes sobre as opções de configuração. Além disso, o Compass tem muitas outras funcionalidades. Vale a pena pesquisar a referência no site do Compass para mais detalhes.

UPDATE: outra configuração útil para os sprites é o layout das imagens. Por padrão, o layout dos sprites é vertical, ou seja, cada imagem é colocada abaixo da anterior. Porém, o Compass também permite definir layout horizontal, diagonal ou smart. Neste último, a disposição das imagens é feita de acordo com o tamanho de cada uma, e o resultado é uma imagem menor do que com o layout padrão. No meu projeto, ao trocar o layout vertical pelo smart, o sprite ficou cerca de 10% menor. No primeiro exemplo deste post, a configuração do layout ficaria assim:

$imagens-layout: smart

Ao utilizar o scaffold do Rails, ele criará todos os métodos necessários no controller. Depois que o usuário preenche os dados do formulário e envia, é executado o método create do controller. Este método faz algo semelhante ao código abaixo (no exemplo é um CRUD de usuário):

@usuario =[:usuario])

Na forma em que o Rails cria o formulário, os atributos do usuário são passados ao controller como um hash. O valor do params acima é algo parecido com isso:

{ "authenticity_token" => "xI1Cy+LvUZzg6FR/1Y/JHcaHVPRyWsHmRII8BhMOr0E=",
 "utf8" => "?",
 "action" => "create",
 "controller" => "usuarios",
 "usuario" => { "nome" => "Novo usuario", "email" => "" } }

Além da definição do token de segurança, codificação em utf-8, nome do controller e da action que será executada, o parâmetro usuario contém todos os atributos que foram preenchidos no formulário. Desta forma, é muito simples atribuir os parâmetros preenchidos a um objeto Usuario, seja criando um novo (@usuario =[:usuario])) ou editando (@usuario.update_params(params[:usuario])). O Rails chama isso de mass assignment.

O problema desta abordagem é que o usuário poderia facilmente inserir novos parâmetros neste hash, simplesmente adicionando tags input hidden no formulário (usando o Firebug, por exemplo):

<input type="hidden" name="usuario[admin]" id="usuario_admin" value="true" />

Adicionando o código acima, o novo usuário criado receberia o valor true no atributo admin, o que representa uma falha grave na segurança da aplicação.

O Rails oferece um mecanismo para garantir a segurança nestes casos, usando os métodos attr_protected e attr_accessible do ActiveModel. O primeiro permite definir atributos que não podem ser alterados através de mass assignment:

class Usuario
  attr_protected :admin

E o attr_accessible é uma forma mais segura: somente os atributos passados para este método poderão ser alterados com mass assignment. Os demais ficam protegidos:

class Usuario
  attr_accessible :nome, :email

Obviamente estes dois métodos não podem ser usados simultaneamente, pois um exclui o outro.

Se você quiser atualizar um objeto com mass assignment ignorando a segurança fornecida pelo attr_accessible e pelo attr_protected, basta utilizar o parâmetro without_protection (somente no Rails 3.1):

  {:nome => "Novo usuario", :email =>"", :admin => true},
  :without_protection => true)

No exemplo acima, o usuário criado terá o atributo admin igual a true, mesmo que tenha sido usado o método attr_protected ou o attr_accessible para evitar a alteração deste atributo.

Outra opção interessante para evitar a alteração de atributos indesejados é o método attr_readonly. Os atributos passados para este método só poderão ser definidos na criação do objeto, e não poderão ser alterados depois. Porém, este método faz parte do ActiveRecord::Base, e não do ActiveModel, ou seja, ele não estará disponível se você usar outro ORM. Há uma issue aberta no Github solicitando que este método seja movido para o ActiveModel.

Link relacionado: Recebendo dados do usuário: attr_accessible e attr_protected