#208 ERB Blocks in Rails 3
- Download:
- source codeProject Files in Zip (162 KB)
- mp4Full Size H.264 Video (14.9 MB)
- m4vSmaller H.264 Video (9.6 MB)
- webmFull Size VP8 Video (25.7 MB)
- ogvFull Size Theora Video (18 MB)
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
.
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
.
<% 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.
<%= 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.
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:
<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
:
<%= 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.
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.
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Í:
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.
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í:
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.
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.