M w MVC, testy jednostkowe i dobre nawyki

Witam,

Ostatnio mam sporo wątpliwości na temat poprawnego wykorzystania architektury MCV.
Wszelkie problemy najlepiej rozstrzygać posługując się przykładami, więc oto przykład:

Chcę mieć możliwość prostej moderacji użytkowników. Konta użytkowników mają status AKTYWNY, ZABLOKOWANY. Wszelkie akcje moderacji chcę przechowywać w logach (id moderatora, rodzaj zmiany, przyczyna). Dodatkowo przy zmianie statusu konta chcę wysyłać maila do użytkownika. Mam więc dwie tabele:

[code]# pseudo kod
Users {
id, login, passwd, …
email,
status
}

ModerationLogs {
is, user_id, moderator_id, …
status_from, status_to, reason
}[/code]
…i niewygodne pytanie:
Gdzie umieścić kod wykonujący wszystkie wspominanie wyżej czynności?

a/ w kontrolerze?
Kontroler zawiaduje wszystkimi czynnościami - sprawdza uprawnienia, za pomocą modeli wstawia do bazy odpowiednie dane.

A co w przypadku bardziej skomplikowanych operacji, które wymagają zapisania danych w kilku tabelach?
Może dojść do powstania bardzo przeładowanych kontrolerów.
Co jeśli kodu moderacji będziemy chcieli użyć w innym miejscu?

b/ w modelu User?
Kontroler sprawdza tylko uprawnienia moderatora.

W tym przypadku model User (np. funckja User::moderate(…)) będzie zapisywał status i tworzył wpisy w tabeli logów moderacji.
Czy dobrze napisany model może zapisywać/odczytywać dane z innych tabel?
Złamanie tej zasady może spowodować, że powstanie kod, w którym klasy modeli są bardzo mocno od siebie zależne.
Jak testować tak powstały kod?

dokumentacja api Ror -> ActiveRecord::Callbacks
Powinno w pełni zaspokoić Twoje potrzeby. :wink:

Przy okazji mając jeden model odpowiedzialny za logowanie, nie widzę problemu z delegowaniem do niego wywołań podczas akcji na innych modelach. Modele muszą ze sobą oddziaływać, na tym właśnie polega łącząca je aplikacja :slight_smile:

Oczywiście w modelu - kontroler zasadniczo służy do obsługi 2 czynności:

  • przyjmowania żądań użytkownika i przekazywania ich do modelu
  • przekazywania elementów modelu do widoków

[quote]b/ w modelu User?
Kontroler sprawdza tylko uprawnienia moderatora.[/quote]
Do czego? Wykonania jakiejś akcji w kontrolerze - tak. Cokolwiek innego - nie.

[quote]W tym przypadku model User (np. funckja User::moderate(…)) będzie zapisywał status i tworzył wpisy w tabeli logów moderacji.
Czy dobrze napisany model może zapisywać/odczytywać dane z innych tabel?[/quote]
Bezpośrednio - nie. Może jednak wywoływać metody innych elementów modelu, które przekładają się na zapisywanie/odczytywanie danych.

Zależy jakie testy chcesz przeprowadzać. Jeśli chodzi Ci o testy jednostkowe pojedynczych klas, to najlepiej wykorzystać moki (np. http://mocha.rubyforge.org/) dzięki którym możesz testować je w separacji.

W tym celu istnieją obserwatorzy (Observers). Tworzysz takowego i ten nasłuchuje zmian w określonych modelach i reaguje na nie - na przykład wysyłając maila czy zapisując coś do logów.

Dzięki temu User nie musi się zajmować logowaniem, bo to przecież nie jego odpowiedzialność. Jednocześnie kontrolery pozostają tak czyste jak być powinny.

Dokładnie to ActiveRecord::Observer można tu wykorzystać, ale callbacki też mogą być dobre, after_save itp. Ja bym tutaj w mockowanie się nie bawił. Ale jeśli już to ja korzystać w Flexmocka, całkiem ok. Ale nie próbowałem mocha, które poleca aphollo.