Vistas anidadas en Rails: ¿Cuál es la convención aquí?

TLDR: for collections, use <%= render ... %> with a partial template. For singular elements, use convention access to their fields via the delegate helper.

Había empezado a quejarme en un hilo de twitter pero esto tiene más pinta de entrada de blog.

Ocurre una cosa en casi todas las vistas de una aplicación web que use más de una tabla, y es que en algún momento hay que listar elementos que son referencias a otras tablas. Lo más común es tener un modelo con un has_many hacia otro, y que al estar mostrando una instancia del primer modelo es prácticamente obligado mostrar también la lista de instancias del otro.

El ejemplo es tan común como para que sea parte del tutorial “getting started“. Pero a su vez es lo suficientemente desconcertante para que no sea considerado automáticamente en los generate scaffold. Es una refactorización, según los tutoriales, opcional pero resulta que tiene helpers y lógica de código preparada expresamente para ella.

Lo que está en el fondo la organización de las funcionalidades en programación orientada al objeto, y como se prepara una vista a la vez que se conservan las leyes del sistema Demeter.

Las “convenciones” de rails se basan en asumir el formalismo MVC para cada objeto, pero de manera tal que aunque formalmente se desglose en tres objetos diferentes (de hecho, dos y una tanda de templates) la unidad de nombre permita seguir pensando en ellos como piezas de la misma entidad.

Una primera salvedad es que además de clases e instancias uno se topa enseguida con el concepto de Colecciones, arrays de elementos del mismo tipo. Muchas veces las funciones de una clase devuelven arrays de este tipo en vez de devolver instancias individuales, así que es bastante natural considerar estos arrays como algo vinculado a la clase. Eso se refleja en la existencia de una view llamada “list” o “index“, que se especializa en mostrar elementos de ese tipo.

Así, las vistas básicas que encontramos para cada modelo-clase en rails son “index” y “show“. Esto es especialmente cierto en las templates de jbuilder, donde curiosamente el scaffold ya nos genera una tercera template, una partial _modelname.json.jbuilder. Se supone que nosotros mismos nos daremos cuenta del exceso de verbosidad de la template index.html y acabaremos creando una partial _modelname.html.erb.

El objetivo de estas partials es generar lo que podríamos llamar la “vista inline” de una instancia, esto es la que se va a mostrar cuando se hagan listados de una colección, de los cuales “index” pasa a ser un caso particular.

Y aquí es donde incorpora Rails la magia necesaria para anidar una vista dentro de otra. Consideremos la aplicacion del getting started: un model para articulos, y otro para comentarios al articulo. En este caso los comentarios se deben visualizar como parte de la vista del modelo “articles“, pero son obviamente elementos de la clase “comments“. Así que lo que ocurre es que se invoca no al index de la clase comments sino a su partial.

<h2>Comments</h2>
<%= render @article.comments %>

 

El equivalente en un diseño de objetos puro sería que la instancia de “articles” le va pidiendo a cada instancia de “comments” el html que necesita para ir construyendo la vista. Dado que la parte de ActiveView que procesa las templates no es muy explícita en el framework, posiblemente el uso de la llamada render es un buen compromiso. Con esto ya hemos tomado una decisión que parece estar dentro de las convenciones.

Cuando no se trata de mostrar una lista sino datos de un solo elemento,  asociado via belongs_to o has_one, la teoría es que podríamos hacer lo mismo y solicitar un rendering por parte del otro objeto, pero hay que reconocer que ese caso es demasiado cargante… En tal caso para acceder a los campos del objeto enlazado sin romper el formalismo los de RBP recomiendan usar  el helper delegate

class Invoice < ActiveRecord::Base
  belongs_to :user
  delegate :name, :ap1, :address :to => :user, :prefix => true
end

<%= @invoice.user_name %>
...

El consejo es de 2010 y el helper ha sobrevivido sin deprecarse en todo este tiempo, así que podemos tener confianza también en que esto es parte del framework, aunque no se haga mucho énfasis en los tutoriales oficiales.

Queda por considerar aún la cuestión de qué controller tiene que atender a los eventos generados en cada parte de la vista. Aquí juegan las reminiscencias de la idea de iframes, donde de verdad cada pedazo de la página era una vista diferente y era natural aprovechar el nesting para pasar información a las vistas incrustadas.

Asi, en una vieja web de iframes, vieja pero ya con la convención path/controller/id/action?parameters=… , habríamos tenido una pagina /articles/1111/show? y en esa página una iframe que habría podido ser /articles/1111/comments/index? o simplemente /comments/index?article=1111. Si el partial de comments tuviera que generar enlaces a acciones, podría plantearse usar cualquiera de estos tres paths. Pero una vez desaparecieron las iframes, estaba el riesgo de que se forzara en un camino inadecuado el recargo de página . Y ahora que ha llegado el json, ha vuelto a desaparecer ese riesgo… pero no es necesario pasar toda la información en la url, así que igual conviene volver a pensar con caminos simplificados. Me parece que voy a tener que leer más para enterarme cual es la opinión. Menos mal que rails es un opinionated framework.

 

 

Leave a Comment

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.