Existe-t-il une fonction Rails pour vérifier si un partiel existe?

98

Lorsque je rend un partiel qui n'existe pas, j'obtiens une exception. Je voudrais vérifier si un partiel existe avant de le rendre et au cas où il n'existerait pas, je rendrai autre chose. J'ai fait le code suivant dans mon fichier .erb, mais je pense qu'il devrait y avoir un meilleur moyen de le faire:

    <% begin %>
      <%= render :partial => "#{dynamic_partial}" %>
    <% rescue ActionView::MissingTemplate %>
      Can't show this data!
    <% end %>
Daniel Cukier
la source
1
La réponse qui utilise rescueest risquée. Je regarderais les autres solutions avant de l'utiliser.
Grant Hutchins

Réponses:

98

Actuellement, j'utilise ce qui suit dans mes projets Rails 3 / 3.1:

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

L'avantage par rapport aux autres solutions que j'ai vues est que cela va regarder dans tous les chemins de vue au lieu de simplement votre racine de rails. C'est important pour moi car j'ai beaucoup de moteurs sur rails.

Cela fonctionne également dans Rails 4.

Rêne
la source
9
lookup_context.exists? ('posts / find') n'a pas fonctionné pour moi. Au lieu de cela, j'ai utilisé lookup_context.exists? (Nom, préfixe, partiel) ou lookup_content.exists? ('Find', 'posts', true) dans cet exemple.
Jenn
2
C'est la façon actuelle (rails> = 3.2) de vérifier les modèles (source apidock )
maček
1
Si le partiel se trouve dans le même dossier que le modèle de vue actuel, vous pouvez utiliser lookup_context.exists?("find", lookup_context.prefixes, true). De cette façon, vous n'avez pas besoin de coder en dur le répertoire de vue dans l'appel. Remarque, ceci est pour les partiels. Pour les non partiels, omettez le dernier argument (ou utilisez false au lieu de true)
Nathan Wallace
71

J'avais du mal avec ça aussi. C'est la méthode que j'ai fini par utiliser:

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

En gros, si le partiel n'existe pas, ne faites rien. Vouliez-vous imprimer quelque chose si le partiel est manquant?

Edit 1: Oh, j'échoue à la compréhension de la lecture. Vous avez dit que vous vouliez rendre autre chose. Dans ce cas, qu'en est-il de cela?

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

ou

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

Modifier 2:

Alternative: vérification de l'existence du fichier partiel:

<%= render :partial => "#{dynamic_partial}" if File.exists?(Rails.root.join("app", "views", params[:controller], "_#{dynamic_partial}.html.erb")) %>
Jeff
la source
6
Ma question est que je ne veux pas utiliser les exceptions pour faire le contrôle de flux, qui est un anti-pattern: stackoverflow.com/questions/1546514/…
Daniel Cukier
6
Une exception est un type de contrôle de flux utilisé pour gérer les choses qui se produisent au-delà du fonctionnement normal d'un programme. Si le partiel dynamique est censé être là mais que quelque chose ne va pas et qu'il finit par ne pas y être, alors c'est une utilisation raisonnable d'une exception (l'OMI, bien sûr - l'utilisation appropriée des exceptions est une guerre sainte elle-même). Je dirais que votre alternative est de vérifier le système de fichiers pour savoir si le fichier réel existe ou non. Je mettrai à jour ma réponse avec ce code.
Jeff
3
J'aime la solution, néanmoins elle avale toute sorte d'exception jetée dans le partiel. À mon humble avis, cela rend plus difficile la recherche des erreurs.
Matt
5
Si vous avez un autre type d'exception, les méthodes rescue nilet ... rescue ...le masqueront. Cela conduit à des bogues difficiles à déboguer.
nicholaides
8
Je n'aime vraiment pas cette solution. le sauvetage coûte cher, et File.exists? suppose que le partiel ne peut se trouver qu'à un seul endroit. La solution de @ Rein utilisant le lookup_context est la voie à suivre, je crois.
Bert Goethals
52

De l'intérieur d'une vue, template_exists? fonctionne, mais la convention d'appel ne fonctionne pas avec la chaîne de nom partielle unique, à la place, elle prend template_exists? (nom, préfixe, partiel)

Pour vérifier le chemin d'accès partiel: app / views / posts / _form.html.slim

Utilisation:

lookup_context.template_exists?("form", "posts", true)
Luke Imhoff
la source
Sur Rails 3.0.10, j'ai trouvé que si j'avais une extension alternative, comme app / views / posts / _foo.txt.erb, je devais l'ajouter à l'argument comme: template_exists? ("Foo.txt", "posts" , true)
Gabe Martin-Dempesy
Ceci est obsolète dans les rails 3.2
maček
Il ne semble pas être délégué dans Rails 3.2.x, cependant, le deuxième argument est un tableau de préfixes. De plus, il existe sur le contrôleur actuel.
Brendan
2
Vous pouvez utiliser lookup_context.prefixes comme deuxième argument: lookup_context.template_exists? ("Form", lookup_context.prefixes, true)
lion.vollnhals
C'est la meilleure réponse en termes d'accès à ces informations à partir de la couche de vue.
Brendon Muir le
30

Dans Rails 3.2.13, si vous êtes dans un contrôleur, vous pouvez utiliser ceci:

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

template_exists?est délégué à lookupcontext, comme vous pouvez le voir dansAbstractController::ViewPaths

_prefixes donne le contexte de la chaîne d'héritage du contrôleur.

true parce que vous recherchez un partiel (vous pouvez omettre cet argument si vous voulez un modèle régulier).

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

Flackou
la source
J'ai voté pour. Explication plus à jour et meilleure des paramètres.
jacobsimeon
4
Du point de vue ( par exemple une mise en page), cela fonctionne: lookup_context.template_exists?("navbar", controller._prefixes, :partial). Cela me dit si le modèle actuel rendant cette mise en page a la "barre de navigation" partielle indiquée, et si c'est le cas, je peux le rendre. Je passe :partialjuste pour être explicite sur ce qu'est ce booléen - :partialest la vérité. Merci pour le _prefixespeu, @Flackou!
pdobb
Remplacez _prefixespar nilsi vous appelez un partiel qui se trouve dans un répertoire parent différent.
ben
8

Je sais que cela a été répondu et qu'il a un million d'années, mais voici comment j'ai fini par résoudre cela pour moi ...

Rails 4.2

Tout d'abord, je mets ceci dans mon 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

et maintenant au lieu d'appeler

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

je viens d'appeler <%= render_if_exists "#{dynamic_path}" %>

J'espère que cela pourra aider. (je n'ai pas essayé dans rails3)

afxjzs
la source
1
Cela ne fonctionne pas si vous souhaitez fournir une solution de secours. Il ne prend pas non plus en compte les variables locales.
phillyslick
Ceci est exactement ce que je cherchais. Réponse très claire.
Ensoleillé
1
@BenPolinsky je suppose que vous pouvez utiliser def render_if_exists(*args); render(*args) if ...pour cela
sites
6

J'ai utilisé ce paradigme à plusieurs reprises avec beaucoup de succès:

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

L'avantage du code ci-dessus est que nous pouvons gérer des cas spécifiques de remorquage:

  • Le partiel manque en effet
  • Le partiel existe, mais il a généré une erreur pour une raison quelconque

Si nous n'utilisons que le code <%= render :partial => "#{dynamic_partial}" rescue nil %>ou un dérivé, le partiel peut exister mais déclencher une exception qui sera silencieusement mangée et deviendra une source de douleur à déboguer.

br3nt
la source
4

Qu'en est-il de votre propre assistant:

def render_if_exists(path, *args)
  render path, *args
rescue ActionView::MissingTemplate
  nil
end
Andreï
la source