Normalmente cuando leemos a alguien evangelizando sobre RoR se tratan los aspectos más espectaculares a primera vista del framework, a saber: generación automática de código a partir del esquema de la base de datos (scaffolding o andamiaje), separación conceptual imbricada en la misma esencia de la aplicación que nos lleva naturalmente a un código más organizado, soporte de AJAX usando Script.acoul.us, integración de pruebas automáticas del código…
Sin embargo normalmente no se le suele dar la suficiente importancia a una característica que, a mi juicio, tiene la misma relevancia que las anteriormente mencionadas. Ruby on Rails es extensible, y mucho. Y además lo es de una manera sencilla e intuitiva que sólo un lenguaje como Ruby puede permitir. Me remito a un ejemplo:
#!/usr/bin/env ruby
require 'Time'
r=Time.now
puts r.rfc822
Time::RFC2822_DAY_NAME=['Dom','Lun','Mar','Mie','Jue','Vie','Sab']
s=Time.now
puts r.rfc822
puts s.rfc822
Al ejecutar este script obtendremos lo siguiente:
Sun, 29 Jan 2006 19:04:10 +0100
./fechas.rb: 11: warning: already initialized constant RFC2822_DAY_NAME
Dom, 29 Jan 2006 19:04:10 +0100
Dom, 29 Jan 2006 19:04:10 +0100
Nótese que al redefinir una contante de la clase Time de Ruby estamos afectando no sólo a las instancias que se han definido a partir del cambio, sino también a las que ya estaban definidas. O lo que es lo mismo, podemos cambiar las mismas tripas del lenguaje dinámicamente (en este caso, de la librería estándar). Como es obvio, esto no es precisamente algo con lo que querramos complicarnos la vida por lo general, de forma que el intérprete de Ruby emite un aviso por la salida de error. Podemos redefinir cualquier cosa de una clase, no sólo una constante como en este caso sino métodos o incluso cambiar el carácter de lectura o escritura de un atributo
Por supuesto, Rails saca partido a esta camaleónica propiedad de Ruby y lo hace con lo que el mecanismo de plugins.
¿Qué son los plugins?
Son extensiones de Ruby on Rails que amplían el framework añadiendo o modificando cierta funcionalidad usando técnicas parecidas a las que hemos visto más arriba. Los plugins fueron introducidos oficialmente en Rails en la versión 1.0RC1, así que si tienes una versión anterior a esta más vale ir actualizándose
Un plugin es un ciudadano de primera clase: una vez que ha sido instalado (e inicializado en tiempo de ejecución) para el código de nuestra aplicación no hay forma de distinguir si cierta funcionalidad usada por nuestro proyecto está incluida en Rails, está proporcionada por algún plugin instalado, o está incluida en el core de Rails pero un plugin la ha interceptado y nos suministra una versión adicional.
El concepto es tan poderoso que puede usarse no sólo para extender el framework, sino también, como nos cuenta Chad Fowler, para ¡parchear código defectuoso!
Gestión de nuestros plugins
Otra característica interesante de estas extensiones es que se instalan a nivel de aplicación al contrario que, por ejemplo, otros mecanismos de extensión como las gems que afectan a todas las aplicaciones que tengamos en nuestra instalación de Ruby. Esto nos permite gran flexibilidad a la hora de probar y desplegar las extensiones que usamos. Sobre todo para esto último porque los plugins siempre se encuentran instalados en el directorio vendor/plugin de nuestra aplicación así que (salvo que no dependan de algún Gem específico) siempre los podemos subir y bajar a nuestro servidor sin preocuparnos de mucho más que satisfacer las posibles dependencias entre extensiones (lo cual se satisface si subimos el directorio en su totalidad)
Añado aquí que, aunque en teoría no es imprescidible, sí que es algo más que recomendable disponer de Subversion en nuestra máquina de desarrollo pues la mayoría de los plugins disponibles sólo pueden ser instalados usando un cliente de Subversion. Importante: no es necesario que pongamos nuestra aplicación bajo control de Subversion, sólo se usa este sistema de control de versiones para descargar el código de la extensión deseada.
Teniendo en cuenta lo anterior, sólo necesitamos saber que al igual que hay un repositorio más o menos oficial de extensiones a Ruby (las Gems) hay otro repositorio de plugins de Ruby on Rails, que viene dado por el conjunto de repositorios que están descritos en la esta página del wiki de Rails Con la herramienta adecuada podemos descargar e instalar en nuestra aplicación la extensión que deseemos, actualizarla, borrarla, etcétera. Y la herramienta adecuada no es otra que nuestro nuevo amigo, ruby script/plugin. Recordad que si no tenéis este fichero en vuestra aplicación, tenéis que migrar a una versión más actual de Rails.
script/plugin nos proporciona la panoplia habitual de comandos de gestión de paquetes, como install, list, delete, etc. No hace falta recordarlos todo, lo único que hay que recordar es que tenemos a nuestra disposición ruby script/plugin --help para cuando nos encontremos perdidos.
¿Qué plugins hay disponibles?
Al visiar la página anteriormente mencionada vereis que, como en botica, hay de todo. Desde extensiones para hacer más fácil definir restricciones o relaciones entre entidades de nuestro modelo (act_as_taggable, por ejemplo, promete ayudarnos a implementar folksonomías), hasta amplicaciones del sistema de vistas de RoR que integran Google Maps o generan PDFs”. Cada uno de ellos trata de ser una solución autocontenida a una necesidad específica. Una advertencia: instalar y usar un plugin en cierto modo es casarse con él. Deberíamos escoger usar extensiones que estén siendo usadas por un número de usuarios, lo que nos debería garantizar un mínimo de madurez y estabilidad en el código que estamos metiendo en nuestra aplicación. En caso contrario, deberíamos estar dispuestos a resolver cualquier incidencia, en caso de que la extensión quede sin soporte.
¿Y qué pasa con los componentes de Rails?
En la página 364 del libro Agile Web Development With Ruby on Rails nos dicen que la idea de los componentes es que uno pueda encontrar componentes que pueda enchufar en la aplicación y simplemente añadirlos al directorio components. ¿No es esta la misma idea muy parecida a la de los plugins (que viven en vendor/plugins)?
Y mi opinión es que los componentes son un esbozo o rudimento de plugins, que han sido totalmente remozados para la versión 1.0 de Rails. Entre las limitaciones de los componentes tenemos:
- Se limitan a ser extensiones que consisten en controladores y vistas, nunca tratan con los modelos.
- No se integran de manera transparente en nuestra aplicación, sino que tenemos que invocarlos con una sintaxis específica (
render_component) - No hay un sistema de instalación y gestión de los componentes que tengamos instalados más allá de
cpymv
A consecuencia de esto, no ha florecido un ecosistema de componentes que podamos buscar o descargar para nuestras aplicaciones, lo que sí que ha ocurrido con los plugins. Sin embargo, los componentes tienen sus ventajas: son más sencillos de usar y de comprender (normalmente consisten en unos cuantos controladores y algunas vistas específicas) y son más adecuados para empotrarlos en nuestros layouts. En definitiva, fomentan la reusabilidad del código dentro de nuestra aplicación, no fuera de ella. Es complicado usar un componente de un proyecto en otro.
En definitiva, a la hora de componentizar nuestra aplicación, yo trataría de aislarla en componentes y, sólo en contadas ocasiones debería necesitar escribir un plugin. Por el contrario, si lo que quiero es ir de compras y descargar una extensión ya escrita por alguien, los plugins_ son el camino a seguir.
Engines
Los engines son una nueva vuelta de tuerca: se trata de extensiones de la extensión, por así decirlos. Un engine es un plugin que para hacer su labor se apoya sobre las extensiones instaladas por otro plugin (el plugin engine). Los engines son auténtica miniaplicaciones de arriba a abajo, e implementan subsistemas completos (por ejemplo, un sistema de autenticación de usuarios), incluyendo modelos, controladores y vistas para empotrar en el layout del proyecto que utiliza el engine en cuestión.
Pero esto no acaba aquí. Si los plugins se limitaban a extender el framework añadiendo cierta funcionalidad por ejemplo a nuestros modelos, los engines van más allá. Un engine puede incluir prácticamente cualquier cosa que queramos incluir en la aplicación anfitriona: tanto contenido dinámico como estático. Esto se ve claramente en las trazas de WEBRick, cuando hacemos una petición a un recurso proporcionado por un engine, porque nos muestran como Rails va buscando este recurso no sólo en app/ sino en los directorios de engines que cuelgan de /vendor/plugins
Advertencias
¿Es realmente conveniente que exista esta librería de componentes de alto nivel (plugins, engines, etc.) que podamos instalar y desinstalar a nuestro gusto? A priori la respuesta es sí, pero David Heinemeier tiene sus objeciones
Según su argumento en la vida real los componentes de alto nivel (sean componentes, plugins o engines) son peligrosos porque son difíciles de reusar y de parametrizar (o sea, de configurar para cada proyecto). En definitiva (siempre según David) este tipo de intentos termina siendo más complicado y costoso de mantener que escribir el código por nuestra cuenta. ¿Sistemas de login y autenticación? ¿Calendarios? ¿Foros? David cree que es mejor dar una infraestructura que haga muy sencillo implementar estos componentes de alto nivel que suministrar versiones precongeladas. Los generadores y scaffolds consisten precisamente en eso, generar automáticamente un esqueleto que sea sencillo de refinar y rematar.
Este último enfoque no está exento de problemas: si actualizamos nuestro generador de código con nuevas funcionalidades o errores corregidos, ¿cómo hacer que el generador vuelva a generar el código y respete los cambios que hayamos hechos sobre éste? Esta era la motivación principal del proyecto de engines que hemos presentado arriba.
A qué viene todo esto
Pues viene a que en la próxima revisión de nuestro Sudokuonrails instalaremos un sistema de autenticación de usuarios usando el LoginEngine, lo que nos llevará a actualizar la aplicación a Rails 1.0, configurar una base de datos, instalar los plugin engine y LoginEngine... permanezcan atentos a sus agregadores.


February 24th, 2006 at 12:13 PM el warning: already initialized constant RFC2822_DAY_NAME puedes evitarlo con Time::RFC2822_DAY_NAME.replace(['Dom','Lun','Mar','Mie','Jue','Vie','Sab']) Saludos