2008-10-26 7 views
26

J'ai un système de templates très ancien écrit au dessus d'ERB. Il s'appuie sur des modèles ERB stockés dans la base de données. Ceux-ci sont lus et rendus. Quand je veux passer des données d'un gabarit à un autre, j'utilise le paramètre: locals parameter to Rails. Pour définir les variables par défaut de ces variables dans certains modèles, j'utilise le défini? méthode qui me dit simplement si la variable locale a été définie et sinon je l'initialiser avec la valeur par défaut comme ceci:défini? méthode dans Ruby and Rails

unless defined?(perex) 
    perex = true 
end 

Je suis mise à niveau de l'application aux dernières Rails et je vois un comportement bizarre. Fondamentalement cela fonctionne parfois (parfois perex est indéfini) et parfois non (perex est défini et mis à zéro). Cela arrive sans que rien d'autre ne change.

J'ai deux questions: Y a-t-il un autre moyen que d'utiliser défini? qui s'avère peu fiable (était fiable depuis plusieurs années sur Rails 1.6)? Une telle méthode ne devrait pas entraîner la réécriture de tous les modèles. J'ai passé par des documents de Ruby et n'ai pas pu trouver quelque chose sur défini? méthode. Était-ce obsolète ou suis-je simplement aveugle?

Modifier: Le problème réel a été causé par ce qui semble être un bogue Ruby/eRB. Parfois, l'instruction à moins que ne fonctionne, mais parfois non. La chose étrange est que même si la deuxième ligne a été exécutée perex est restée nulle pour le reste du monde. Supprimer défini? résolu cela.

Répondre

36

Première: en fait, defined? is an operator.

Deuxièmement: si je comprends bien votre question, la façon de le faire est avec cet idiome Ruby:

perex ||= true 

Ce assignerons vrai perex si elle est définie ou nil. Ce n'est pas exactement ce que votre exemple fait, puisque le vôtre n'évalue pas le devoir quand la valeur est nil, mais si vous comptez sur cela alors, à mon avis, sans le voir, vous n'écrivez pas de code clair.

Modifier: Comme Honza noté, la déclaration ci-dessus va remplacer la valeur de perex quand il est false. Ensuite, je propose ce qui suit pour réécrire le nombre minimal de lignes:

perex ||= perex.nil? # Assign true only when perex is undefined or nil 
+10

Ce n'est pas vrai. perex || = true fait la même chose que perex = perex || true qui définit perex à true s'il est indéfini, nul ou faux. Le dernier cas casserait tout. – Honza

24

La meilleure façon de tester si un local est défini dans un modèle Rails est:

local_assigns[:perex] 

Ceci est documenté dans l'API Rails avec l'explication que defined? ne peut pas être utilisé en raison d'une restriction de mise en œuvre.

+1

Que faire si la valeur de perex est fausse? –

+0

vous pouvez le vérifier par vous-même :) perex = true si local_assigns [: perex] .nil? – Hannes

12

Par réponse de mislav, je suis allé chercher cette documentation dans l'API Rails, et l'ai trouvée dans Class ActionView::Base (sous la rubrique "Passer des variables locales à des sous-modèles"). Cela valait à peine la peine de chercher, puisqu'elle disait à peine autre chose que Mislav. Sauf qu'il recommande ce modèle:

if local_assigns.has_key? :perex 
+0

Ceci est maintenant déprécié en faveur de la clé Hash #? mais je pense que pour l'instant fonctionne toujours. –

6

Prise en considerationg mislav's original answer et KenB's elaboration, je pense que ce qui suit est la meilleure approche absolue (si je suis ouvert à l'opinion).Il utilise la méthode Hash#fetch de Ruby pour se replier sur une autre valeur si la clé n'existe pas dans le hachage d'origine.

perex = local_assigns.fetch(:perex, true) 

Ceci est d'autant mieux que la méthode ||= que la plupart des utilisateurs suggéreront car parfois vous voulez permettre false valeurs. Par exemple, le code suivant jamais permettre une valeur false à passer dans:

perex = local_assigns[:perex] || true