Last active
December 11, 2015 02:18
-
-
Save aneziocampos/4529155 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Cuidados com Observer e callbacks | |
Já foi divulgado que na versão 4 do Rails estará sendo removido o Observer e ele deverá ser utilizado como uma gem. Estive analisando em alguns projetos a utilização dessa classe e as vezes encontro situações onde acredito que ela pode estar fazendo mais mal do que bem para 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, o que 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. | |
class UserObserver < ActiveRecord::Observer | |
def after_create(user) | |
NotificationsMailer.welcome_user(user.id).deliver | |
end | |
end | |
DRY utilizando Observer | |
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 tendo 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: | |
class AuditObserver < ActiveModel::Observer | |
observe :account, :balance | |
def after_update(record) | |
AuditTrail.new(record, "UPDATED") | |
end | |
end | |
Aqui o Observer está criando um registro de auditoria tanto no Account quanto no Balance. Já justifica a sua utilização. | |
Caos do callback | |
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 | |
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: | |
class UserSignup | |
def initialize(params) | |
@user = User.new(params) | |
end | |
def signup | |
if @user.save | |
NotificationsMailer.welcome_user(user.id).deliver | |
end | |
@user | |
end | |
end | |
class UsersController < ApplicationController | |
def create | |
@user = UserSignup.new(params).signup | |
if @user.errors.present? | |
redirect_to dashboard_path, notice: "User created successfully" | |
else | |
render :new | |
end | |
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. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment