Czy w modelach można używać pomocników routingu Rails (tj. Mymodel_path (model))?

368

Powiedz, że mam model szyny o nazwie Thing. Rzecz ma atrybut url, który można opcjonalnie ustawić na adres URL gdzieś w Internecie. W widoku kodu potrzebuję logiki, która wykonuje następujące czynności:

<% if thing.url.blank? %>
<%= link_to('Text', thing_path(thing)) %>
<% else %>
<%= link_to('Text', thing.url) %>
<% end %>

Ta logika warunkowa w widoku jest brzydka. Oczywiście mógłbym zbudować funkcję pomocnika, która zmieniłaby widok na to:

<%= thing_link('Text', thing) %>

To rozwiązuje problem z gadatliwością, ale naprawdę wolałbym mieć funkcjonalność w samym modelu. W takim przypadku kod widoku to:

<%= link_to('Text', thing.link) %>

To oczywiście wymagałoby metody link w modelu. Oto, co powinien zawierać:

def link
  (self.url.blank?) ? thing_path(self) : self.url
end

Do rzeczy pytania rzecz_path () jest niezdefiniowaną metodą w kodzie modelu. Zakładam, że możliwe jest „wciągnięcie” niektórych metod pomocniczych do modelu, ale jak? Czy istnieje prawdziwy powód, dla którego routing działa tylko na kontrolerze i wyświetla warstwy aplikacji? Mogę wymyślić wiele przypadków, w których kod modelu może wymagać obsługi adresów URL (integracja z systemami zewnętrznymi itp.).

Aaron Longwell
źródło
Przykładem użycia może być: wygenerowanie skróconego adresu URL z goo.gl w aftersave,
lulalala
2
Prawdopodobnie powinieneś owinąć swój model w prezentera, jeśli chcesz dodać logikę widoku, dzięki temu warstwy MVC zostaną rozdzielone. Zobacz Draper ( github.com/jcasimir/draper ).
Kris,
2
Zobacz także sekcję „Generowanie adresów URL dla nazwanych tras” w dokumentacji pod adresem api.rubyonrails.org/classes/ActionDispatch/Routing/UrlFor.html
Backo

Odpowiedzi:

698

W Railsach 3, 4 i 5 możesz użyć:

Rails.application.routes.url_helpers

na przykład

Rails.application.routes.url_helpers.posts_path
Rails.application.routes.url_helpers.posts_url(:host => "example.com")
Paul Horsfall
źródło
14
Ypu może włączyć to do dowolnego modułu, a następnie uzyskać dostęp do pomocników URL. Dzięki
Wiaczesław Molokow
41
Oszczędź sobie od kopiowania :hostopcji wszędzie i ustaw ją raz w plikach konfiguracyjnych swojego środowiska:Rails.application.routes.default_url_options[:host] = 'localhost:3000'
Andrew
5
include Rails.application.routes.url_helpersdziała na mnie w Rails 4.1
Jan
1
Jeśli potrzebujesz tylko ścieżki, możesz użyć: only_path => true jako opcję bez przekazywania parametru hosta.
trueinViso,
1
wydaje się to dobra opcja: hawkins.io/2012/03/…
Renars Sirotins
182

Znalazłem odpowiedź dotyczącą tego, jak to zrobić samodzielnie. W kodzie modelu po prostu wstaw:

Dla szyn <= 2:

include ActionController::UrlWriter

W przypadku szyn 3:

include Rails.application.routes.url_helpers

To w magiczny sposób thing_path(self)zwraca adres URL bieżącej rzeczy lub other_model_path(self.association_to_other_model)inny adres URL.

Aaron Longwell
źródło
27
Tylko aktualizacja, ponieważ powyższe jest przestarzałe. To jest obecny obejmują:include Rails.application.routes.url_helpers
yalestar
2
Dostaję NoMethodError (niezdefiniowana metoda `optimize_routes_generation? 'Dla # <ActionView :: Base: 0x007fe8c0eecbd0>), gdy próbuję tego
moger777
118

Może się również okazać, że następujące podejście jest czystsze niż w przypadku każdej metody:

class Thing
  delegate :url_helpers, to: 'Rails.application.routes' 

  def url
    url_helpers.thing_path(self)
  end
end
matthuhiggins
źródło
7
Dokumentacja na ten temat wydawała się trudna do znalezienia, więc tutaj jest odpowiedni link dotyczący korzystania z delegata w ActiveSupport. simonecarletti.com/blog/2009/12/inside-ruby-on-rails-delegate
tlbrack 30.09.11
2
Pojawia się undefined local variable or method 'url_helpers' for Event:Classbłąd ... :(
Augustin Riedinger
I dostać undefined method url_helpers. Co ja zrobię
asiniy,
@asiniy, czy jesteś pewien, że użyłeś opcji delegowania do url_helpers zapisanych po deklaracji klasy?
Krzysztof Witczak
Nie działa, ale może się zdarzyć, że działa to tylko wtedy, gdy utworzysz własny class, jak pokazano w odpowiedzi. Jeśli Twoja klasa Modelu już się rozszerza, < ApplicationRecordto nie zadziała?
skplunkerin
13

Każda logika związana z tym, co jest wyświetlane w widoku, powinna zostać przekazana do metody pomocniczej, ponieważ metody w modelu służą wyłącznie do obsługi danych.

Oto, co możesz zrobić:

# In the helper...

def link_to_thing(text, thing)
  (thing.url?) ? link_to(text, thing_path(thing)) : link_to(text, thing.url)
end

# In the view...

<%= link_to_thing("text", @thing) %>
Josh Delsman
źródło
1
Czego nie lubię w metodach pomocniczych w tym przypadku: Kiedy patrzę na link_to_thing (), muszę zdecydować, czy jest to pomocnik specyficzny dla konkretnej rzeczy czy dla całej aplikacji (łatwo może to być albo). Muszę rozważyć sprawdzenie 2 plików dla źródła. thing.link nie pozostawia pytań o plik źródłowy.
Aaron Longwell,
Ponadto, co jeśli potrzebuję użyć tej funkcji w zadaniu Rake (aby wyeksportować plik CSV adresów URL rzeczy) ... przejście bezpośrednio do modelu byłoby również znacznie lepsze w tym przypadku.
Aaron Longwell,
Różnica polega jednak na tym, że link_to nie byłby dostępny w modelu, ponieważ jest on pomocnikiem ActionView. To nie zadziałałoby. Możesz zhakować niektóre domyślne wartości atrybutów w modelu, więc jeśli nie jest ustawiony, domyślnie coś zmienia, ale to zależy od ciebie.
Josh Delsman,
12
Wraz z rozwojem interfejsów API usług internetowych modele często muszą kontaktować się z zasobami zewnętrznymi z własnymi danymi i podawać adresy URL wywołania zwrotnego. Na przykład obiekt zdjęcia musi opublikować się w serwisie Socialmod, który będzie wywoływał adres URL tego zdjęcia po przeprowadzeniu moderacji. Hak after_create () miałby sens, aby opublikować w Socialmod, ale skąd znasz adres URL wywołania zwrotnego? Myślę, że własna odpowiedź autora ma sens.
JasonSmith
3
Chociaż masz rację w ściśle technicznym sensie, są chwile, kiedy ludzie po prostu potrzebują rzeczy do pracy bez konieczności golenia każdego jaka jest, aby uniknąć naruszenia jakiegoś świętego wzoru lub tego, co masz, być może potrzebują adresu URL i faktycznie przechowują przydałby się w bazie danych, gdyby model wiedział, jak to zrobić, zamiast przekazywać rzeczy tam iz powrotem, czasem reguły mogą zostać złamane.
nitekoder
1

Chociaż może istnieć sposób, aby nie dopuścić do tego rodzaju logiki poza modelem. Zgadzam się, że nie powinieneś umieszczać tego w widoku ( utrzymuj go chudego ), ale chyba że model zwraca adres URL jako kawałek danych do kontrolera, rzeczy związane z routingiem powinny znajdować się w kontrolerze.

Ryan Montgomery
źródło
4
Re: „O ile model nie zwraca adresu URL jako fragmentu danych”. Dokładnie tak się dzieje tutaj ... Model „posiada” ten kawałek danych, który jest albo linkiem do adresu URL w witrynie, albo poza nią. W niektórych przypadkach adres URL jest generowany z Rails Routing. W innych przypadkach adres URL to dane dostarczone przez użytkownika.
Aaron Longwell,
0

(Edycja: Zapomnij o mojej poprzedniej paplaninie ...)

Ok, mogą zdarzyć się sytuacje, w których możesz pójść albo do modelu, albo do innego adresu URL ... Ale tak naprawdę nie sądzę, że to należy do modelu, widok (a może model) brzmi bardziej odpowiedni.

O trasach, o ile wiem, trasy służą do akcji w kontrolerach (które zwykle „magicznie” używają widoku), a nie bezpośrednio do widoków. Administrator powinien obsługiwać wszystkie żądania, widok powinien prezentować wyniki, a model powinien obsługiwać dane i podawać je do widoku lub kontrolera. Słyszałem tutaj wiele osób mówiących o trasach do modeli (do tego stopnia, że ​​prawie to uwielbiam), ale jak rozumiem: trasy idą do kontrolerów. Oczywiście wiele kontrolerów jest kontrolerami dla jednego modelu i jest często nazywane <modelname>sController(np. „UsersController” jest kontrolerem modelu „User”).

Jeśli zauważysz, że piszesz nieprzyjemne ilości logiki w widoku, spróbuj przenieść logikę w bardziej odpowiednie miejsce; logika żądania i komunikacji wewnętrznej prawdopodobnie należy do kontrolera, logika związana z danymi może być umieszczona w modelu (ale nie logika wyświetlania, która zawiera tagi łącza itp.), a logika, która jest czysto związana z wyświetlaniem, zostanie umieszczona w pomocniku.

Stein G. Strindhaug
źródło
Powiedz, że masz model obrazu. Jeśli z obrazem jest powiązany zewnętrzny adres URL, wskaż ten adres URL. W przeciwnym razie wskaż stronę pokazu obrazu, aby załadować obraz? Po prostu pomysł.
Josh Delsman,
Co powiesz na ten mniej ogólny przykład: link_to „Pełny rozmiar obrazu”, image.link Metoda link w modelu łączyłaby się z adresem URL witryny (ścieżka_obrazu) lub, powiedzmy, adresem URL Flickr, gdyby użytkownik podał taki i .url został ustawiony na obrazie.
Aaron Longwell,