Po oficjalnym usunięciu Observers z Rails 4.0 jestem ciekawy, czego używają inni programiści w ich miejsce. (Poza używaniem wydobytego klejnotu.) Podczas gdy obserwatorzy byli z pewnością wykorzystywani i czasami mogli łatwo stać się nieporęczni, było wiele przypadków użycia poza zwykłym czyszczeniem pamięci podręcznej, w których były one korzystne.
Weźmy na przykład aplikację, która musi śledzić zmiany w modelu. Obserwator może łatwo obserwować zmiany w Modelu A i rejestrować te zmiany w Modelu B w bazie danych. Jeśli chcesz obserwować zmiany w kilku modelach, pojedynczy obserwator może sobie z tym poradzić.
W Rails 4 jestem ciekaw, jakie strategie używają inni programiści zamiast Observers, aby odtworzyć tę funkcjonalność.
Osobiście skłaniam się ku pewnego rodzaju implementacji „grubego kontrolera”, w której te zmiany są śledzone w metodzie tworzenia / aktualizacji / usuwania kontrolera każdego modelu. Chociaż nieznacznie powiększa zachowanie każdego kontrolera, pomaga w czytelności i zrozumieniu, ponieważ cały kod znajduje się w jednym miejscu. Wadą jest to, że istnieje teraz bardzo podobny kod rozproszony w kilku kontrolerach. Wyodrębnienie tego kodu do metod pomocniczych jest opcją, ale nadal pozostają wszędzie wywołania tych metod. To nie koniec świata, ale też niezupełnie w duchu „chudych kontrolerów”.
Wywołania zwrotne ActiveRecord to kolejna możliwa opcja, chociaż osobiście nie lubię, ponieważ moim zdaniem ma tendencję do łączenia dwóch różnych modeli zbyt blisko siebie.
Więc w świecie Rails 4, no-Observers, gdybyś musiał stworzyć nowy rekord po utworzeniu / aktualizacji / zniszczeniu innego rekordu, jakiego wzorca projektowego byś użył? Grube kontrolery, wywołania zwrotne ActiveRecord czy coś zupełnie innego?
Dziękuję Ci.
źródło
Odpowiedzi:
Spójrz na obawy
Utwórz folder w katalogu modeli o nazwie obawy. Dodaj tam moduł:
Następnie uwzględnij to w modelach, w których chcesz uruchomić after_save:
W zależności od tego, co robisz, może to Cię zbliżyć bez obserwatorów.
źródło
Są teraz we wtyczce .
Czy mogę również polecić alternatywę, która da ci kontrolery takie jak:
źródło
ActiveSupport::Notifications
są nastawione na instrumentację, a nie ogólne sub / pub.ActiveSupport::Notifications
?Notifications
dużo, ale powiedziałbym, żeWisper
ma ładniejszy interfejs API i funkcje, takie jak „globalni subskrybenci”, „na prefiksie” i „mapowanie zdarzeń”, któreNotifications
tego nie robią. Przyszłe wydanieWisper
umożliwi również asynchroniczne publikowanie za pośrednictwem SideKiq / Resque / Celluloid. Potencjalnie w przyszłych wydaniach Railsów API dlaNotifications
może się zmienić, aby bardziej skupić się na instrumentacji.Proponuję przeczytać post na blogu Jamesa Golicka pod adresem http://jamesgolick.com/2010/3/14/crazy-heretical-and-awesome-the-way-i-write-rails-apps.html (spróbuj zignorować jak nieskromnie brzmiący tytuł).
Kiedyś był to „gruby model, chudy kontroler”. Wtedy modele tłuszczu stały się gigantycznym bólem głowy, szczególnie podczas testów. Niedawno nacisk padł na chude modele - idea jest taka, że każda klasa powinna odpowiadać za jedną odpowiedzialność, a zadaniem modelu jest utrwalanie danych w bazie danych. Więc gdzie kończy się cała moja złożona logika biznesowa? W klasach logiki biznesowej - klasy reprezentujące transakcje.
Takie podejście może zamienić się w grzęzawisko (giggity), gdy logika zacznie się komplikować. Koncepcja jest jednak rozsądna - zamiast wywoływać rzeczy niejawnie za pomocą wywołań zwrotnych lub obserwatorów, które są trudne do przetestowania i debugowania, wyzwalaj rzeczy jawnie w klasie, która nakłada logikę na model.
źródło
Korzystanie z wywołań zwrotnych aktywnego rekordu po prostu odwraca zależność twojego sprzężenia. Na przykład, jeśli masz
modelA
iCacheObserver
obserwującmodelA
styl szyn 3, możesz usunąćCacheObserver
bez problemu. Teraz zamiast tego powiedz, żeA
musi ręcznie wywołaćCacheObserver
po zapisaniu, co byłoby rails 4. Po prostu przeniosłeś swoją zależność, więc możesz bezpiecznie usunąć,A
ale nieCacheObserver
.Teraz z mojej wieży z kości słoniowej wolę, aby obserwator był zależny od modelu, który obserwuje. Czy obchodzi mnie na tyle, aby zaśmiecać kontrolery? Dla mnie odpowiedź brzmi: nie.
Przypuszczalnie zastanawiałeś się, dlaczego chcesz / potrzebujesz obserwatora, a zatem stworzenie modelu zależnego od obserwatora nie jest straszną tragedią.
Mam również (jak sądzę rozsądną) wstręt do jakiegokolwiek obserwatora zależnego od działania kontrolera. Nagle musisz wstrzyknąć obserwatorowi dowolną akcję kontrolera (lub inny model), która może zaktualizować model, który chcesz obserwować. Jeśli możesz zagwarantować, że twoja aplikacja będzie modyfikować instancje tylko poprzez tworzenie / aktualizowanie akcji kontrolera, więcej mocy dla ciebie, ale nie jest to założenie, które bym poczynił w odniesieniu do aplikacji railsowej (rozważ zagnieżdżone formularze, modelowanie logiki biznesowej aktualizujących powiązania itp.)
źródło
Wisper to świetne rozwiązanie. Moje osobiste preferencje dotyczące wywołań zwrotnych są takie, że są one uruchamiane przez modele, ale zdarzenia są odsłuchiwane tylko wtedy, gdy przychodzi żądanie, tj. Nie chcę, aby wywołania zwrotne były uruchamiane podczas konfigurowania modeli w testach itp., Ale chcę je uruchamiane za każdym razem, gdy w grę wchodzą kontrolerzy. Jest to naprawdę łatwe do skonfigurowania za pomocą Wispera, ponieważ możesz nakazać mu słuchanie tylko wydarzeń wewnątrz bloku.
źródło
W niektórych przypadkach po prostu używam Active Support Instrumentation
źródło
Moją alternatywą dla Rails 3 Observers jest ręczna implementacja, która wykorzystuje wywołanie zwrotne zdefiniowane w modelu, ale udaje się (jak stwierdza agmin w swojej odpowiedzi powyżej) "odwrócić zależność ... sprzężenie".
Moje obiekty dziedziczą z klasy bazowej, która umożliwia rejestrację obserwatorów:
(To prawda, w duchu kompozycji zamiast dziedziczenia, powyższy kod można umieścić w module i mieszać w każdym modelu).
Inicjator rejestruje obserwatorów:
Każdy model może następnie zdefiniować własne obserwowalne zdarzenia, poza podstawowymi wywołaniami zwrotnymi ActiveRecord. Na przykład mój model użytkownika ujawnia 2 zdarzenia:
Każdy obserwator, który chce otrzymywać powiadomienia o tych zdarzeniach, musi jedynie (1) zarejestrować się w modelu, który ujawnia zdarzenie i (2) mieć metodę, której nazwa odpowiada zdarzeniu. Jak można by się spodziewać, wielu obserwatorów może zarejestrować się na to samo zdarzenie, a (w odniesieniu do drugiego akapitu pierwotnego pytania) obserwator może obserwować zdarzenia w kilku modelach.
Poniższe klasy obserwatorów NotificationSender i ProfilePictureCreator definiują metody dla zdarzeń udostępnianych przez różne modele:
Jedynym zastrzeżeniem jest to, że nazwy wszystkich zdarzeń ujawnionych we wszystkich modelach muszą być niepowtarzalne.
źródło
Myślę, że problem deprecjonowania Observerów nie polega na tym, że obserwatorzy byli źli sami w sobie, ale to, że byli wykorzystywani.
Przestrzegałbym przed dodawaniem zbyt dużej ilości logiki do wywołań zwrotnych lub po prostu przenoszeniem kodu w celu symulacji zachowania obserwatora, gdy istnieje już rozsądne rozwiązanie tego problemu we wzorcu obserwatora.
Jeśli ma sens korzystanie z obserwatorów, to z całą pewnością używaj obserwatorów. Po prostu zrozum, że będziesz musiał upewnić się, że logika obserwatora jest zgodna z rozsądnymi praktykami kodowania, na przykład SOLID.
Klejnot obserwatora jest dostępny w rubygems, jeśli chcesz go dodać z powrotem do swojego projektu https://github.com/rails/rails-observers
zobacz ten krótki wątek, chociaż nie jest to pełna wyczerpująca dyskusja, myślę, że podstawowy argument jest słuszny. https://github.com/rails/rails-observers/issues/2
źródło
Możesz spróbować https://github.com/TiagoCardoso1983/association_observers . Nie jest jeszcze testowany pod kątem szyn 4 (które nie zostały jeszcze uruchomione) i wymaga dalszej współpracy, ale możesz sprawdzić, czy to załatwia sprawę.
źródło
A może zamiast tego użyć PORO?
Logika stojąca za tym jest taka, że twoje „dodatkowe akcje przy zapisywaniu” będą prawdopodobnie logiką biznesową. To lubię trzymać oddzielnie od obu modeli AR (które powinny być tak proste, jak to tylko możliwe) i kontrolerów (których testowanie jest kłopotliwe)
I po prostu nazwij to tak:
Można go nawet rozszerzyć, wstrzykując dodatkowe obiekty akcji po składowaniu
I żeby podać przykład „dodatków”. Możesz jednak trochę je podrasować:
Jeśli podoba Ci się to podejście, polecam przeczytanie wpisu na blogu Bryana Helmkampsa 7 Patterns .
EDYCJA: Powinienem również wspomnieć, że powyższe rozwiązanie pozwala na dodanie logiki transakcji również w razie potrzeby. Np. Z ActiveRecord i obsługiwaną bazą danych:
źródło
Warto wspomnieć, że
Observable
moduł ze standardowej biblioteki Ruby nie może być używany w obiektach typu active-record, ponieważ metody instancjichanged?
ichanged
będą kolidować z tymi zActiveModel::Dirty
.Raport o błędzie dla Railsów 2.3.2
źródło
Mam ten sam problem! Znajduję rozwiązanie ActiveModel :: Dirty, abyś mógł śledzić zmiany swojego modelu!
http://api.rubyonrails.org/classes/ActiveModel/Dirty.html
źródło