homeASCIIcasts

279: El conducto de estáticos 

(view original Railscast)

Other translations: En Ja Fr

Other formats:

Written by Juan Lupión

El conducto de estáticos (N. del T: asset pipeline en inglés) es una de las funcionalidades más importantes de Rails 3.1, pero también es de las más confusas. En este episodio vamos a tratar de aclarar cómo se usa para gestionar los estáticos de nuestras aplicaciones Rails. Los que no estén familiarizados con esto pueden empezar por la Guía Rails sobre el conducto de estáticos porque cubre muchas de sus funcionalidades.

Los que ya hayan escrito una aplicación Rails 3.1 sabrán que si visitamos http://localhost:3000/assets/application.js veremos un archivo con todo el JavaScript que utiliza nuestra aplicación. Pero, ¿cómo funciona esto?

El fichero application.js no tiene nada de especial; de hecho cualquier fichero que se ponga en el directorio /app/assets/javascripts será accesible igualmente. Por ejemplo si en dicho directorio creamos un fichero llamado greeting.txt podemos verlo con el navegador cargando la URL http://localhost:3000/assets/greeting.txt. Nótese que aunque el fichero se encuentra en el directorio /app/assets/javascripts la URL que usamos para acceder a él es /assets/greeting.txt. Esto se aplica para cualquier subdirectorio de /app/assets en el que pongamos el archivo, podemos incluso crear un nuevo directorio y ponerlo ahí, que el fichero seguirá estando accesible en la misma URL (aunque si hacemos esto tendremos que reiniciar el servidor).

El directorio /app/assets no es el único sitio donde podemos añadir este tipo de estáticos. Si creamos un directorio en /lib/assets cualquier fichero que pongamos ahí podrá ser accedido igual que si hubiese sido puesto en /app/assets, y lo mismo para todos los ficheros que cuelguen de /vendor/assets.

Si tenemos archivos estáticos que no son realmente específicos de la aplicación nos puede venir bien ponerlos o bien en /lib o en /vendor. Por ejemplo si usamos un plugin de jQuery el directorio /vendor/assets es un buen sitio para poner los ficheros JavaScript porque es otra persona quien los mantiene. Igualmente podemos poner en /lib aquellos estáticos que mantenemos nosotros pero que no son específicos de la aplicación.

Simplificando, el conducto de estáticos no es más que una lista de rutas de carga. Podemos ver dicho listado consultado en la consola Rails.application.config.assets.paths:

> y Rails.application.config.assets.paths
--- 
- /Users/eifion/store/app/assets/images
- /Users/eifion/store/app/assets/javascripts
- /Users/eifion/store/app/assets/stylesheets
- /Users/eifion/store/lib/assets/greeting.txt
- /Users/eifion/store/vendor/assets/stylesheets
- /Users/eifion/.rvm/gems/ruby-1.9.2-p180@railspre/gems/jquery-rails-1.0.13/vendor/assets/javascripts

La salida muestra todos los directorios debajo de app/assets, de /lib/assets y de /vendor/assets. Al final aparece un directorio interesante que viene de la gema jquery-rails que hemos incluido en nuestra aplicación. Podemos ver sus contenidos con la orden bundle open.

$ bundle open jquery-rails

Con esto abriremos la gema con el editor de texto definido por las variables de entorno BUNDLE_EDITOR o EDITOR. Si examinamos los ficheros de la gema veremos un directorio llamado vendor/asset/javascripts y que contiene varios ficheros de jQuery que podemos cargar a través del conducto de estáticos.

Ficheros y estructura de carpetas de la gema jquery-rails.

Tal y como es de esperar, podemos acceder a cualquiera de estos archivos en la ruta assets en el navegador, porque todos estos directorios se encuentran en la ruta de carga del conducto de estáticos.

El fichero jquery.js file está accesible en la URL /assets.

Lo más interesante de esto es que las gemas de Ruby ya no sirven sólo para gestionar código Ruby, también podemos usarlas para gestionar JavaScript y otro estáticos. Probablemente veamos en un futuro más librerías de JavaScript distribuidas como gemas de Ruby de forma que puedan aprovecharse de la gestión de dependencias de Bundler.

Gestión de estáticos con Sprockets

Volvamos al fichero application.js de nuestra aplicación, y echémosle un vistazo.

/app/assets/javascripts/application.js

// This is a manifest file that'll be compiled into including all the files listed below.
// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
// be included in the compiled file accessible from http://example.com/assets/application.js
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
//= require jquery
//= require jquery_ujs
//= require_tree .

El fichero sólo contiene comentarios, pero son importantes. Este tipo de archivo se conoce como manifiesto y lo gestiona Sprockets internamente. Cuando llega una petición para este archivo, Sprockets mira el manifiesto y recopila todos los ficheros mencionados y pone sus contenidos antes del código que aparezca en este archivo.

Aquí también funcionan las rutas de carga. En este fichero tenemos require jquery (la extensión .js es opcional). Sprockets buscará en los directorios mencionados en la ruta de carga en busca de este archivo y, en este caso, lo cargará del directorio vendor/asset/javascriptsde la gema.

Se puede poner aquí cualquier fichero JavaScript que se encuentre en la ruta de carga. Así, si añadimos require jquery-ui a este archivo, se incluirá el código jquery-ui de la gema. Esto también se aplica a los ficheros de CoffeeScript, si incluimos require home se analizará e incluirá el fichero /app/assets/javascripts/home.js.

Sin embargo no hace falta incluir dicho fichero home porque al final de este archivo tenemos require_tree ., y el punto aquí quiere decir el directorio actual. Todos los ficheros JavaScript o CoffeScript que haya en este directorio y sus hijos serán automáticamente incluidos.

Podemos excluir algunos archivos si queremos. Supongamos que tenemos unas páginas de administración y sólo queremos incluir su JavaScript cuando estamos viendo una de estas págias. Por defecto estos ficheros serían incluidos para todas las páginas de nuestro sitio.

Si queremos ver qué ficheros se están incluyendo podemos añadir el parámetro debug_assets=1 a la URL, con lo que impediremos que se combinen los archivos JavaScript y si vemos el código fuente de la página veremos todos los archivos que Sprockets habría incluido, y veremos que también aparecen los del directorio admin.

Los ficheros JavaScript no se combinan si añadimos el parámetro debug_asssets en la URL.

Esto lo podemos soslayar de un par de formas. Por un lado podríamos utilizar require_directory en lugar de require_tree y esto incorporaría los ficheros del directorio actual, y no los del subdirectorio, y por otra parte si queremos tener más control podríamos hacer require de forma separada en lugar de incluir todo el directorio. Alternativamente podríamos mover los ficheros JavaScript a un directorio public y luego usar require_tree ./public para incluir sólo dichos archivos.

Cabe preguntarse qué comandos hay disponibles en el manifiesto de Sprocket, por desgracia no hay una fuente definitiva de documentación pero en el fichero directive_processor.rb existen comentarios que explican cómo funciona, así como las distintas órdenes que se le pueden pasar.

Preprocesado

El conducto de estáticos también puede preprocesar. Para ver cómo funciona esto crearemos un fichero llamado greeting.txt en el nuevo directorio /app/assets/anything. Podemos añadir otra extensión al nombre de archivo y especificar un procesador, por ejemplo .erb. Si ahora le añadimos código ERB a este archivo se evaluará correctamente.

/app/assets/anything/greeting.txt.erb

hello world <%= 1 + 1 %>

Si cargamos el fichero en un navegador veremos que el código ERB se ha ejecutado correctamente. Nótese que no incluimos la URL no incluye la extensión del preprocesador.

Se preprocesa el código erb antes de enviar el fichero al navegador.

Así es como básicamente funcionan SASS y CoffeeScript. Cuando un fichero tiene la extensión .sc ésta indica que hay que preprocesarlo con SASS. Podemos encadenar este tipo de extensión y crear un fichero con la extensión, por ejemplo .scss.erb, con lo que el fichero primero pasaría por ERB y luego por SASS.

El preprocesador es muy configurable. Podemos añadir nuestros propios procesador o intercambiar los ya existentes, todo esto se controla con la gema Tilt, en cuya documentación se puede encontrar toda la información necesaria.

Diferencias en modo de producción

Con esto terminamos esta pequeña guía sobre la funcionalidad del conducto de estático, pero hay algunas diferencias de funcionamiento en producció. Primero, arrancaremos el servidor en modo de producción.

$ rails s -e production

Si visitamos la página principal de nuestra aplicación y vemos el código fuente compobaremos que los estáticos se sirven de forma diferente.

<link href="/assets/application-412fe22651f4486c51e54176003a9f57.css" media="screen" rel="stylesheet" type="text/css" />
  <script src="/assets/application-3e3a5167191afa70c7b72440eee7dd40.js" type="text/javascript"></script>

Por motivos de caché, ahora los nombres de archivo aparecen con un guión. Esto da mejor resultado que el viejo método que usa Rails 3.0 de añadir una cadena porque cambia realmente el nombre del archivo. También si vemos el contenido del fichero veremos que el JavaScript ha sido minimizado, lo que ahorra ancho de banda.

Estos estáticos se cachean automáticamente y son servidos por el middleware Rack Cache, lo que es bastante rápido. Si queremos que sea el propio servidor web quien sirva estos estáticos podemos precompilarlos lanzando

$ rake assets:precompile

Con esto se precompilarán los estáticos en el directorio /public de forma que serán accesibles desde el servidor web.

Con esto concluimos este episodio sobre el conducto de estáticos. No olvideis visitar la Guía de Rails que contiene más información.