homeASCIIcasts

208: Bloques con ERB en Rails 3 

(view original Railscast)

Other translations: En It

Other formats:

Written by Juan Lupión

Ya ha sido liberada la segunda versión beta de Rails 3.0 y uno de los cambios más significativos es la forma en la que se gestionan los bloques en las vistas. En este episodio veremos qué es l que ha cambiado.

Actualización de Ruby y Rails

Antes de actualizarnos a la nueva beta de Rails 3 vamos a actualizar primero nuestra versión de Ruby a 1.9.2 En el episodio 200 [verlo, leerlo] vimos cómo utilizar rvm para instalar Ruby 1.9.1 pero esta versión tiene algunos errores y la 1.9.2 parece más robusta (aun cuando sigue estando en desarrollo).

Con rvm tenemos que ejecutar este comando para instalar Ruby 1.9.2:

rvm install ruby-head

Con esto se descargará la última versión de Ruby 1.9.2, que aún está en desarrollo. Una vez que rvm haya instalado la nueva versión podemos cambiar a ella con

rvm ruby-head --default

Nótese que hemos utilizado la opción --default para que 1.9.2 sea la versión por defecto de Ruby, lo que quiere decir que si abrimos otras ventanas de terminal también tendrán como versión de Ruby la 1.9.2.

La beta 2 de Rails 3 requiere que tengamos RubyGems 1.3.6 así que antes de instalarlo tendremos que hacer

gem -v

para ver qué versión tenemos instalado. Si tenemos una anterior a la 1.3.6 podemos hacer

gem update --system

Ya podemos instalar Rails 3.0 beta 2 con:

gem install rails --pre

Actualizando las aplicaciones

Podemos actualizar cualquier aplicación que hayamos desarrollado con la anterior beta de Rails simplemente cambiando el número de versión que figura en el Gemfile de 3.0.0.beta a 3.0.0.beta2.

/Gemfile

gem "rails", "3.0.0.beta2"

Tras esto podemos ejecutar bundle install para que se resuelvan todas las dependencias.

Los cambios en erb

Hay dos tipos de etiquetas que se usan en los ficheros erb. Si el código aparece entre etiquetas <%= %> en la vista aparecerá lo que devuelva la evaluación del código Ruby insertado entre ellas. Si omitimos el signo de igualdad el código se interpretará pero su salida será descartada. Se trata de un concepto muy común en erb que ha sido extendido en Rails 3 para que funcione dentro de bloques.

Esto aparece frecuentemente en los formularios. Abajo figura una plantilla erb correspondiente al formulario de un modelo Product.

/app/views/products/_form.html.erb

<% form_for @product do |f| %>
  <%= f.error_messages %>
  <p>
    <%= f.label :category_id %><br />
    <%= f.collection_select :category_id, Category.all, :id, :name %>
  </p>
  <p>
    <%= f.label :name %><br />
    <%= f.text_field :name %>
  </p>
  <p>
    <%= f.label :price %><br />
    <%= f.text_field :price %>
  </p>
  <p>
    <%= f.label :description %><br />
    <%= f.text_area :description %>
  </p>
  <p><%= f.submit "Submit" %></p>
<% end %>

En la primera línea de este código hemos utilizado form_for, que insertará etiquetas de formulario en la vista rodeando el contenido del bloque... pero en las etiqueta erb no aparece el signo igual, lo que viola la regla de que los bloques erb que devuelven marcado en la vista deberían utilizar <%= %> y ha hecho que sea difícil trabajar dentro de form_for en versiones anteriores de Rails. A partir de la nueva beta ya podemos utilizar signos de igual como con cualquier otro código erb que genera texto en HTML.

/app/views/products/_form.html.erb

<%= form_for @product do |f| %>
  <!-- rest of form -->
<% end %>

Estamos usando el símbolo de igualdad aunque le estemos pasando un bloque a form_for (si bien hay que tener en cuenta que la etiqueta de cierre end no lo lleva) Esto insertará la etiqueta form y los contenidos del formulario correctamente, y sirve también para arreglar el interior de form_for, como veremos en breve.

Cabría preguntarse cuándo hace falta utilizar el símbolo de igualdad y cuando no. Echémosle un vistazo a un par de ejemplos empezando por div_for:

<%= div_for @product do %>

<% end %>

En versiones anteriores de Rails no hacía falta usar el símbolo de igualdad pero ahora es necesario porque estamos devolviendo contenido alrededor del bloque (en este caso una etiqueta div). De hecho la mayoría de los helpers de Rails ahora exigen este símbolo porque siempre escriben algo rodeando el bloque que aceptan.

Sin embargo aún quedan algunos casos en los que no es necesario este símbolo. Por ejemplo cualquier cosa que utilice el método each:

<% @comments.each do |c|%>

<% end %>

El método each no devuelve nada que queramos sacar en la vista, por tanto no se debe utilizar el símbolo de igualdad dado que lo que devuelve este método no se visualiza.

Otro ejemplo es el método content_for.

<% content_for :side do %>

<% end %>

Aquí tampoco se usa el símbolo de igualdad porque la llamada a content_for guarda el contenido del bloque en una variable que se utilizará más tarde. Nada se muestra en la vista, por tanto no hay que usar el símbolo de igualdad.

Por desgracia hay una excepción a esta regla: el método cache. Idealmente, cache debería utilizar un símbolo de igualdad porque podría devolver cierto contenido en la vista pero no lo hace debido a su funcionamiento interno.

<% cache do %>

<% end %>

Uso de bloques en métodos helper

Estos cambios pueden parecer un poco confusos al principio pero una vez que nos acostumbremos cobrarán más sentido. El motivo principal de este cambio es que con él se reorganiza el código interno. En el episodio 40 veíamos cómo utilizar bloques en las vistas en las versiones anteriores de Rails. Era necesario utilizar el método concat para poder poner texto por delante o por detrás del bloque, lo que hacía un poco díficil el trabajo.

def admin_area(&block)
  concat('<div class="admin">', block.binding)
  block.call
  concat("</div>", block.binding)
end

Vamos a ver, como demostración de que como es mucho más fácil utilizar ahora bloques en los métodos, una sencilla aplicación de tienda que debe mostrar un número de enlaces pero que sólo serán visibles a los administradores.

La ficha de un producto mostrando los enlaces del administrador.

Queremos que los enlaces “Edit”, “Destroy” y “View All” sólo sean visibles por el administrador y también queremos rodear los enlaces de una etiqueta div. Los enlaces se crearán en la vista con el siguiente código:

/app/views/products/show.html.erb

<p>
  <%= link_to "Edit", edit_product_path(@product) %> | 
  <%= link_to "Destroy", @product, :confirm => "Are you sure?", :method => :delete %> | 
  <%= link_to "View All", products_path %>
</p>

Vamos a reemplazar las etiquetas del párrafo con un nuevo método helper que llamaremos admin_area:

/app/views/products/show.html.erb

<%= admin_area do %>
  <%= link_to "Edit", edit_product_path(@product) %> | 
  <%= link_to "Destroy", @product, :confirm => "Are you sure?", :method => :delete %> | 
  <%= link_to "View All", products_path %>
<% end %>

Este método admin_area añadirá la etiqueta div y mostrará u ocultará los enlaces según el usuario registrado sea o no administrador. Obsérvese que debido a que nuestro método envía salida a la vista hemos utilizado un símbolo de igualdad en la etiqueta de apertura.

Vamos a definir el método en application_helper Lo mejor de esta nueva forma de trabajar con bloques en los helpers es que se comporta exactamente como sería de esperar, de forma que si el método sólo devuelve una cadena, eso es lo que se mostrará en la vista.

/app/helpers/application_helper.rb

module ApplicationHelper
  def admin_area(&block)
    "OH HAI!"
  end
end

Si ahora recargamos la página el contenido del bloque será reemplazado por la cadena devuelta por el método admin_area. Los enlaces no se muestran dado que no estamos ejecutando el bloque en el método.

The contents of the block are replaced by the string.

Para ejecutar el bloque y devolver el contenido tenemos que llamar a un nuevo método denominado with_output_buffer y pasarle el bloque. Esto hará que se evalúe el bloque en un buffer distinto de salida de forma que el contenido no se muestre directamente en la vista. Podemos asignar dicha salida a una variable y luego hacer lo que queramos con ella. En este caso queremos rodear el contenido de un div con una class llamada admin por lo que vamos a modificar nuestro método admin_area asÍ:

/app/helpers/application_helper.rb

def admin_area(&block)
  content = with_output_buffer(&block)
  content_tag(:div, content, :class => 'admin')
end

Cuando recarguemos la página veremos los enlaces de administración rodeados de un div. Como y ahemos creado un estilo para la clase admin en la hoja de estilos de la aplicación, dichos estilos se aplicarán sobre esta capa.

El bloque queda rodeado de una elemento div.

Examinando el código fuente de la página veremos el div rodeando los enlaces.

<div class="admin">
  <a href="/products/1/edit">Edit</a> | 
  <a href="/products/1" data-confirm="Are you sure?" data-method="delete" rel="nofollow">Destroy</a> | 
  <a href="/products">View All</a>
</div>

Por tanto podemos usar el método with_output_buffer para obtener el contenido evaluado de un bloque en una vista. Dicho esto, el código que hemos escrito en nuestro helper no era más que un ejemplo sencillo y de hecho existe una forma más eficiente de hacer lo mismo.

El método content_tag puede recibir un bloque como argumento, lo que significa que podemos reescribir nuestro helper así:

/app/helpers/application_helper.rb

def admin_area(&block)
  content_tag(:div, :class => 'admin', &block)
end

Por supuesto para que este método sea totalmente funcional querremos mostrar la salida sólo si el usuario es administrador, lo que podemos conseguir utilizando un método que devuelva un valor booleano.

/app/helpers/application_helper.rb

def admin_area(&block)
  content_tag(:div, :class => 'admin', &block) if admin?
end

Eso es todo por este episodio. Es posible que tardemos un poco en acostumbrarnos a utilizar signos de igualdad con bloques en nuestras vistas, pero a largo plazo veremos que tiene más sentido, porque estos bloques devuelven contenido. Y además esto redunda en una mejora de la implementación del código de las vistas en Rails.

Por último, un truco final. En versiones anteriores de Rails podíamos añadir un símbolo de sustracción al principio o final de una etiqueta erb para eliminar los espacios en blanco, para que el marcado quedase un poco más limpio. En Rails 3 esto ya no es necesario y si una etiqueta erb no devuelve salida será automáticamente eliminada, por lo que dicha etiqueta no generará espacios en blanco innecesarios.