Con los observadores eliminados oficialmente de Rails 4.0 , tengo curiosidad por saber qué otros desarrolladores están usando en su lugar. (Aparte de usar la gema extraída.) Si bien los observadores ciertamente fueron abusados y fácilmente podrían volverse difíciles de manejar a veces, hubo muchos casos de uso fuera del solo borrado de caché donde fueron beneficiosos.
Tomemos, por ejemplo, una aplicación que necesita rastrear cambios en un modelo. Un observador podría observar fácilmente los cambios en el Modelo A y registrar esos cambios con el Modelo B en la base de datos. Si desea observar los cambios en varios modelos, entonces un solo observador podría manejar eso.
En Rails 4, tengo curiosidad por saber qué estrategias están usando otros desarrolladores en lugar de Observers para recrear esa funcionalidad.
Personalmente, me estoy inclinando hacia una especie de implementación de "controlador gordo", donde estos cambios se rastrean en el método de creación / actualización / eliminación del controlador de cada modelo. Si bien aumenta ligeramente el comportamiento de cada controlador, ayuda en la legibilidad y la comprensión, ya que todo el código está en un solo lugar. La desventaja es que ahora hay un código muy similar disperso en varios controladores. Extraer ese código en los métodos auxiliares es una opción, pero aún le quedan llamadas a esos métodos en todas partes. No es el fin del mundo, pero tampoco en el espíritu de "controladores flacos".
Las devoluciones de llamada de ActiveRecord son otra opción posible, aunque personalmente no me gusta, ya que, en mi opinión, tiende a acoplar dos modelos diferentes.
Entonces, en el mundo de Rails 4, sin observadores, si tuviera que crear un nuevo registro después de que otro registro fue creado / actualizado / destruido, ¿qué patrón de diseño usaría? ¿Controladores gordos, devoluciones de llamada ActiveRecord o algo completamente diferente?
Gracias.
fuente
Respuestas:
Echa un vistazo a las preocupaciones
Cree una carpeta en su directorio de modelos llamada preocupaciones. Agregue un módulo allí:
A continuación, inclúyalo en los modelos en los que desea ejecutar after_save en:
Dependiendo de lo que esté haciendo, esto podría acercarlo sin observadores.
fuente
Están en un complemento ahora.
¿Puedo recomendar también una alternativa que le dará controladores como:
fuente
ActiveSupport::Notifications
están orientados a la instrumentación, no a los sub / pub genéricos.ActiveSupport::Notifications
?Notifications
mucho, pero diría queWisper
tiene una API más agradable y características como 'suscriptores globales', 'en prefijo' y 'mapeo de eventos' queNotifications
no lo hace. Una versión futura deWisper
también permitirá la publicación asincrónica a través de SideKiq / Resque / Celluloid. Además, potencialmente, en futuras versiones de Rails, la APINotifications
podría cambiar para centrarse más en la instrumentación.Mi sugerencia es leer la publicación del blog de James Golick en http://jamesgolick.com/2010/3/14/crazy-heretical-and-awesome-the-way-i-write-rails-apps.html (trate de ignorar cómo inmodesto suena el título).
En el pasado todo era "modelo gordo, controlador flaco". Luego, los modelos gordos se convirtieron en un dolor de cabeza gigante, especialmente durante las pruebas. Más recientemente, el impulso ha sido para modelos flacos: la idea es que cada clase debe manejar una responsabilidad y el trabajo de un modelo es mantener sus datos en una base de datos. Entonces, ¿dónde termina toda mi lógica empresarial compleja? En las clases de lógica de negocios: clases que representan transacciones.
Este enfoque puede convertirse en un atolladero (risas) cuando la lógica comienza a complicarse. Sin embargo, el concepto es sólido: en lugar de desencadenar cosas implícitamente con devoluciones de llamada u observadores que son difíciles de probar y depurar, desencadenar cosas explícitamente en una clase que superponga la lógica sobre su modelo.
fuente
El uso de devoluciones de llamada de registro activas simplemente cambia la dependencia de su acoplamiento. Por ejemplo, si tiene
modelA
un estilo de rieles deCacheObserver
observaciónmodelA
3, puede eliminarloCacheObserver
sin ningún problema. Ahora, en su lugar, digamos queA
tiene que invocar manualmente elCacheObserver
guardado posterior, que serían rieles 4. Simplemente ha movido su dependencia para que pueda eliminarla con seguridadA
pero noCacheObserver
.Ahora, desde mi torre de marfil, prefiero que el observador dependa del modelo que está observando. ¿Me importa lo suficiente como para desordenar mis controladores? Para mi, la respuesta es no.
Presumiblemente has pensado en por qué quieres / necesitas al observador, y así crear un modelo que dependa de su observador no es una tragedia terrible.
También tengo un disgusto (razonablemente fundamentado, creo) por cualquier tipo de observador que dependa de una acción del controlador. De repente, debe inyectar a su observador en cualquier acción del controlador (u otro modelo) que pueda actualizar el modelo que desea observar. Si puede garantizar que su aplicación solo modificará instancias a través de acciones de controlador de creación / actualización, más poder para usted, pero eso no es una suposición que haría sobre una aplicación de rieles (considere formas anidadas, asociaciones de actualización de lógica de negocios modelo, etc.)
fuente
Wisper es una gran solución. Mi preferencia personal para las devoluciones de llamada es que son activadas por los modelos, pero los eventos solo se escuchan cuando llega una solicitud, es decir, no quiero que se activen las devoluciones de llamada mientras configuro modelos en pruebas, etc. pero sí los quiero despedido cuando los controladores están involucrados. Esto es realmente fácil de configurar con Wisper porque puedes decirle que solo escuche eventos dentro de un bloque.
fuente
En algunos casos, simplemente uso la Instrumentación de soporte activo
fuente
Mi alternativa a Rails 3 Observers es una implementación manual que utiliza una devolución de llamada definida dentro del modelo pero que (como afirma Agmin en su respuesta anterior) "cambia la dependencia ... acoplamiento".
Mis objetos heredan de una clase base que permite registrar observadores:
(Por supuesto, en el espíritu de composición sobre la herencia, el código anterior podría colocarse en un módulo y mezclarse en cada modelo).
Un inicializador registra observadores:
Cada modelo puede definir sus propios eventos observables, más allá de las devoluciones de llamada básicas de ActiveRecord. Por ejemplo, mi modelo de usuario expone 2 eventos:
Cualquier observador que desee recibir notificaciones para esos eventos simplemente necesita (1) registrarse con el modelo que expone el evento y (2) tener un método cuyo nombre coincida con el evento. Como cabría esperar, múltiples observadores pueden registrarse para el mismo evento y (en referencia al segundo párrafo de la pregunta original) un observador puede observar eventos en varios modelos.
Las clases de observador NotificationSender y ProfilePictureCreator a continuación definen métodos para los eventos expuestos por varios modelos:
Una advertencia es que los nombres de todos los eventos expuestos en todos los modelos deben ser únicos.
fuente
Creo que el problema con la desaprobación de los observadores no es que los observadores fueran malos en sí mismos, sino que estaban siendo abusados.
Yo advertiría de no agregar demasiada lógica en sus devoluciones de llamada o simplemente mover el código para simular el comportamiento de un observador cuando ya hay una solución sólida a este problema, el patrón Observador.
Si tiene sentido usar observadores, entonces, por supuesto, use observadores. Solo comprenda que deberá asegurarse de que la lógica de su observador siga las prácticas de codificación de sonido, por ejemplo, SOLID.
La gema del observador está disponible en rubygems si desea agregarla nuevamente a su proyecto https://github.com/rails/rails-observers
vea este breve hilo, aunque no es una discusión exhaustiva completa, creo que el argumento básico es válido. https://github.com/rails/rails-observers/issues/2
fuente
Puede probar https://github.com/TiagoCardoso1983/association_observers . Todavía no se ha probado para los rieles 4 (que aún no se lanzó), y necesita más colaboración, pero puede verificar si funciona.
fuente
¿Qué tal usar un PORO en su lugar?
La lógica detrás de esto es que sus 'acciones adicionales en guardar' probablemente serán lógica de negocios. Esto me gusta mantenerme separado de los modelos AR (que deberían ser lo más simples posible) y los controladores (que son molestos de probar correctamente)
Y simplemente llámalo como tal:
Incluso podría ampliarlo inyectando objetos de acción adicionales posteriores al guardado
Y para dar un ejemplo de los 'extras'. Sin embargo, es posible que desee ampliarlos un poco:
Si le gusta este enfoque, le recomiendo una lectura de la publicación del blog Bryan Helmkamps 7 Patterns .
EDITAR: También debo mencionar que la solución anterior también permite agregar lógica de transacción cuando sea necesario. Por ejemplo, con ActiveRecord y una base de datos compatible:
fuente
Vale la pena mencionar que el
Observable
módulo de la biblioteca estándar de Ruby no se puede usar en objetos similares a registros activos, ya que los métodos de instanciachanged?
ychanged
chocarán con los deActiveModel::Dirty
.Informe de error para Rails 2.3.2
fuente
Tengo el mismo problema! ¡Encuentro una solución ActiveModel :: Dirty para que pueda seguir los cambios de su modelo!
http://api.rubyonrails.org/classes/ActiveModel/Dirty.html
fuente