Skip to content
maio 8, 2010 / cassiomarques

Como organizo meus models e suas specs

Models podem ficar grandes, bem grandes. Isso é sinal de que talvez eles estejam fazendo coisa demais, o que indica que você precisa separar as responsabilidades e provavelmente criar outros objetos que interajam entre si. Para simplificar um pouco mais as coisas, além de criar models que façam coisas específicas ao invés de querer “abraçar o mundo”, é interessante também separar os diferentes mecanismos que constituem o comportamento de um model.

Dentro de um model acabamos sempre descrevendo dois tipos de comportamentos distintos: O comportamento da classe do model (através de métodos estáticos) e o comportamento das instâncias dessa classe. No modelo clássico adotado pelo Rails, a parte estática dos models cuida da criação de associações, regras de validação, escopos de busca e alguns métodos utilitários que façam sentido dentro da modelagem do seu problema. Por outro lado, métodos de instância descrevem as regras de negócio da sua aplicação e constituem a parte mais importante do model.

Seguindo essa linha, é interessante imaginar um model como um conjunto de interesses, ou concerns. Podemos separar os models em diferentes concerns, de forma a organizar melhor o código. Como desenvolvemos usando TDD (você usa TDD, certo?) nossos testes também devem refletir essa forma de organizar os models.

Tenho usado com sucesso um pequenos plugin para o Rails (que pode até mesmo ser implementado em um único arquivo) chamado concerned_with para separar diferentes concerns de models que tendem a ficar muito longos. Se vocês olharem, esse plugin não é atualizado há um bom tempo, mas isso se deve ao fato de que não há muito o que se fazer, o código que ele implementa é realmente muito simples.

Para usar o concerned_with, é bem fácil:

class Person < ActiveRecord::Base
  concerned_with :validations
  concerned_with :associations_and_scopes

  concerned_with :whatever
end

e crie uma nova pasta com o nome do model dentro de app/models, na qual você vai criar um novo arquivo para cada concern. Esses arquivos devem ter o mesmo nome que seu respectivo concern. No nosso exemplo, ficariamos com a seguinte estrutura:


|-- app
|   |   
|   |-- models
|   |   |-- person
|   |   |   |-- associations_and_scopes.rb
|   |   |   |-- validations.rb
|   |   |   `-- whatever.rb
|   |   `-- person.rb

Em cada arquivo de concern, reabra o seu model e descreva o código normalmente. Por exemplo, no caso do arquivo app/models/person/validation.rb

class Person < ActiveRecord::Base
  validates_presence_of :name

  validate_on_create :some_validation_rule

  private
  def some_validation_rule
    # ...
  end
end

As specs deverão seguir o mesmo formato. Você até pode colocar os testes de cada concern no mesmo arquivo spec/models/person_spec.rb, mas é melhor separar para manter a convenção e organizar melhor as coisas:


|-- spec
|   |-- models
|   |   |-- person
|   |   |   |-- associations_and_scopes_spec.rb
|   |   |   |-- validations_spec.rb
|   |   |   `-- whatever_spec.rb
|   |   `-- person_spec.rb

E dentro de cada spec, algo como:

describe Person, "(validations)" do
  # ...
end

Você pode agora estar pensando: “Mas isso vai dificultar a navegação entre arquivos, vai ficar mais dificil seguir a lógica implementada no model”. Para evitar que isso aconteça, siga a convenção criada. Nunca misture os concerns dentro de um mesmo arquivo.

Outra coisa que ajuda bastante é usar os recursos de tags do seu editor. Em conjunto com o ctags, é possível fazer com que seu editor navegue pelas definições dos métodos. no terminal, dentro do seu projeto, faça:


$ ctags -R .

Isso criará um arquivo com as tags do seu código, a partir do qual seu editor, caso possua alguma extensão para tratar arquivos de tags, poderá navegar pelas definições dos métodos. Existem outras opções para controlar como ou quais tags são geradas, dê uma olhada na documentação do ctags.

No Vim, com o cursor sobre o nome de um método, basta fazer C-] (control + ‘]’) que o editor irá para a definição do método. Para voltar ao ponto anterior, C-t (control + ‘t’). No Emacs sei que também existe suporte para o ctags (mas não sei quais são os atalhos). No Textmate não sei se existe suporte.

Com isso, fica fácil navegar pelos arquivos criados para cada model, e o que é melhor, sem colocar a mão no mouse :)

5 Comentários

Deixe um comentário
  1. Jésus Lopes / maio 9 2010 12:38 am

    Rápido, objetivo e muito útil!

    Estou passando por uma situação parecida e já estava imaginando uma maneira de como resolver isso.

    Valeu pela dica!

    Abraço.

  2. Ricardo Yasuda / maio 13 2010 9:45 pm

    Cássio, e dá pra fazer essa separação de specs mesmo sem usar o concerned_with?

    • cassiomarques / maio 13 2010 10:27 pm

      Se você mantiver seu model em um único arquivo mesmo, dá para separar as specs normalmente. Eu já fazia isso antes. Pro Rspec, quando ele carrega os arquivos que terminam com _spec.rb, não há diferença se dois blocos describe… do … end estão um abaixo do outro no mesmo arquivo ou em arquivos separados.

  3. Daniel Lopes / ago 26 2010 5:22 am

    Cara, por que voce não gosta do comportamento de usar apenas módulos?

Deixe uma resposta

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

%d blogueiros gostam disto: