Existe alguma função Rails para verificar se existe um parcial?

98

Quando eu renderizo um parcial que não existe, recebo uma exceção. Gostaria de verificar se existe um parcial antes de renderizá-lo e, caso não exista, vou renderizar outra coisa. Fiz o seguinte código em meu arquivo .erb, mas acho que deve haver uma maneira melhor de fazer isso:

    <% begin %>
      <%= render :partial => "#{dynamic_partial}" %>
    <% rescue ActionView::MissingTemplate %>
      Can't show this data!
    <% end %>
Daniel Cukier
fonte
1
A resposta que usa rescueé arriscada. Eu examinaria as outras soluções antes de usá-lo.
Grant Hutchins

Respostas:

98

Atualmente, estou usando o seguinte em meus projetos Rails 3 / 3.1:

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

A vantagem sobre outras soluções que eu vi é que isso vai olhar em todos os caminhos de exibição em vez de apenas na raiz do rails. Isso é importante para mim, pois tenho muitos motores Rails.

Isso também funciona no Rails 4.

Rédea
fonte
9
lookup_context.exists? ('posts / find') não funcionou para mim. Em vez disso, usei lookup_context.exists? (Name, prefix, partial) ou lookup_content.exists? ('Find', 'posts', true) neste exemplo.
Jenn
2
Esta é a forma atual (rails> = 3.2) de verificar os modelos ( apidock fonte )
maček
1
Se a parcial estiver na mesma pasta do modelo de vista atual, você pode usar lookup_context.exists?("find", lookup_context.prefixes, true). Dessa forma, você não precisa codificar o diretório de visualização na chamada. Observe, isso é para parciais. Para não parciais, omita o último argumento (ou use falso em vez de verdadeiro)
Nathan Wallace
71

Eu estava lutando com isso também. Este é o método que acabei usando:

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

Basicamente, se o parcial não existe, não faça nada. Você quer imprimir algo se o parcial estiver faltando?

Edição 1: Oh, eu falho em compreensão de leitura. Você disse que queria renderizar outra coisa. Nesse caso, que tal isso?

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

ou

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

Editar 2:

Alternativa: Verificar a existência do arquivo parcial:

<%= render :partial => "#{dynamic_partial}" if File.exists?(Rails.root.join("app", "views", params[:controller], "_#{dynamic_partial}.html.erb")) %>
Jeff
fonte
6
Minha pergunta é que não quero usar Exceções para fazer o controle de fluxo, que é um antipadrão: stackoverflow.com/questions/1546514/…
Daniel Cukier
6
Uma exceção é um tipo de controle de fluxo usado para lidar com coisas que acontecem além da operação normal de um programa. Se a parcial dinâmica deveria estar lá, mas algo deu errado e ela acabou não estando lá, então esse é um uso razoável para uma exceção (IMO, é claro - o uso adequado de exceções é uma guerra santa em si). Eu diria que sua alternativa é verificar o sistema de arquivos para ver se o arquivo real existe ou não. Vou atualizar minha resposta com esse código.
Jeff
3
Gosto da solução, mas engole qualquer tipo de exceção lançada na parcial. IMHO, isso torna mais difícil rastrear erros.
Matt
5
Se você tiver um tipo diferente de exceção, os métodos rescue nile ... rescue ...irão ocultá-lo. Isso leva a bugs que são difíceis de depurar.
nicholaides
8
Eu realmente não gosto dessa solução. resgatar é caro, e File.exists? assume que o parcial só pode estar em um local. A solução de @Rin usando o lookup_context é o caminho a percorrer, eu acredito.
Bert Goethals
52

De dentro de uma visão, template_exists? funciona, mas a convenção de chamada não funciona com a string de nome parcial única, em vez disso, leva template_exists? (nome, prefixo, parcial)

Para verificar se há parcial no caminho: app / views / posts / _form.html.slim

Usar:

lookup_context.template_exists?("form", "posts", true)
Luke Imhoff
fonte
No Rails 3.0.10 descobri que se eu tiver uma extensão alternativa, como app / views / posts / _foo.txt.erb, preciso adicioná-la ao argumento como: template_exists? ("Foo.txt", "posts" , verdadeiro)
Gabe Martin-Dempesy
Este foi descontinuado no rails 3.2
maček
Não parece ser delegado no Rails 3.2.x, entretanto, o segundo argumento é um array de prefixos. Além disso, ele existe no controlador atual.
Brendan
2
Você pode usar lookup_context.prefixes como o segundo argumento: lookup_context.template_exists? ("Form", lookup_context.prefixes, true)
lion.vollnhals
Esta é a melhor resposta em termos de acesso a essas informações da camada de visualização.
Brendon Muir
30

No Rails 3.2.13, se você estiver em um controlador, você pode usar isto:

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

template_exists?é delegado lookupcontext, como você pode ver emAbstractController::ViewPaths

_prefixes fornece o contexto da cadeia de herança do controlador.

true porque você está procurando por um parcial (você pode omitir este argumento se quiser um modelo regular).

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

Flackou
fonte
Votado. Mais atualizado e melhor explicação dos parâmetros.
jacobsimeon
4
Do ponto de vista (como um layout), isso funciona: lookup_context.template_exists?("navbar", controller._prefixes, :partial). Isso me diz se o modelo atual que renderiza este layout tem o parcial "navbar" declarado e, se tiver, posso renderizá-lo. Eu passo :partialapenas para ser explícito sobre o que esse booleano é - :partialé verdade. Obrigado pela _prefixesajuda, @Flackou!
pdobb
Substitua _prefixespor nilse você estiver chamando um parcial que está em um diretório pai diferente.
ben
8

Sei que isso foi respondido e tem um milhão de anos, mas foi assim que acabei consertando isso para mim ...

Rails 4.2

Primeiro, coloco isso em meu 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

e agora em vez de ligar

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

eu acabei de ligar <%= render_if_exists "#{dynamic_path}" %>

espero que ajude. (não tentei no rails3)

afxjzs
fonte
1
Isso não funciona se você deseja fornecer um substituto. Também não leva em consideração as variáveis ​​locais.
phillyslick
Isso é exatamente o que eu estava procurando. Resposta muito clara.
Ensolarado de
1
@BenPolinsky Suponho que você possa usar def render_if_exists(*args); render(*args) if ...para isso
sites de
6

Usei esse paradigma em muitas ocasiões com grande sucesso:

<%=
  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
%>

A vantagem do código acima é que podemos lidar com dois casos específicos:

  • O parcial realmente está faltando
  • O parcial existe, mas gerou um erro por algum motivo

Se usarmos apenas o código <%= render :partial => "#{dynamic_partial}" rescue nil %>ou algum derivado, o parcial pode existir, mas levanta uma exceção que será silenciosamente comida e se tornará uma fonte de problemas para depurar.

br3nt
fonte
4

E quanto ao seu próprio ajudante:

def render_if_exists(path, *args)
  render path, *args
rescue ActionView::MissingTemplate
  nil
end
Andrey
fonte