2010-02-03 9 views
16

Advertencia: Noob aquí.Rieles: refactorización, vistas, ayudantes: ¿cómo va todo junto?

Sé que este es un tema trivial, pero estoy teniendo muchas dificultades para descubrir cómo puedo simplificar mis vistas al mover partes de ellas en ayudantes. Por ejemplo, siempre he leído que los condicionales en sus vistas son los principales candidatos para la extracción en ayudantes, pero realmente no pude encontrar ejemplos de esto, y mis intentos de lograr esto fallaron.

Por ejemplo, supongamos que tengo:

#index.html.erb 

<% for beast in @beasts do -%> 
    <% if beast.dead? -%> 
    <%= beast.body %> 
    <%= link_to "bury", bury_beast_path(:id => beast.id) %> 
    <% else -%> 
    <%= beast.body %> 
    <%= link_to "kill!", kill_beast_path(:id => beast.id) %> 
    <% end -%> 
<% end -%> 

me molesta un poco para tener esto en mi punto de vista, pero exactamente cómo podría yo pasar esto a un ayudante en su lugar? Y simplificar aún más, si es posible. (He leído en alguna parte que los condicionales son malos pero es sólo más allá de mí cómo se puede programar nada sin ellos.)

Otro ejemplo: Necesito id mis body etiquetas con el formato controller_action. El mejor que tengo hasta ahora es la siguiente:

#index.html.erb 

<body id="<%= controller_action %>"> 

... y ...

#application_helper.rb 

def controller_action 
    @id = @controller.controller_name + "_" + @controller.action_name 
end 

No soy un experto, pero que sigue siendo feo incluso para mí.

Para hacer las cosas más complicadas, Ryan Singer said something I liked: para tratar ERB como una etiqueta de imagen, usando ayudantes para "revelar la intención". Luego, en el siguiente aliento, diciendo que no deberías tener HTML en ayudantes, es el camino al infierno. WTF? ¿Cómo son ambas cosas compatibles? Si ha llegado al punto en el que puede declarar comportamientos en la vista, seguramente debería haber una gran cantidad de HTML que se renderice detrás de escena. No puedo entenderlo.

Entonces, eso es básicamente. Agradecería que alguien pudiera compartir algunas ideas al respecto, o señalarme algunas buenas lecturas en profundidad sobre el tema, que encontré que tienen una cobertura muy débil en la web. Ya lo busqué en Google hasta el cansancio, pero quién sabe.

+0

Nota secundaria. Usar 'for' está bien, pero usar un iterador es más una convención de Ruby, por ejemplo,' <% @ beasts.each do | bestia | %> '. –

+0

@Sarah: gracias. Es que realmente me gusta el aspecto del lenguaje natural de usar 'for'. –

Respuesta

26

La refabricación hace que sus vistas sean más fáciles de mantener. El problema es elegir dónde va el código refactorizado.

Sus dos opciones son parciales y ayudantes. No hay reglas establecidas por piedra que dicten lo que debe usarse donde. Hay un par de directrices flotando alrededor, como la que establece que los ayudantes no deben contener HTML.

Generalmente, los parciales son más adecuados para refactorizar secciones que son más HTML/ERB/​​HAML que ruby. Por otro lado, los helpers se usan para fragmentos de código ruby ​​con HTML mínimo o para generar HTML simple a partir de los parámetros.

Sin embargo, no estoy de acuerdo con el sentimiento de que los ayudantes no deberían contener HTML. Un poco está bien, simplemente no más de hacerlo. La forma en que se procesan los ayudantes dificulta su uso para producir grandes cantidades de HTML. Por eso se sugiere que sus ayudantes contengan cantidades mínimas de HTML. Si miras a la fuente los ayudantes que envían los rieles, notarás que la mayoría genera html. Los pocos que no lo hacen, se utilizan principalmente para generar parámetros y evaluar condiciones comunes.

Por ejemplo, cualquiera de los helper de formularios o las variantes de link_to se ajustan a la primera forma de ayudantes. Mientras que cosas como url_for y logged_in? como lo suministran varios modelos de autenticación son del segundo tipo.

Esta es la cadena de decisiones que utilizo para determinar si se debe factorizar el código de una vista en un parcial o ayudante.

  1. ¿Declaraciones repetitivas o casi idénticas que producen una única etiqueta html superficial? => ayudante
  2. ¿Expresión común utilizada como argumento para otro ayudante? => ayudante
  3. Expresión larga (más de 4 términos) utilizada como argumento para otro ayudante? => ayudante
  4. 4 o más líneas de rubí (que no se evalúa en HTML)? => ayudante
  5. Casi todo lo demás => parcial.

Voy a usar el código que está buscando para refactorizar como ejemplo:

Me refactorizar la vista en la pregunta de esta manera:

app/helpers/beast_helper.rb :

def beast_action(beast) 
    if beast.dead? 
    link_to "bury", bury_beast_path(beast) 
    else 
    link_to "kill!", kill_beast_path(beast) 
    end 
end 

app/views/bestias/_beast.html.erb:

<%= beast.body %> 
<%= beast_action(beast) %> 

app/views/bestias/index.html.erb:

<%= render :partial => "beast", :collection => @beasts %> 

Es técnicamente más complicado, porque es 3 archivos, y 10 líneas totales en comparación con 1 archivo y 10 líneas. Las vistas ahora son solo 3 líneas combinadas repartidas en 2 archivos. El resultado final es que tu código es mucho más SECO. Permitiendo reutilizar partes o todo en otros controladores/acciones/vistas con una complejidad mínima agregada.

En cuanto a su identificación de la etiqueta del cuerpo. Realmente deberías estar usando content_for/yield. Para ese tipo de cosas.

app/views/layouts/application.html.erb

... 
<body id="<%= yield(:body_id) %>"> 
... 

app/views/bestias/index.html.erb

<% content_for :body_id, controller_action %> 
... 

Esto le permitirá anular el id de la cuerpo en cualquier vista que lo requiera. Por ejemplo:

app/views/users/preferences.html.erb

<% content_for :body_id, "my_preferences" %> 
+0

Mientras el parcial esté en 'bestias/_beast.html.erb', puede hacer' render @ bestias'. – Garrett

+0

@Garrett: eso solo funciona en Rails 2.3 o posterior. No tengo idea de por qué un nuevo usuario usaría una versión anterior. Pero sentí que esa versión era más segura. – EmFi

+0

Buen punto, con Rails 3 próximamente Creo que es seguro comenzar a presionar 'render @ beasts' – Garrett

8

Lo primero que haría sería la siguiente:

#index.html.erb 
<%= render @beasts %> 

#_beast.html.erb 
<%= beast.body %> 
<%= link_to_next_beast_action(beast) %>  

#beast_helper.rb 
def link_to_next_beast_action(beast) 
    if beast.dead? 
    link_to "bury", bury_beast_path(:id => beast.id) 
    else 
    link_to "kill!", kill_beast_path(:id => beast.id) 
    end 
end 

Lo que he hecho es separar la prestación de la bestia en un parcial que utiliza la semántica de recogida.

Luego moví la lógica para mostrar los enlaces de matar/enterrar a un ayudante de bestia. De esta forma, si decides agregar otra acción (por ejemplo, 'bring back from dead'), solo tendrás que cambiar tu helper.

¿Le sirve de ayuda?

+0

Incluso puede hacer 'render @ bestias' ya que se dará cuenta de que es una colección. – Garrett

+0

Parece que ha llegado a la misma refactorización que hice. No noté la nueva respuesta mientras componía la mía. – EmFi

+2

@EmFi He votado positivamente su respuesta porque ha entrado en más detalles sobre cuándo usar partials/helpers. – jonnii

0

Otra startegy sería no utilizar plantillas y ayudantes en absoluto. Para la representación puede:

  1. visualice sus puntos de vista directamente desde sus controladores utilizando render (: inline =>). Si aún desea mantener Vistas y Controladores formalmente separados, puede crear módulos/mixins que incluya en los controladores.
  2. o crea tus propias clases de vista y úsalas para mostrar tu respuesta.

La idea detrás de esto es que los ayudantes y los carriles sistema de plantillas erb no tome ventaja de programación orientada a objetos, de modo que al final del día no se puede definir comportamientos generales que usted se especializan de acuerdo con cada una de controlador/las necesidades de la solicitud; la mayoría de las veces uno termina reescribiendo trozos de código de aspecto muy similar, lo que no es muy agradable desde el punto de vista del mantenimiento.

Entonces, si todavía necesita algunos métodos de ayuda (por ejemplo, form_tag, h, raw, ...) solo tiene que incluirlos en su controlador/clase de vista dedicada.

Consulte esto: rails-misapprehensions-helpers-are-shit para obtener un artículo divertido pero útil.

EDITAR: para que no parezca una ducha completa, yo diría que implementar esto depende de cuán grande se supone debe ser su aplicación, y con qué frecuencia tendrá que actualizar su código. Además, si está delegando el diseño a un programador no profesional, es posible que tenga algunos cursos de programación antes de profundizar en su código, lo que sin duda sería menos comprensible que con la sintaxis de las plantillas.

1

Una tercera opción es utilizar un modelo de vista del Cells gem. Este es un marco muy popular que trae orientación de objeto a la capa de vista en Rails.

# app/cells/beast/cell.rb 

class Beast::Cell < Cell::Concept 
    def show 
    return dead if model.dead? 
    kill 
    end 

private 
    def dead 
    link_to "bury", bury_beast_path(:id => model.id) 
    # you could render a view here, too! 
    end 

    def kill 
    link_to "kill!", kill_beast_path(:id => model.id) 
    end 
end 

Luego renderiza un modelo de vista utilizando un ayudante (en la vista o el controlador).

# app/views/beasts/index.erb 

<%= concept(:beast, @beast).call %> 
<%-# this returns the link content %> 

¡Eso es todo! Puedes probar esta celda aislada en una prueba separada. Las celdas también le ofrecen visualización, visualización de herencia y muchas cosas más.

Como ejemplo, puede usar una vista para el enlace kill.

# app/cells/beast/cell.rb 

class Beast::Cell < Cell::Concept 

    # .. 

    def kill 
    render :kill 
    end 
end 

Esto representa la vista asesina de la celda.

# app/cells/beast/views/index.erb 

<%= link_to "kill!", kill_beast_path(:id => model.id) %> 

Tenga en cuenta la ubicación de la vista, está muy bien empaquetada en el directorio de la celda.

Y, sí, las células pueden hacer HAML y cualquier otro motor de plantilla compatible con AbstractController.

Cuestiones relacionadas