Já foi divulgado que na versão 4 do Rails removerão o Observer e ele deverá ser utilizado como uma gem. Analizei em alguns projetos a utilização dessa classe e as vezes encontro situações onde acredito que ela pode estar prejudicando mais do que colaborando com o projeto.
A grosso modo a utilização de Observer nada mais é do que uma extração de código dos callbacks. Ou seja, é necessário também muito cuidado ao ser utilizado para não exagerar na lógica que é colocada nela, já que isso pode gerar comportamentos não desejados da classe, além de aumentar a complexidade nos testes onde a gente acaba tendo que mockar/implementar funcionalidades extras do que realmente está querendo ser testado.
Um exemplo dessa situação é o Welcome email que é enviado quando um usuário é cadastrado.
1 class UserObserver < ActiveRecord::Observer
2 def after_create(user)
3 NotificationsMailer.welcome_user(user.id).deliver
4 end
5 end
A utilização de observer sem que ele seja utilizado em multiplas classes é desnecessária, você não está fazendo nada mais do que pegando o callback de uma classe e extraindo para outra classe. Não vejo o que se ganha com isso. Além de estar complicando o desenvolvedor, já que ele tem que abrir 2 arquivos para entender o que está sendo realizado.
Essa extração é produtiva no caso onde diversas classes acabam executando o mesmo callback como nesse exemplo:
1 class AuditObserver < ActiveModel::Observer
2 observe :account, :balance
3
4 def after_update(record)
5 AuditTrail.new(record, "UPDATED")
6 end
7 end
Aqui o Observer está criando um registro de auditoria tanto no Account quanto no Balance. Já justifica a sua utilização.
Este tipo de callback cria um comportamento padrão para toda criação de usuário que nem sempre pode ser desejada. Por exemplo, posso querer importar usuários e não querer que seja disparado o welcome email, e sim outro email customizado.
Além disso isso também cria mais complexidade nos testes, suponha que quero enviar um email na mudança de status do User, nos testes vou precisar criar o usuário e alterar seu status para disparar o email que quero testar, no entanto ao criar o usuario estarei enviando o Welcome email, algo que realmente não é necessario e terei de fazer um tratamento nos testes para evita-lo.
Este caso é de um simples email extra enviado, mas agora imagine um after_create executando 4 ou 5 tarefas o quanto isso vai implicar na implementação de diversos testes e também o quanto vai surgir de tratamentos que deverão ser realizados para executar ou não cada tarefa, isso pode se tornar um grande BAD SMELL.
Uma solução que tem se mostrado muito claro, simples e quebra a complexidade dos models é extrair comportamentos de um determinado cenario para uma nova classe. Por exemplo:
1 class UserSignup
2 def initialize(params)
3 @user = User.new(params)
4 end
5
6 def signup
7 if @user.save
8 NotificationsMailer.welcome_user(user.id).deliver
9 end
10 @user
11 end
12 end
13
14 class UserController < ApplicationController
15 def create
16 @user = UserSignup.new(params).signup
17
18 if @user.errors.present?
19 render :new
20 else
21 redirect_to dashboard_path, notice: "User create successfully"
22 end
23 end
24 end
Dessa forma estou extraindo a situação específica da criação de usuários pelo formulário padrão do projeto para uma classe que terá seu comportamento bem específico deixando assim o código mais simples para se testar, tirando complexidade da classe User e de quebra ainda deixará de executar em diversos testes do projeto os callbacks da criação do User o que irá impactar positivamente na velocidade da execução dos testes.
Comentários
Included file post/disqus_thread.html not found in _includes directory