Com os Observadores removidos oficialmente do Rails 4.0 , estou curioso para saber o que outros desenvolvedores estão usando em seu lugar. (Além de usar a gema extraída.) Embora os Observadores certamente tenham sido abusados e possam se tornar facilmente difíceis às vezes, havia muitos casos de uso fora da limpeza de cache, onde eles eram benéficos.
Pegue, por exemplo, um aplicativo que precise rastrear alterações em um modelo. Um observador pode facilmente observar as alterações no modelo A e registrar essas alterações no modelo B no banco de dados. Se você quisesse observar mudanças em vários modelos, um único observador poderia lidar com isso.
No Rails 4, estou curioso sobre quais estratégias outros desenvolvedores estão usando no lugar do Observers para recriar essa funcionalidade.
Pessoalmente, estou inclinado a uma espécie de implementação de "controlador de gordura", onde essas alterações são rastreadas no método de criação / atualização / exclusão de cada controlador de modelo. Embora inche levemente o comportamento de cada controlador, ele ajuda na legibilidade e compreensão, pois todo o código está em um só lugar. A desvantagem é que agora existe um código muito semelhante, espalhado por vários controladores. A extração desse código em métodos auxiliares é uma opção, mas você ainda recebe chamadas para esses métodos espalhados por toda parte. Não é o fim do mundo, mas também não está no espírito dos "controladores magros".
Os retornos de chamada ActiveRecord são outra opção possível, embora eu pessoalmente não goste, pois tende a acoplar dois modelos diferentes muito próximos na minha opinião.
Portanto, no mundo do Rails 4, sem observadores, se você tivesse que criar um novo registro depois que outro registro fosse criado / atualizado / destruído, que padrão de design você usaria? Controladores de gordura, retornos de chamada ActiveRecord ou algo totalmente diferente?
Obrigado.
fonte
Respostas:
Dê uma olhada no Preocupações
Crie uma pasta no diretório de modelos chamada preocupações. Adicione um módulo lá:
A seguir, inclua nos modelos em que você deseja executar o after_save em:
Dependendo do que você está fazendo, isso pode te aproximar sem observadores.
fonte
Eles estão em um plugin agora.
Também posso recomendar uma alternativa que fornecerá controladores como:
fonte
ActiveSupport::Notifications
é voltado para a instrumentação, não para o sub / pub genérico.ActiveSupport::Notifications
?Notifications
muito, mas eu diria queWisper
tem uma API melhor e recursos como 'assinantes globais', 'no prefixo' e 'mapeamento de eventos' queNotifications
não. Uma versão futura doWisper
também permitirá a publicação assíncrona via SideKiq / Resque / Celluloid. Além disso, potencialmente, em versões futuras do Rails, a APINotifications
pode mudar para se concentrar mais na instrumentação.Minha sugestão é ler o post de James Golick em http://jamesgolick.com/2010/3/14/crazy-heretical-and-awesome-the-way-i-write-rails-apps.html (tente ignorar como indecente o título).
Voltar no dia era tudo "modelo gordo, controlador magro". Então os modelos gordos se tornaram uma dor de cabeça gigante, especialmente durante os testes. Mais recentemente, a tendência foi para modelos magros - a idéia é que cada classe deve lidar com uma responsabilidade e o trabalho de um modelo é manter seus dados em um banco de dados. Então, onde termina toda a minha lógica de negócios complexa? Nas classes de lógica de negócios - classes que representam transações.
Essa abordagem pode se transformar em um atoleiro (giggity) quando a lógica começa a ficar complicada. Porém, o conceito é sólido - em vez de desencadear coisas implicitamente com retornos de chamada ou observadores difíceis de testar e depurar, desencadeie coisas explicitamente em uma classe que coloca a lógica em cima do seu modelo.
fonte
O uso de retornos de chamada de registro ativos simplesmente inverte a dependência do seu acoplamento. Por exemplo, se você possui
modelA
um estilo deCacheObserver
observação demodelA
trilhos 3, pode removêCacheObserver
-lo sem problemas. Agora, diga queA
precisa chamar manualmente oCacheObserver
after save, que seria o trilhos 4. Você simplesmente mudou sua dependência para poder remover com segurança,A
mas não o fazCacheObserver
.Agora, da minha torre de marfim, prefiro que o observador seja dependente do modelo que está observando. Eu me importo o suficiente para bagunçar meus controladores? Para mim, a resposta é não.
Presumivelmente, você pensou um pouco sobre por que deseja / precisa do observador e, portanto, criar um modelo dependente dele não é uma tragédia terrível.
Também tenho uma aversão (razoavelmente fundamentada, eu acho) por qualquer tipo de observador dependente de uma ação do controlador. De repente, você deve injetar seu observador em qualquer ação do controlador (ou outro modelo) que possa atualizar o modelo que você deseja observar. Se você pode garantir que seu aplicativo apenas modifique instâncias por meio de ações de criação / atualização de controlador, mais poder será para você, mas isso não é uma suposição que eu faria sobre um aplicativo rails (considere formulários aninhados, modele associações de atualização de lógica de negócios etc.)
fonte
O Wisper é uma ótima solução. Minha preferência pessoal por retornos de chamada é que eles são acionados pelos modelos, mas os eventos são ouvidos apenas quando uma solicitação é recebida, ou seja, eu não quero que os retornos sejam acionados enquanto estou configurando modelos em testes, etc. acionado sempre que houver controladores envolvidos. Isso é realmente fácil de configurar com o Wisper, porque você pode dizer para ouvir apenas eventos dentro de um bloco.
fonte
Em alguns casos, eu simplesmente uso a Instrumentação de Suporte Ativo
fonte
Minha alternativa ao Rails 3 Observers é uma implementação manual que utiliza um retorno de chamada definido dentro do modelo, mas consegue (como agmin afirma em sua resposta acima) "inverter a dependência ... o acoplamento".
Meus objetos herdam de uma classe base que fornece o registro de observadores:
(Concedido, no espírito de composição sobre herança, o código acima pode ser colocado em um módulo e misturado em cada modelo.)
Um inicializador registra observadores:
Cada modelo pode definir seus próprios eventos observáveis, além dos retornos de chamada básicos do ActiveRecord. Por exemplo, meu modelo de usuário expõe 2 eventos:
Qualquer observador que deseja receber notificações para esses eventos apenas precisa (1) se registrar no modelo que expõe o evento e (2) ter um método cujo nome corresponda ao evento. Como seria de esperar, vários observadores podem se registrar para o mesmo evento e (em referência ao segundo parágrafo da pergunta original) um observador pode observar eventos em vários modelos.
As classes de observador NotificationSender e ProfilePictureCreator abaixo definem métodos para os eventos expostos por vários modelos:
Uma ressalva é que os nomes de todos os eventos expostos em todos os modelos devem ser exclusivos.
fonte
Penso que o problema dos observadores serem preteridos não é que os observadores eram maus por si mesmos, mas que estavam sendo abusados.
Eu recomendaria não adicionar muita lógica em seus retornos de chamada ou simplesmente mover o código para simular o comportamento de um observador quando já houver uma solução sólida para esse problema, o padrão Observador.
Se faz sentido usar observadores, use todos os meios. Apenas entenda que você precisará garantir que a lógica do observador siga práticas de codificação de som, por exemplo, o SOLID.
A jóia do observador está disponível em rubygems se você quiser adicioná-la novamente ao seu projeto https://github.com/rails/rails-observers
veja esta breve discussão, embora não seja uma discussão abrangente e completa, acho que o argumento básico é válido. https://github.com/rails/rails-observers/issues/2
fonte
Você pode tentar https://github.com/TiagoCardoso1983/association_observers . Ainda não foi testado para o Rails 4 (que ainda não foi lançado) e precisa de mais colaboração, mas você pode verificar se ele faz o truque para você.
fonte
Que tal usar um PORO?
A lógica por trás disso é que suas 'ações extras ao salvar' provavelmente serão uma lógica de negócios. Eu gosto de me manter separado dos modelos de AR (que devem ser o mais simples possível) e dos controladores (que são incômodos para testar adequadamente)
E simplesmente chame assim:
Você pode até expandi-lo injetando objetos de ação pós-salvamento extras
E para dar um exemplo dos 'extras'. Você pode querer aumentá-los um pouco:
Se você gosta dessa abordagem, recomendo a leitura da postagem no blog Bryan Helmkamps 7 Patterns .
Edição: devo mencionar também que a solução acima permite adicionar lógica de transação, bem quando necessário. Por exemplo, com ActiveRecord e um banco de dados suportado:
fonte
Vale ressaltar que o
Observable
módulo da biblioteca padrão Ruby não pode ser usado em objetos do tipo registro ativo desde os métodos de instânciachanged?
echanged
colidirá com os deActiveModel::Dirty
.Relatório de bug do Rails 2.3.2
fonte
Eu tenho o mesmo probjem! Encontro uma solução ActiveModel :: Dirty para que você possa acompanhar as alterações no seu modelo!
http://api.rubyonrails.org/classes/ActiveModel/Dirty.html
fonte