Czy jest jakaś funkcja Railsów do sprawdzania, czy podrzędna istnieje?

98

Kiedy renderuję fragment, który nie istnieje, otrzymuję wyjątek. Chciałbym sprawdzić, czy podrzędny istnieje przed renderowaniem, a jeśli nie istnieje, wyrenderuję coś innego. Zrobiłem następujący kod w moim pliku .erb, ale myślę, że powinien być lepszy sposób na zrobienie tego:

    <% begin %>
      <%= render :partial => "#{dynamic_partial}" %>
    <% rescue ActionView::MissingTemplate %>
      Can't show this data!
    <% end %>
Daniel Cukier
źródło
1
Odpowiedź, której używa, rescuejest ryzykowna. Przed użyciem przyjrzałbym się innym rozwiązaniom.
Grant Hutchins

Odpowiedzi:

98

Obecnie w moich projektach Rails 3 / 3.1 używam:

lookup_context.find_all('posts/_form').any?

Zaletą nad innymi rozwiązaniami, które widziałem, jest to, że będzie to wyglądać we wszystkich ścieżkach widoku, a nie tylko w katalogu głównym rails. Jest to dla mnie ważne, ponieważ mam wiele silników szynowych.

Działa to również w Rails 4.

Wodza
źródło
9
lookup_context.exists? („posts / find”) nie działa dla mnie. Zamiast tego użyłem lookup_context.exists? (Nazwa, prefiks, częściowa) lub lookup_content.exists? ('Find', 'posts', true) w tym przykładzie.
Jenn
2
To jest obecny (rails> = 3.2) sposób sprawdzania szablonów (źródło apidock )
maček
1
Jeśli część znajduje się w tym samym folderze co bieżący szablon widoku, możesz użyć lookup_context.exists?("find", lookup_context.prefixes, true). W ten sposób nie musisz na stałe kodować katalogu widoku w wywołaniu. Uwaga, to dotyczy częściowych. W przypadku nie częściowych pomiń ostatni argument (lub użyj fałszu zamiast prawdy)
Nathan Wallace,
71

Ja też się z tym zmagałem. Oto metoda, którą ostatecznie użyłem:

<%= render :partial => "#{dynamic_partial}" rescue nil %>

Zasadniczo, jeśli nie ma częściowej, nic nie rób. Czy chciałeś coś wydrukować, jeśli brakuje fragmentu?

Edycja 1: Och, nie czytam ze zrozumieniem. Powiedziałeś, że chciałeś wyrenderować coś innego. W takim razie co powiesz na to?

<%= render :partial => "#{dynamic_partial}" rescue render :partial => 'partial_that_actually_exists' %>

lub

<%= render :partial => "#{dynamic_partial}" rescue "Can't show this data!" %>

Edycja 2:

Alternatywa: sprawdzanie istnienia pliku częściowego:

<%= render :partial => "#{dynamic_partial}" if File.exists?(Rails.root.join("app", "views", params[:controller], "_#{dynamic_partial}.html.erb")) %>
Jeff
źródło
6
Moje pytanie jest takie, że nie chcę używać wyjątków do sterowania przepływem, co jest anty-wzorcem: stackoverflow.com/questions/1546514/…
Daniel Cukier
6
Wyjątkiem jest rodzaj kontroli przepływu używanej do obsługi rzeczy, które zdarzają się poza normalnym działaniem programu. Jeśli dynamiczna częściowa ma tam być, ale coś idzie nie tak i ostatecznie jej tam nie ma, to jest to rozsądne zastosowanie dla wyjątku (oczywiście IMO - właściwe użycie wyjątków jest samą świętą wojną). Powiedziałbym, że alternatywą jest sprawdzenie systemu plików pod kątem tego, czy plik istnieje, czy nie. Zaktualizuję odpowiedź tym kodem.
Jeff
3
Podoba mi się to rozwiązanie, niemniej jednak połyka każdy wyjątek rzucony w części. IMHO to utrudnia wyśledzenie błędów.
Matt,
5
Jeśli masz inny typ wyjątku rescue nili ... rescue ...metody będą go ukryć. Prowadzi to do błędów, które są trudne do debugowania.
Nicholaides
8
Naprawdę nie podoba mi się to rozwiązanie. ratowanie jest drogie, a File.exists? zakłada, że ​​część może znajdować się tylko w jednym miejscu. Uważam, że rozwiązanie @ Reina przy użyciu lookup_context jest drogą do osiągnięcia.
Bert Goethals,
52

W widoku, template_exists? działa, ale konwencja wywoływania nie działa z pojedynczym ciągiem częściowej nazwy, zamiast tego przyjmuje template_exists? (nazwa, prefiks, częściowa)

Aby sprawdzić, czy ścieżka jest częściowa: app / views / posts / _form.html.slim

Posługiwać się:

lookup_context.template_exists?("form", "posts", true)
Luke Imhoff
źródło
W Railsach 3.0.10 stwierdziłem, że jeśli mam alternatywne rozszerzenie, takie jak app / views / posts / _foo.txt.erb, muszę dodać to do argumentu jako: template_exists? ("Foo.txt", "posts" , prawda)
Gabe Martin-Dempesy
Jest to przestarzałe w
kolejkach
Wydaje się, że nie jest delegowany w Railsach 3.2.x, jednak drugi argument to tablica przedrostków. Ponadto istnieje na bieżącym kontrolerze.
Brendan,
2
Możesz użyć lookup_context.prefixes jako drugiego argumentu: lookup_context.template_exists? ("Form", lookup_context.prefixes, true)
lion.vollnhals
To jest lepsza odpowiedź, jeśli chodzi o dostęp do tych informacji z poziomu warstwy widoku.
Brendon Muir,
30

W Railsach 3.2.13, jeśli jesteś w kontrolerze, możesz użyć tego:

template_exists?("#{dynamic_partial}", _prefixes, true)

template_exists?jest delegowany lookupcontext, jak widać wAbstractController::ViewPaths

_prefixes podaje kontekst łańcucha dziedziczenia administratora.

true ponieważ szukasz częściowej (możesz pominąć ten argument, jeśli potrzebujesz zwykłego szablonu).

http://api.rubyonrails.org/classes/ActionView/LookupContext/ViewPaths.html#method-i-template_exists-3F

Flackou
źródło
Głosowano za. Bardziej aktualne i lepsze wyjaśnienie parametrów.
jacobsimeon
4
Z widokiem (takich jak układ), to działa: lookup_context.template_exists?("navbar", controller._prefixes, :partial). To mówi mi, czy bieżący szablon renderujący ten układ ma określony "pasek nawigacyjny" częściowy, a jeśli tak, mogę go wyrenderować. Przechodzę :partialtylko po to, aby wyraźnie powiedzieć, czym jest ta wartość logiczna - :partialjest prawdą. Dzięki za _prefixestrochę, @Flackou!
pdobb
Wymień _prefixessię niljeśli dzwonisz częściowy to w innym katalogu nadrzędnego.
ben
8

Wiem, że odpowiedź na to pytanie ma milion lat, ale oto jak to dla mnie naprawiłem ...

Szyny 4.2

Najpierw umieściłem to w pliku application_helper.rb

  def render_if_exists(path_to_partial)
    render path_to_partial if lookup_context.find_all(path_to_partial,[],true).any?
  end

a teraz zamiast dzwonić

<%= render "#{dynamic_path}" if lookup_context.find_all("#{dynamic_path}",[],true).any? %>

po prostu dzwonię <%= render_if_exists "#{dynamic_path}" %>

mam nadzieję, że to pomoże. (nie próbowałem w rails3)

afxjzs
źródło
1
To nie działa, jeśli chcesz zapewnić rezerwę. Nie bierze również pod uwagę zmiennych lokalnych.
phillyslick
To jest dokładnie to, czego szukałem. Bardzo czysta odpowiedź.
Słoneczny
1
@BenPolinsky Przypuszczam, że możesz def render_if_exists(*args); render(*args) if ...do tego użyć
strony
6

Wielokrotnie korzystałem z tego paradygmatu z wielkim sukcesem:

<%=
  begin
    render partial: "#{dynamic_partial}"
  rescue ActionView::MissingTemplate
    # handle the specific case of the partial being missing
  rescue
    # handle any other exception raised while rendering the partial
  end
%>

Zaletą powyższego kodu jest to, że możemy obsłużyć określone przypadki:

  • Brakuje częściowej
  • Część istnieje, ale z jakiegoś powodu spowodowała błąd

Jeśli użyjemy tylko kodu <%= render :partial => "#{dynamic_partial}" rescue nil %>lub jakiejś pochodnej, część może istnieć, ale zgłosi wyjątek, który zostanie po cichu zjedzony i stanie się źródłem bólu podczas debugowania.

br3nt
źródło
4

A co z twoim własnym pomocnikiem:

def render_if_exists(path, *args)
  render path, *args
rescue ActionView::MissingTemplate
  nil
end
Andrey
źródło