Helpery i modele

Hej.
Mam małe/szybkie pytanie. Czy istnieją jakieś przeciwzkazania przed używaniem modeli w helperach? Z jednej strony jest taka możliwość, wszystko ładnie dziala. Ale z drugiej strony, definicyjnie, helpery mają za zadanie rozszerzać funkcjonalność szablonu. Nie gryzie się to z MVC? Sorki za takie n00b pytanie.

Kawałek kodu dla przykładu

[code]module TasksHelper

def deadline_info(deadline,id)

distance = distance_of_time_in_words(Time.now, deadline)

done = Goal.count(["task_id = ? and status = ?", id, 1]) 
undone = Goal.count(["task_id = ? and status = ?", id, 0])              

allGoals = Float(done) + Float(undone)

if allGoals != 0.00 
 
 percent = (Float(done) / allGoals) * 100 
 
else 

  percent = "0"

end

if deadline > Time.now  
	message = "#{distance} to deadline"                           
  deadlineSpan = "deadlineRed"
end

if Time.now > deadline
message = “#{distance} after deadline”
end

if percent == 100.00

  deadlineSpan = "deadlineGreen"               

end

if percent == 0.00

percent = "0"

end

deadline = deadline.strftime("%H:%M %d-%m-%Y")

end

end[/code]

Teoretycznie się gryzie, bo miejscem w którym powinny być pobierane dane z modelu to kontroler. Ale w praktyce wszyscy to ignorują włącznie z samym DHH. Mimo, że RoR jest dosyć mocno związany ze wzorcem MVC to ma pewne luki pozwalające na rozluźnienie zasad tego paradygmatu. Np. szablony mogą teoretycznie robić wszystko, bo w środku jest dostępny pełny język Ruby. No i ludzie to trochę rozluźniają, bo wzorzecz MVC jest czasami zbyt ograniczający. Np. czasami chciałoby się potworzyć komponenty, które by się prosto zestawiało ze sobą . Niestety RoR nie zaleca używania swoich komponentów, bo te co ma są beznadziejnie źle zaimplementowane (są b. wolne). Z kolei pluginy nie są komponentami, bo nie mają warstwy prezentacyjnej (są bardziej odpowiednikiem djangowych genericsów). Chyba jedynym odpowiednikiem komponentów są Rails Engines, bo łączą to co mają pluginy z możliwością przypięcia warstwy prezentacji.

Ech, chciałoby się mieć prosty, szybki i elastyczny mechanizm komponentów Myghty jakie używa Pylons. Railsowe komponenty nie dorastają im do pięt. Pylons pozwala na indywidualne i b. elastyczne ustawienie kesza dla każdego komponentu i pozwala na komunikacje między nimi z pominięciem URL. Oczywiście Pylons umie sam czyścić cache i ustawiać czas jego trwania (tego RoR w ogóle nie potrafi). No i szybkość. Pylonsowe komponenty zabijają te nieudaczne, railsowe. Może zamiast je wywalać, by tak poprawili ich implementację?

Tak mi się przypomniało, że partials mogą “od biedy” robić za komponenty. (Nie wiem wtedy w ogóle po co komu Rails Engines?) Przecież partialom można przekazywać parametry formalne. Swoją logikę mogą mieć wyniesioną do helpera. Kiedyś pisałem podobną metodą w pehapowych Smarty. Niestety szybko zaprzestałem z powodu ograniczeń PHP. Np. nie mogłem w pętli includować szablonów które miały dołączoną logikę z definicją klas. PHP wywalał się na konflikcie nazw funkcji (bo nie ma zaimplementowanych przestrzeni nazw).

Szkoda, że RoR nie pozwala na przekazywanie parametrów formalnych dla

render :template => ‘jakis’, :locals => {:msg => ‘hello’}

czy

render_to_string :action => ‘jakas’, :locals => {:msg => ‘hello’}

Można to używać tylko w kontekście szablonów cząstkowych

render :partial => ‘wstawka’, :locals => {:msg => ‘hello’}

To jest jakaś niekonsekwencja Railsów. Niby dlaczego nie mogę przekazywać parametrów formalnych do szablonów? I żeby było gorzej, metoda render_to_string jest wciąż spieprzona. Zmienia globalnie nagłóweki HTTP co wyklucza ją z wielu sensownych zastosowań. Jak wysłałem im (4 miesiące temu!) patcha aby to poprawili, tak do tej pory jest to olane.

Dzięki za obszerną odpowiedź.

Mój problem polega na tym że potrzebuję wygenerować te same dane/html w dwóch różnych kontrollerach i chciałem uniknąć powtarzania kodu.
Chyba zrobie tak że zrobię z tego helpera metodę w kontollerze odpowiedzialnym za “Goals” i będę “includował” sobie do szablonów wynik jej działania za pomocą:
<%= render_component(:controller => “goal”, :action => “deadline”, :id => @task.id) %>

Chyba najlepsze sposob na unikniecie komponentow: http://www.igvita.com/blog/2006/09/17/helpers-instead-of-components/.
Dla mnie rewelacja (tu jest wlasnie logika w helperze … i co z tego, wlasnie do tego sluzy aby pobrac odpowiednie dane do zmiennych i yield to tamplata), aby uniknac komponentow walczylem ze wszystkimi before_filter, metodami kontrolera dodatkowo when_fragment_expired (timed_fragment_cache plugin) i zaczyal byc niebywaly syf - wszystko pozmienialem i wreszcie jestem zadowolony.

No właśnie tak czytam, czytam że komponenty to wszelkie zło. Czyli są 2 rozwiązania:

  1. Helpery z modelami
  2. Partiale

Tylko właśnie niedokońca rozumiem jak partiale mają być “dobrymi” zamiennikami komponentów. Bo jeśli renderuję partiala to muszę do niego przekazać dane. W sytuacji gdy 2 różne metody mają wyświetlić tego samego partiala muszę 2 razy przekazać dane. Gdzie tu DRY?

[quote=Maslav]No właśnie tak czytam, czytam że komponenty to wszelkie zło. Czyli są 2 rozwiązania:

  1. Helpery z modelami

  2. Partiale[/quote]
    Helpery z modelami?? Bez sensu. Co to ma wspólnego z komponentem? Są 4 możliwe metody:

  3. Stare komponenty (wolne i passe)

  4. Rails Engines (najbardziej zaawansowane)

  5. Szablony z helperami

  6. Podszablony z helperami

Jeśli to jest jest komponent którego parametry są ustalane automatycznie (na podst. sesji, parametru z url itp) to w ApplicationController dodaj filtr before_filter do metody która wstawi do zmiennej instancji zrenderowany szablon (render_to_string) i potem osadzaj tylko ta zmienną w szablonach czy layoutach. Czyli zachodzi tu przypadek 3.

W wypadku kiedy osadzasz szablon-komponent w szablonie, to masz 2 możliwości. Pkt 3 i <%= @moj_zrenderowany_szablon %> lub <%= render :partial => “shared/moj_podkomponent” %>

Wadą pkt.3 jest to że Railsy nie umieją przekazywać parametrów formalnych do szablonów (strasznie wkurzające) i wszystko trzeba pchać przez zmienne instancji zanieczyszczając przestrzeń nazw (te zmienne widzi każdy inny szablon i kontroler)