Użytkownik komentuje wpis. Po zapisaniu, model wpisu uruchamia ater_save, w którym tworzę event z informacją kto i co. Po zapisaniu eventu, uruchamia się after_save, tym razem modelu event, który …
Zauważyłem, że coraz więcej mi się takich łańcuszków robi (jeden model tworzy w after_save drugi model, ktory w swoim after_save tworzy trzeci, albo cos w tym stylu). Na razie mam mały projekt, a już zaczynam się czasami w nich gubić, bo trzeba skakać po plikach i uważać, żeby nie zrobiła się gdzieś pętla. Zastanawiałem się czy nie lepiej np. zrobić to wszystko od razu w pierwszym modelu.
Dobrze, że już w tym momencie do tego doszedłeś To zależy od architektury ale moim zdaniem używanie filtrów gdzie się da to droga donikąd - w dużych projektach może się bardzo zemścić. Zastanów się, może jakieś wzorce przyjdą z pomocą
after_save to zdecydowanie nie jest miejsce na tego typu zabawy. Jeśli musisz stworzyć wiele powiązanych obiektów, to niech to się dzieje w osobnej klasie (może być to CommentService, ale ja bym to inaczej nazwał). Napisz testy dla różnych scenariuszy tworzenia commentów. No i pomyśl o pozbyciu się twardych odwołań do klas (typu Comment.create, Event.create, etc.) To Ci ułatwi testowanie.
To zabrzmi groźnie - dependency injection. Temat jest wałkowany również na naszym forum (ostatnio pisał o tym paneq w kontekście walidacji), ale żeby nie powtarzać rzeczy już powiedzianych, to polecam:
Avdi Grimm - Objects on Rails - solidne wprowadzenie do tego jak można dobrze pisać apkę Railsową korzystając z TDD
Dependor - framework DI dla Rubiego. Nie do końca jestem przekonany, że jest niezbędny, ale można popatrzeć na przykłady i samemu zdecydować.
A sam kod też mogę podać - ale bez odpowiedniego kontekstu to będzie za mało
end
end[/code]
Można tam oczywiście te parametry również przesunąć gdzieś do zmiennych za pomocą let. Ale to nie jest najważniejsze - najistotniejsze jest przekazanie odpowiednich fabryk np. w konstruktorze. Wtedy w testach możemy podmienić to co w realnej implementacji jest ustawione domyślnie na Comment i Event.
Ja niezbyt lubie (też nie próbowałem) używać dependency injection w takich przypadkach, jak powyższy, gdzie nie ma dużo logiki biznesowej, a jest tylko logika tworzenia obiektów. Dopiero gdy logika tworzenia obiektu(ów) robi się skomplilkowana, to tworzę klasy, które nie posiadają zależności od ActiveRecord.
A czym się różni jedno o drugiego? To że się jakiś event tworzy pododaniu komentarza to zdecydowanie logika biznesowa. Polecam poczytać Objects on Rails. Oczywiście wielu uzna, że to przerost formy nad treścią. Dopóki nie napotka systemu, w którym wprowadzenie zmiany będzie trudniejsze niż napisanie połowy systemu od nowa.
Jak dla mnie to chyba za wysokie progi na razie (a ta książka to już w ogóle, po kilku stronach niestety musiałem odpuścić). Rozumiem, że po prostu całą logikę obsługi tego co się dzieje przy tworzeniu nowego komentarza (inicjowanie nowych modeli, zapisanie ich itd.) wydzielamy poza model Comment.
Nie trzeba od razu tworzyć kodu w stylu Objects on Rails aby mieć czysty, łatwy do zmian kod.[/quote]
Tylko jak już przyjdzie na to czas, to trudno będzie to zrobić post factum.
Ze względu na prostotę Rubiego (domyślne parametry, klasy są od początku fabrykami), dodanie DI w takiej postaci jak w Object on Rails jest prawie bezkosztowe. Narzut polega głównie na użyciu @comment_factory.new zamianst Comment.new. Czy to jest aż taka różnica? IMHO nie. Czy ułatwia testowanie i zmiany? Bardzo!