2010-08-21 6 views
5

Je suis sûr que cela a déjà été demandé, mais je ne trouve pas la réponse.Éviter les vues nulles dans Rails

J'ai un modèle de projet, qui a une relation belongs_to avec mon modèle client. Un client a un nom, mais un projet n'a pas nécessairement de client.

À mon avis, j'ai le code comme ceci:

<%=h project.client && project.client.name %> 

parce que si le projet ne dispose pas d'un client puis essayez d'accéder project.client.name provoque une NoMethodError (nil ne dispose pas d'une méthode appelée name).

La question est, est-il acceptable d'avoir ce genre de vérification nulle dans la vue, ou devrais-je chercher un autre moyen de contourner le problème?

Répondre

10

Il suffit d'utiliser

project.client.try(:name) 
+0

J'ai oublié celui-là ... :) Cependant, il devient toujours pénible quand vous traversez vers le bas 5-6 modèles profonds. :( – DGM

+3

http://en.wikipedia.org/wiki/Law_of_Demeter – Reactormonk

+0

@Tass Vous avez raison sur la loi de demeter, mais je pense que ce n'est pas la bonne façon de l'implémenter, s'il vous plaît voir mon post ci-dessous – dombesz

3

Je pense que c'est tout à fait acceptable - c'est la logique de vue, vous décidez plus ou moins de montrer ou non des parties de votre vue, selon qu'il y a des données.

3

Je cours là-dedans tout le temps, et oui c'est agaçant. Même quand il est supposé ne jamais être nul, les données sales dont j'ai hérité le déclenchent parfois.

Votre solution est une façon de gérer la situation. Vous pouvez également ajouter une méthode à Project appelée client_name qui affiche le nom du client s'il existe, mais vous liez les modèles ensemble plus que certaines personnes recommandent. Vous pouvez également faire une méthode d'assistance pour le faire, mais vous pouvez finir par en écrire plusieurs. :)

Comme mentionné par Skilldrick ci-dessous, il est également utile d'ajouter une chaîne par défaut:

def client_name 
    client ? client.name : "no client" 
end 
+1

C'est certainement utile dans certaines circonstances (par exemple si vous voulez un nom par défaut comme "pas de client"). – Skilldrick

+1

Une autre implémentation de chaîne par défaut sans utiliser l'opérateur ternaire: 'client.name || "no client" ' – Eric

+0

Bien sûr, puisque la question originale parlait d'avoir un modèle mvc pur, qu'en mettant une chaîne d'affichage par défaut dans votre modèle, vous pouvez injecter ce qui est vraiment plus de logique de vue dans votre modèle, non? C'est pourquoi je l'aurais fait comme l'a fait le questionneur. Mais, tout est style, ça marche aussi :). Je déteste juste supposer que chaque vue que je fais, je veux la même chaîne par défaut, vous savez? – jasonpgignac

0

ma solution aki est de donner un bloc et secourir l'erreur . Beaucoup diraient utiliser le sauvetage car la logique est une très mauvaise forme. N'utilisez pas ceci là où vous auriez besoin de savoir quand quelque chose est nul et ne devrait pas l'être.

En application_helper.rb:

def none_on_fail 
     begin 
      return yield 
     rescue 
      return "(none entered)" 
     end 
    end 

Puis dans la vue:

<%= none_on_fail { project.client.name } %> 

Ensuite, les méthodes peuvent être chaînés aussi profond que nécessaire et il peut être utilisé sur toute méthode, mais il couvrira d'autres problèmes potentiels avec les modèles/relations/méthodes s'ils existent. Je l'assimilerais à prendre un éclat avec un lance-flammes. Très efficace avec des conséquences douloureuses s'il est mal utilisé.

+1

Very Pythonic :) – Skilldrick

0

Je pense que ces contrôles peuvent généralement être éliminés avec un peu de réflexion. Cela a l'avantage de garder votre code de vue plus propre et, plus important encore, de garder la logique hors de la couche de vue, ce qui est une bonne pratique. Certains moteurs de modèles n'autorisent aucune logique dans la vue.

Il existe au moins quelques scénarios. Supposons que vous ayez une action show qui dépend d'une variable d'instance.Je dirais que si l'enregistrement n'est pas trouvé, le contrôleur ne devrait pas rendre le code HTML, en le redirigeant ou quelque chose d'autre. Si vous avez une boucle dans la vue pour un tableau, utilisez @array.each do |a| end afin qu'il n'évalue pas si le tableau est vide. Si vous voulez vraiment une application par défaut dans la vue, essayez de la charger à partir d'un fichier de configuration, par ex. @page_title || #{@APP_CONFIG['page_title']} (voir Railscasts #85). N'oubliez pas que vous pouvez modifier ces chaînes plus tard, par exemple en traduisant l'interface utilisateur.

Il s'agit de quelques scénarios dans lesquels les contrôles de présence et l'utilisation de try peuvent être évités. J'essaierais de les éviter si possible. Si vous ne pouvez pas les éviter, je mettrais les contrôles conditionnels dans une aide de vue et ajouterais un test d'unité d'aide pour vérifier (et documenter) les deux chemins de code.

2

Vous pouvez utiliser delegate dans votre classe Project, de cette façon vous allez respecter le Law of demeter qui dit que vous devriez "parler seulement à vos amis immédiats".

project.rb

class Project 
    delegate :name, to: :client, prefix: true, allow_nil: true  
end 

Ainsi, cette façon l'objet du projet savoir où poser des questions sur le nom du client:

#You can now call 
project.client_name 

En savoir plus sur delegate dans le Rails documentation.

Questions connexes