2010-08-21 13 views
5

Estoy seguro de que esto ya se ha preguntado, pero no puedo encontrar la respuesta.Evitar nil en las vistas de Rails

Tengo un modelo de proyecto, que tiene una relación belongs_to con mi modelo de cliente. Un cliente tiene un nombre, pero un proyecto no necesariamente tiene un cliente.

En mi opinión, tengo un código como éste:

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

porque si el proyecto no tiene un cliente a continuación, tratar de acceder a project.client.name provoca una NoMethodError (nil no tiene un método llamado name).

La pregunta es, ¿es aceptable tener este tipo de comprobación nula en la vista, o debería estar buscando otra forma de evitarlo?

Respuesta

10

sólo tiene que utilizar

project.client.try(:name) 
+0

Olvidé esa ... :) Sin embargo, todavía se pone pesado cuando recorres 5-6 modelos de profundidad. :( – DGM

+3

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

+0

@Tass Tienes razón acerca de la ley de Demeter, pero creo que esa no es la manera correcta de implementarlo, por favor mira mi publicación a continuación. – dombesz

3

Creo que es perfectamente aceptable: esta es la lógica de la vista, usted está más o menos decidiendo si mostrar o no partes de su vista, en función de si hay datos.

3

Me encuentro con esto todo el tiempo, y sí, es molesto. Incluso cuando se supone que nunca debe haber nada, los datos sucios que heredé a veces lo desencadenan.

Su solución es una forma de manejarlo. También puede agregar un método al Project llamado client_name que muestra el nombre del cliente si existe, pero luego está vinculando los modelos más de lo que algunas personas recomiendan.

def client_name 
    client && client.name 
end 

También puede hacer un método de ayuda para hacerlo, pero puede terminar escribiendo muchos de ellos. :)

Como se ha mencionado por Skilldrick a continuación, esto también es útil para agregar una cadena por defecto:

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

Este es, sin duda útil en ciertas circunstancias (por ejemplo, si desea un nombre predeterminado como "sin cliente"). – Skilldrick

+1

Otra implementación de cadena predeterminada sin usar el operador ternario: 'client.name || "no client" ' – Eric

+0

Se podría argumentar, por supuesto, que dado que la pregunta original era discutir tener un modelo de mvc puro, al poner una cadena de pantalla predeterminada en su modelo, puede estar inyectando lo que realmente tiene más lógica de visualización en su modelo, ¿no? Es por eso que lo hubiera hecho de la forma en que lo hizo el interrogador. Pero, es todo estilo, esto funciona, también :). Odio suponer que cada vista que hago, quiero la misma cadena predeterminada, ¿sabes? – jasonpgignac

0

mi solución hacky es para producir un bloque y rescatar el error . Muchos dirían usar el rescate ya que la lógica es muy mala. Simplemente no use esto donde realmente necesitaría saber cuando algo es nulo y no debería ser.

En application_helper.rb:

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

Luego, en la vista:

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

Entonces métodos se pueden encadenar tan profundo como sea necesario y se puede utilizar en cualquier método, pero cubrirá hasta otros problemas potenciales con modelos/relaciones/métodos si existen. Lo igualaría a sacar una astilla con un lanzallamas. Muy efectivo con dolorosas consecuencias si se usa incorrectamente.

+1

Very Pythonic :) – Skilldrick

0

Creo que estos controles generalmente se pueden eliminar con un poco de reflexión. Esto tiene el beneficio de mantener el código de vista más limpio y, lo que es más importante, mantener la lógica fuera de la capa de visualización, lo cual es una buena práctica. Algunos motores de plantillas no permiten ninguna lógica en la vista.

Hay al menos un par de escenarios. Supongamos que tiene una acción show que depende de una variable de instancia.Yo diría que si no se encuentra el registro, el controlador no debería renderizar el html, redireccionando o alguna otra cosa. Si tiene un bucle en la vista de una matriz, use @array.each do |a| end para que no se evalúe si la matriz está vacía. Si realmente desea una aplicación predeterminada en la vista, intente cargarla desde un archivo de configuración, p. @page_title || #{@APP_CONFIG['page_title']} (ver Railscasts #85). Recuerde que es posible que desee cambiar estas cadenas más tarde, por ejemplo, la traducción de la interfaz de usuario.

Esos son algunos escenarios en los que se pueden evitar las comprobaciones de presencia y el uso de try. Intentaré evitarlos si es posible. Si no puede evitarlos, colocaría las verificaciones condicionales en un asistente de visualización y agregaría una prueba de unidad auxiliar para verificar (y documentar) ambas rutas de código.

2

Puede usar delegate en su clase Project, de esta manera respetará el Law of demeter que dice que debe "hablar solo con sus amigos inmediatos".

project.rb

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

Así de esta manera el objeto de proyecto sabrá dónde preguntar sobre el nombre del cliente:

#You can now call 
project.client_name 

Ver más sobre delegate en el Rails documentation.

Cuestiones relacionadas