Asocjacje polimorficzne [z cyklu: działa ale wydaje mi się że źle]

Cześć,
Zaczynam zabawe z asocjacjami polimorficznymi, oto jej skutki:
http://pastie.org/389138
Chodzi o to że mam na stronie filmy i zdjęcia które można dodawać do “ulubionych”, chcę by ficzer ten obsłużył jeden kontroler. To co napisałem działa, jednak myślę że nie jest to najlepsze rozwiązanie mojego problemu. Proszę was o wskazówki jak powinienem to wszystko napisać. Z góry dziękuje za wszystkie porady.

Pozdrawiam,
Ernest.

ale jaki masz problem dokładnie?

Uważam że jest to źle napisane, że wymyślam gdzieś koło od nowa, itd. Proszę was o sugestie co tu poprawić :slight_smile:

Hej,
ja bym nie definiował metod fav i unfav, tylko użył REST-a, wtedy masz new oraz destroy, których możesz użyć.
Myślę, ze możesz zdefiniować w modelu metody add_to_favorites i remove_from_favorites. Twój kod w kontrolerze jest dość długi. No i mając wszystko w modelu możesz napisać łatwo testy, więc masz pewność, że Ci wszystko działa.

PS.
http://github.com/joergbattermann/acts_as_favorite/tree/master

Ale co do pluginów, to są osoby, które twierdzą, że używanie do wszystkiego pluginów nie jest właściwe. No i czasem pluginy potrafią robić zamieszanie i problemy.

No plugin jest fajny, będe się na nim wzorował, gdyby ktoś mógł wskazać kretynizmy w moim wcześniejszym kodzie, byłbym wdzięczny.

Tak na dzień dobry: masz polimorfika tylko z jednej strony, czyli modelu belongs_to. Przyda Ci się (i skróci kod) zdefiniowanie polimorficznej asocjacji “z drugiej strony”, czyli has_one/has_many :as => :favouritable. Patrz:
http://wiki.rubyonrails.org/rails/pages/UnderstandingPolymorphicAssociations

Od samego przeczytania powinny Ci przyjść do głowy pomysły na uproszczenie tego kodu (ODCHUDŹ KONTROLER! :wink: ) :slight_smile:

Polecaliście mi na forum użycie rest, zrobienie akcji create i destroy. Tak też uczyniłem, ale nie wiem co dalej ;), oto efekt:
http://pastie.org/389836

Z góry dzięki za pomoc

Zostaw na razie REST w spokoju, Sławosz dał Ci radę typu “jak nie działa Ci samochód, zmień drzwi do piwnicy”. Jedna rzecz na raz - skoro uczysz się obsługi modeli i polimorficznych asocjacji, to na tym się skup. Oldschoolowe :action, :controller naprawdę nie jest złe, a RESTa zdążysz się nauczyć przy innej okazji.

Ok, zrobiłem to tak jak uważam za poprawne: http://pastie.org/390047 może być?

Kilka sugestii przed pójściem spać :wink:

  1. Nie podałeś kodu zamieszanych modeli, ale zakładam że jest właściwy.

Dalsze punkty dotyczą odchudzania FavoritesController:

  1. Określanie typu faworytki: kod z linijek 8-11 możesz zamienić na:
    @type = Kernel.const_get(params[:type].capitalize).find(params[:id])
    1a) koniecznie poczytaj co robi Kernel.const_get i nawet nie myśl o zamianie tego na eval()
    1b) poczytaj o polymorphic routes, podobnie a bardziej elegancko

  2. funkcjonalność linijek 13-19 wywal do modelu tak, żeby można było zrobić
    flash[:notice] = current_user.add_or_remove_favorite(@type)

To tylko przykład refaktoryzacji; w każdym razie odchudzamy kontroler i uelastyczniamy kod aplikacji, rajt? :slight_smile:

  1. nie podoba mi się idea ładowania funkcjonalności zarówno dodawnania, jak i usuwania faworytki do tej samej metody kontrolera; takie decyzje powinny być podejmowane / podsuwane użytkownikowi na podstawie widoku, a jeśli “chakiersko” podmieni ścieżkę, niech się po prostu nic nie dzieje

  2. linijka 10 “else break” - no prośba :wink:
    4a) find i tak rzuci wyjątkiem jeśli nie znajdzie danego elementu - oczywiście gdzieś (ApplicationController) przechwytujesz (rescue) wyjątek ActiveRecord::RecordNotFound i wyświetlasz stronę typu “nie znaleziono elementu”, prawda? :wink:
    4b) Kernel.const_get też wypluje wyjątek jeśli nie znajdzie stałej (w tym przypadku klasy) o podanej w params[:type] nazwie
    4c) zawsze możesz (tu nawet powinieneś) po prostu zrobić pod koniec akcji/metody (przed end zamykającym definicję metody)
    rescue
    render :text => “wystąpił błąd przy dodawaniu faworytki”

dobranoc :slight_smile:

Dzięki Tomash za porady, zrobiłem tak jak polecasz. Mam pytanie odnośnie pkt.3, masz na myśli że powinny być 2 funkcje (unfav i fav) a nie jedna?

Odpowiem za Tomasha - tak :slight_smile:

W niektórych aplikacjach widać dziwną tendencję do łączenia 2 akcji w jedną, na przykład zamiast new i create jest tylko new, która w zależności od tego czy to get czy post działa jak new albo create. Czasami widzę pójście nawet dalej - akcje new i edit są łączone.

Ja rozumiem, że niektórym może się wydawać, że zrobienie z 4 metod jednej może uprościć kod, ale to jest myślenie całkowicie błędne. Po pierwsze ciężko jest wtedy zrobić specyficzne dla każdej akcji respond_to, dużo ciężej jest debugować, kod jest tak naprawdę bardziej zawiły, bo dochodzi trochę ifów i niestandardowych zachować. Strasznie tego nie lubię.

Szczególnie, że jak podchodzę do czyjejś aplikacji, to nie znam kodu i zamiast mieć coś co jest standardem dostaję jakieś widzimisię programisty, który uważa, że to jest niesamowicie przebiegłe, że wszystko upchnął do jednej metody.

Polecam przy okazji prezentację Marcela Moliny - Beautiflu code (albo coś w tym stylu), w której pokazuje kawałek swojego kodu, który może się z początku wydawać sprytnie napisany, ale po przeanalizowaniu wychodzi na to, że tak naprawdę zamiast jakieś dziwnej struktury wystarczy zastosować jednego switcha, który zrobi to o wiele szybciej, wydajniej i nawet początkujący zrozumie o co chodzi.

+1 do tego co napisał Drogomir :slight_smile:

Ok ;), proszę mi powiedzieć czy następujący kontroler i trasy są dobrze zrefaktoryzowane ;):
http://pastie.org/392247
dzięki!

Ależ babol w linijce 24. Podwójny end na końcu nie wzbudził Twoich podejrzeń?

Poza tym to nieźle :slight_smile: