É muito comum na implementação precisarmos realizar ações relacionadas às alterações dos objetos. Em um exemplo de caso a ser demonstrado, vamos supor que queremos enviar uma notificação quando os atributos :data_inicio ou :data_fim do model Projeto forem alterados. A primeira alternativa (trivial) seria utilizar algum callback do ActiveRecord, como o after_update:
1 2 3 4 5 6 7 8 9 10 11 |
class Projeto < ActiveRecord::Base after_update :check_data def check_data if self.data_inicio_changed? || self.data_fim_changed? Notificacao.nova_data(self.data_inicio, self.data_fim) end end end |
O código acima funciona perfeitamente como esperado, mas viola uma boa prática de programação do SOLID: o princípio de responsabilidade única. O model Projeto é responsável por suas relações, validações e métodos relacionados, mas não por notificar. Para tal, nós podemos delegar essa função a uma outra classe utilizando um padrão de projeto chamado Observer:
Diagrama UML do design pattern Observer
O ActiveRecord deixou de possuir a classe Observer em seu núcleo por padrão desde a versão 4 do Rails. Então para versões Rails 4.0 ou mais recentes, você precisa incluir a gem no seu Gemfile antes de instalá-la com o bundle:
1 |
gem 'rails-observers' |
Para utilizá-la, você pode criar uma pasta de observers em app/observers. No exemplo citado anteriormente, o callback de after_update seria transferido para o arquivo app/observers/projeto_observer.rb:
1 2 3 4 5 6 7 8 9 |
class ProjetoObserver < ActiveRecord::Observer def after_update(projeto) if projeto.data_inicio_changed? || projeto.data_fim_changed? Notificacao.nova_data(projeto.data_inicio, projeto.data_fim) end end end |
O nome do método recebe o mesmo nome do callback a que se refere e recebe como parâmetro o objeto que está sendo observado. Além disso, é importante notar o padrão NomeDoModelObserver para nomear a classe, que nesse caso sabe exatamente qual model deve ser observado. Caso não siga o padrão, você deve indicar na sua classe qual model está sendo observado (é possível também utilizar um mesmo observer para assistir mais de um model):
1 2 3 |
class DataObserver < ActiveRecord::Observer observe :projeto, :tarefa end |
Após a criação da sua classe, é necessário também registrar o observer no application.rb (não se esqueça de reiniciar o servidor):
1 |
config.active_record.observers = :projeto_observer |
Pronto. Isso é suficiente para que seu observer opere da forma esperada. É válido ressaltar que os observers não interferem no ciclo de criação/atualização/destruição do objeto. Portanto, se você usa por exemplo um callback before_destroy que cancela a deleção do objeto em determinada condição, isso deve ser feito no model. O observer é utilizado apenas para monitorar as alterações e é empregado principalmente para notificações, envio de emails e atualizações assíncronas de tela. Quaisquer dúvidas ou sugestões, utilize a área de comentários ou entre em contato.
1 Comentário
Sannytet
12 de dezembro de 2018 at 01:11Nice posts! 🙂
___
Sanny