Da Observers offiziell aus Rails 4.0 entfernt wurden , bin ich gespannt, was andere Entwickler an ihrer Stelle verwenden. (Abgesehen von der Verwendung des extrahierten Edelsteins.) Während Beobachter sicherlich missbraucht wurden und manchmal leicht unhandlich werden konnten, gab es viele Anwendungsfälle außerhalb des Cache-Löschens, bei denen sie von Vorteil waren.
Nehmen Sie zum Beispiel eine Anwendung, die Änderungen an einem Modell verfolgen muss. Ein Beobachter kann problemlos nach Änderungen an Modell A suchen und diese Änderungen mit Modell B in der Datenbank aufzeichnen. Wenn Sie auf Änderungen in mehreren Modellen achten möchten, kann ein einzelner Beobachter damit umgehen.
In Rails 4 bin ich gespannt, welche Strategien andere Entwickler anstelle von Beobachtern verwenden, um diese Funktionalität wiederherzustellen.
Persönlich neige ich zu einer Art "Fat Controller" -Implementierung, bei der diese Änderungen in der Erstellungs- / Aktualisierungs- / Löschmethode jedes Controller-Modells verfolgt werden. Während es das Verhalten jedes Controllers leicht aufbläht, hilft es bei der Lesbarkeit und dem Verständnis, da sich der gesamte Code an einem Ort befindet. Der Nachteil ist, dass es jetzt Code gibt, der sehr ähnlich ist und auf mehrere Controller verteilt ist. Das Extrahieren dieses Codes in Hilfsmethoden ist eine Option, aber Sie haben immer noch Aufrufe für diese Methoden, die überall verstreut sind. Nicht das Ende der Welt, aber auch nicht ganz im Sinne von "Skinny Controllern".
ActiveRecord-Rückrufe sind eine weitere mögliche Option, die ich persönlich nicht mag, da sie meiner Meinung nach dazu neigen, zwei verschiedene Modelle zu eng miteinander zu verbinden.
Wenn Sie in der Rails 4-Welt ohne Beobachter einen neuen Datensatz erstellen müssten, nachdem ein anderer Datensatz erstellt / aktualisiert / zerstört wurde, welches Entwurfsmuster würden Sie verwenden? Fat Controller, ActiveRecord-Rückrufe oder etwas ganz anderes?
Danke dir.
quelle
Antworten:
Werfen Sie einen Blick auf Bedenken
Erstellen Sie in Ihrem Modellverzeichnis einen Ordner mit dem Namen "Anliegen". Fügen Sie dort ein Modul hinzu:
Fügen Sie als Nächstes Folgendes in die Modelle ein, in denen Sie after_save ausführen möchten:
Je nachdem, was Sie tun, können Sie sich ohne Beobachter nähern.
quelle
Sie sind jetzt in einem Plugin .
Kann ich auch eine Alternative empfehlen, die Ihnen Controller bietet wie:
quelle
ActiveSupport::Notifications
sind auf Instrumentierung ausgerichtet, nicht auf generisches Sub / Pub.ActiveSupport::Notifications
?Notifications
viel verwendet , aber ich würde sagen, dassWisper
es eine schönere API und Funktionen wie "globale Abonnenten", "Präfix" und "Ereigniszuordnung" gibt, dieNotifications
dies nicht tun. Eine zukünftige Version vonWisper
wird auch das asynchrone Veröffentlichen über SideKiq / Resque / Celluloid ermöglichen. Möglicherweise wird sich die API für zukünftige Rails-VersionenNotifications
ändern, um sich stärker auf die Instrumentierung zu konzentrieren.Mein Vorschlag ist, James Golicks Blog-Beitrag unter http://jamesgolick.com/2010/3/14/crazy-heretical-and-awesome-the-way-i-write-rails-apps.html zu lesen (versuchen Sie zu ignorieren, wie unbescheiden klingt der Titel).
Früher war alles "fettes Modell, dünner Controller". Dann wurden die fetten Modelle zu riesigen Kopfschmerzen, besonders beim Testen. In jüngerer Zeit wurden dünne Modelle vorangetrieben. Die Idee war, dass jede Klasse eine Verantwortung übernehmen sollte und die Aufgabe eines Modells darin besteht, Ihre Daten in einer Datenbank zu speichern. Wo also landet all meine komplexe Geschäftslogik? In Geschäftslogikklassen - Klassen, die Transaktionen darstellen.
Dieser Ansatz kann sich in einen Sumpf (Giggity) verwandeln, wenn die Logik kompliziert wird. Das Konzept ist jedoch solide - anstatt Dinge implizit mit Rückrufen oder Beobachtern auszulösen, die schwer zu testen und zu debuggen sind, lösen Sie Dinge explizit in einer Klasse aus, die Logik über Ihr Modell legt.
quelle
Durch die Verwendung aktiver Datensatzrückrufe wird einfach die Abhängigkeit Ihrer Kopplung umgedreht. Wenn Sie beispielsweise
modelA
einenCacheObserver
Beobachtungsschienen-modelA
3-Stil haben, können Sie diesen problemlos entfernenCacheObserver
. Jetzt muss say stattdessenA
manuell dasCacheObserver
After-Save aufrufen , was Rails 4 wäre. Sie haben einfach Ihre Abhängigkeit verschoben, damit Sie sie sicher entfernen können,A
aber nichtCacheObserver
.Von meinem Elfenbeinturm aus bevorzuge ich, dass der Beobachter von dem Modell abhängig ist, das er beobachtet. Interessiert es mich genug, meine Controller zu überladen? Für mich lautet die Antwort nein.
Vermutlich haben Sie sich Gedanken darüber gemacht, warum Sie den Beobachter wollen / brauchen, und daher ist es keine schreckliche Tragödie, ein Modell zu erstellen, das von seinem Beobachter abhängig ist.
Ich habe auch eine (meiner Meinung nach begründete) Abneigung gegen jede Art von Beobachter, der von einer Controller-Aktion abhängig ist. Plötzlich müssen Sie Ihren Beobachter in eine Controller-Aktion (oder ein anderes Modell) einbinden, die möglicherweise das Modell aktualisiert, das Sie beobachten möchten. Wenn Sie garantieren können, dass Ihre App Instanzen immer nur über Aktionen zum Erstellen / Aktualisieren von Controllern ändert, haben Sie mehr Leistung, aber das ist keine Annahme, die ich über eine Rails-Anwendung machen würde (berücksichtigen Sie verschachtelte Formulare, Modellzuordnungen zur Aktualisierung der Geschäftslogik usw.).
quelle
Wisper ist eine großartige Lösung. Meine persönliche Präferenz für Rückrufe ist, dass sie von den Modellen ausgelöst werden, die Ereignisse jedoch nur abgehört werden, wenn eine Anfrage eingeht, dh ich möchte nicht, dass Rückrufe ausgelöst werden, während ich Modelle in Tests usw. einrichte, aber ich möchte sie Wird ausgelöst, wenn Controller beteiligt sind. Dies ist mit Wisper sehr einfach einzurichten, da Sie festlegen können, dass nur Ereignisse innerhalb eines Blocks abgehört werden sollen.
quelle
In einigen Fällen verwende ich einfach Active Support Instrumentation
quelle
Meine Alternative zu Rails 3 Observers ist eine manuelle Implementierung, die einen im Modell definierten Rückruf verwendet und es dennoch schafft (wie in seiner obigen Antwort angegeben), die Abhängigkeit ... Kopplung umzudrehen.
Meine Objekte erben von einer Basisklasse, die die Registrierung von Beobachtern ermöglicht:
(Zugegeben, im Sinne der Komposition über die Vererbung könnte der obige Code in ein Modul eingefügt und in jedes Modell gemischt werden.)
Ein Initialisierer registriert Beobachter:
Jedes Modell kann dann über die grundlegenden ActiveRecord-Rückrufe hinaus seine eigenen beobachtbaren Ereignisse definieren. Zum Beispiel macht mein Benutzermodell zwei Ereignisse verfügbar:
Jeder Beobachter, der Benachrichtigungen für diese Ereignisse erhalten möchte, muss sich lediglich (1) bei dem Modell registrieren, das das Ereignis verfügbar macht, und (2) über eine Methode verfügen, deren Name mit dem Ereignis übereinstimmt. Wie zu erwarten ist, können sich mehrere Beobachter für dasselbe Ereignis registrieren, und (in Bezug auf den zweiten Absatz der ursprünglichen Frage) ein Beobachter kann über mehrere Modelle hinweg nach Ereignissen suchen.
Die folgenden Beobachterklassen NotificationSender und ProfilePictureCreator definieren Methoden für die Ereignisse, die von verschiedenen Modellen angezeigt werden:
Eine Einschränkung ist, dass die Namen aller Ereignisse, die in allen Modellen angezeigt werden, eindeutig sein müssen.
quelle
Ich denke, das Problem mit der Ablehnung von Beobachtern ist nicht, dass Beobachter an und für sich schlecht waren, sondern dass sie missbraucht wurden.
Ich würde davor warnen, Ihren Rückrufen zu viel Logik hinzuzufügen oder einfach Code zu verschieben, um das Verhalten eines Beobachters zu simulieren, wenn es bereits eine fundierte Lösung für dieses Problem gibt, das Observer-Muster.
Wenn es sinnvoll ist, Beobachter einzusetzen, sollten Sie auf jeden Fall Beobachter einsetzen. Verstehen Sie einfach, dass Sie sicherstellen müssen, dass Ihre Beobachterlogik den Soundcodierungspraktiken folgt, z. B. SOLID.
Das Observer Gem ist auf Rubygems verfügbar, wenn Sie es wieder zu Ihrem Projekt hinzufügen möchten: https://github.com/rails/rails-observers
siehe diesen kurzen Thread, obwohl nicht vollständige Diskussion Ich denke, das grundlegende Argument ist gültig. https://github.com/rails/rails-observers/issues/2
quelle
Sie können https://github.com/TiagoCardoso1983/association_observers ausprobieren . Es ist noch nicht für Rails 4 getestet (das noch nicht gestartet wurde) und erfordert etwas mehr Zusammenarbeit, aber Sie können überprüfen, ob es den Trick für Sie macht.
quelle
Wie wäre es stattdessen mit einem PORO?
Die Logik dahinter ist, dass Ihre "zusätzlichen Aktionen beim Speichern" wahrscheinlich Geschäftslogik sein werden. Dies möchte ich sowohl von AR-Modellen (die so einfach wie möglich sein sollten) als auch von Controllern (die lästig sind, um richtig zu testen) getrennt halten.
Und nennen Sie es einfach so:
Sie können es sogar erweitern, indem Sie zusätzliche Aktionsobjekte nach dem Speichern einfügen
Und um ein Beispiel für die "Extras" zu geben. Vielleicht möchten Sie sie aber ein bisschen aufpeppen:
Wenn Ihnen dieser Ansatz gefällt, empfehle ich Ihnen, den Blog-Beitrag von Bryan Helmkamps 7 Patterns zu lesen .
EDIT: Ich sollte auch erwähnen, dass die obige Lösung das Hinzufügen von Transaktionslogik bei Bedarf auch ermöglicht. ZB mit ActiveRecord und einer unterstützten Datenbank:
quelle
Es ist erwähnenswert, dass das
Observable
Modul aus der Ruby-Standardbibliothek seit Instanzmethoden nicht in Objekten verwendet werden kann, die wie ein aktiver Datensatz aussehen,changed?
undchanged
mit denen von zusammenstößtActiveModel::Dirty
.Fehlerbericht für Rails 2.3.2
quelle
Ich habe das gleiche Problem! Ich finde eine Lösung ActiveModel :: Dirty, damit Sie Ihre Modelländerungen verfolgen können!
http://api.rubyonrails.org/classes/ActiveModel/Dirty.html
quelle