Skip navigation.
Home Home

Aprendiendo más sobre Rails y Ruby

Experiencias e informes
Experiencias e informes

Tras conseguir instalar Ruby on Rails en la última sesión ha llegado la hora de entrar en materia. Como ya comenté mi idea era partir de los artículos del último número de la revista on-line ObjectiveView. Gracias a ellos he conseguido crear mi primera aplicación Ruby on Rails y por el camino también he aprendido mucho más tanto sobre este framework como sobre Ruby. Os cuento como ha sido la experiencia.

El primer artículo de la revista es sobre Ruby. Lo he leído rápidamente por encima y no me ha entusiasmado aunque si me he quedado con algunas de las peculiaridades de Ruby. En realidad tenía ganas de llegar al artículo "Ruby on Rails" de Obie Fernandez. Todo lo que escribo a continuación te resultará mucho más útil si has seguido, estás siguiendo o piensas seguir próximamente dicho artículo.

Siguiendo el artículo sobre Ruby on Rails

Siguiendo las instrucciones de los primeros párrafos he creado la aplicación ejecutando:

$ rails sprinttracker 

Me ha gustado mucho la idea de que tras ejecutar el comando ya tengo una aplicación ejecutable y a la que puede accederse desde el navegador. Es más fácil y gratificante ir construyendo poco a poco sobre algo que funciona que escribir líneas y líneas que probablemente no funcionarán a la primera ni a la segunda.

Esta primera parte del artículo está bien escrita y con mucho detalle así que basta con seguir sus ejemplos. El primer paso es la configuración de la base de datos. Por defecto viene configurada para MySQL pero tal y como sugieren en el artículo la cambiamos por SQLite que almacena los datos en ficheros (al estilo de HypersonicSQL en Java). Para ello hay que instalar la rubygem correspondiente:

$ sudo apt-get install libsqlite3-dev sqlite3
$ gem install sqlite3-ruby
 2. sqlite3-ruby 1.1.0 (ruby)

Vaya, no me ha hecho mucha gracia que me instalara cosas en /usr/local. Supongo que es la forma de actual de rubygems y habrá que acostumbrarse.

Al configurar la base de datos veo que en Rails está incluido soporte para varios entornos (por defecto desarrollo, pruebas y producción). ¡Qué gran idea! Esta es una de las necesidades en el mundo Java que inspiró EasyConf y en Rails está considerada de entrada.

El siguiente paso es crear el modelo de datos. Interesante el poder expresarlo en un lenguaje propio de Ruby que es independiente de la base de datos. Y más interesante aún el sistema de migraciones. Funciona de forma similar a como lo hace Liferay: escribiendo el número de versión del esquema en la base de datos y comparando con la versión actualmente en ejecución.

El artículo pasa después a explicar Rake que es tremendamente familiar viniendo del mundo de ant. Rails viene con muchos 'targets' predefinidos al estilo de lo que se obtiene con Maven y la forma de extenderlos es muy elegante.

Seguimos entrando en materia, esta vez creando el modelo de dominio. El artículo explica como desarrollar un ejemplo de autenticación de usuario así que hay que crear una clase User:

$ ruby script/generate model User

Hmm, nunca me ha gustado mucho la generación de código, vamos a ver que ha generado:

class User < ActiveRecord::Base
end

¡Ah! que sencillo, y según explican la clase ya es funcional. Parece que rails usa la generación de código para reducir la curva de entrada, pero genera muy poco código con lo que se evitan la mayoría de los peligros de esta estrategia y se convierte en una herramienta opcional. Rails también tiene otro sistema de generación de código al que denominan scaffolding que consiste en la generación dinámica de código en tiempo de ejecución. Este tipo de generación de código me gusta más dado que dicho código nunca lo ve el programador ni va a tener que modificarlo.

Tras unas pruebas con SQLite3 que resultan muy útiles para hacerse con su entorno, en el apartado HTTP Request Handling se explica el sistema usado para asociar URLs con la lógica que se ejecuta. Es un sistema basado en reglas realmente brillante, ¿porqué no se le ha ocurrido a nadie antes?. Llevo un tiempo pensando en ampliar el sistema de URLs amigables de Liferay así que creo que tomaré este como modelo. Por cierto en mi caso los mensajes de error no se corresponden con los que el artículo dice que van a mostrarse, pero no tiene ninguna importancia.

Ahora pasamos a uno de los platos fuertes: ¡programación dirigida por pruebas (TDD) con Ruby on Rails! Mi valoración del sistema de pruebas de Ruby y de cómo Rails lo extiende para facilitar el desarrollo de aplicaciones web es excelente. Valga decir que los ejemplos del artículo son pruebas de sistema mas que pruebas unitarias dado que involucran una base de datos. Pero la facilidad que da Rails para popular la base de datos con datos de ejemplo y limpiarla tras cada test (al estilo de un dbUnit integrado) hacen que en la práctica resulten como si fueran pruebas unitarias.

Siguiendo el estilo TDD el artículo muestra varios métodos de validación incorporados en Ruby. En particular se muestra como validar:

  • Que se ha pasado un campo (validates_presence_of)
  • Que el valor del campo no está siendo usado ya (validates_uniqueness_of)
  • Que el campo tiene una longitud determinada (validates_length_of)

El sistema es muy sencillo y parece muy potente. No se aún si será un sustituto completo de la funcionalidad proporcionada por el validador de Struts, dado que en el artículo no muestran cómo se podría presentar al usuario un mensaje como resultado de la validación.

En fin, aquí es donde he empezado a tener problemas. Primero he tenido que resolver varios errores de compilación provocados por fallos míos o por errores tipográficos del artículo. Por cierto, en Ruby no se usan ';' por lo que los saltos de línea adquieren mucha relevancia. Si copias el código del artículo ten en cuenta que hay saltos de línea que en realidad no deberían estar ahí y que son culpa de la maquetación del artículo en columnas tan estrechas.

Toca estudiar

Sin embargo, después de corregir todos los errores de compilación dos tests no pasaban. Además no me sentía muy cómodo con el código que había escrito (ejem, copiado) porque no entendía demasiadas cosas. Así que he decidido buscar documentación sobre Ruby. Para ahorrar tiempo he buscado en Google artículos sobre Ruby para programadores Java y he encontrado algunos interesantes. En particular recomiendo 10 Things Every Java Programmer Should Know About Ruby por que es muy breve pero deja muy claras las diferencias de conceptos y da una idea de la potencia de Ruby. También leí otros artículos como este, este, este (parte 2, parte 3) o este, pero creo que es mejor pasar directamente a los dos primeros capítulos del libro Programming Ruby on Rails y aprovechar que está disponble gratuítamente en la red (1. Ruby.new y 2. Classes, Objects, and Variables)

Retomando la aplicación de ejemplo

El estudio ha dado sus frutos, al releer el código ahora entiendo prácticamente todo y además descubro cual era el problema: había cambiado " por ' pensando que era lo mismo pero en Ruby hay diferencias. En particular las dobles comillas soportan interpolación, que quiere decir que puedes poner variables usando la sintaxis "valor de variable = #{nombre_var}".

Sigo con el artículo. Esta última parte tiene unos cuantos errores, destaco los más importantes:

  • Hay un error en login_controller.rb: Person en lugar de User
  • Falta pintar mensaje devuelto por el controlador en login.rhtml: <%= @flash[:notice] %>
  • Falta añadir a la base de datos un usuario. Añado los mismos datos que los usados para la prueba:
    jorge@gandalf:~/proyectos/sprinttracker$ sqlite3 db/development.sqlite3
    SQLite version 3.2.1
    Enter ".help" for instructions
    sqlite> .dump
    BEGIN TRANSACTION;
    CREATE TABLE schema_info (version integer);
    INSERT INTO "schema_info" VALUES(2);
    CREATE TABLE users ("id" INTEGER PRIMARY KEY NOT NULL, "login" varchar(40), 
       "created_at" datetime, "updated_at" datetime, 
       "crypted_password" varchar(255), "salt" varchar(255));
    COMMIT;
    sqlite> insert into users values (1, "quentin", "2006-03-18", "2006-03-18", 
        "be61f3ff72492591afe5081857a8ff17a85b21f9", 
        "62a636a58d0648eadf7410aa2e4444866174c96e");
    sqlite> .dump
    BEGIN TRANSACTION;
    CREATE TABLE schema_info (version integer);
    INSERT INTO "schema_info" VALUES(2);
    CREATE TABLE users ("id" INTEGER PRIMARY KEY NOT NULL, "login" varchar(40), 
       "created_at" datetime,  "updated_at" datetime, "crypted_password" varchar(255), 
       "salt" varchar(255));
    INSERT INTO "users" VALUES(1, 'quentin', '2006-03-18', '2006-03-18', 
        'be61f3ff72492591afe5081857a8ff17a85b21f9', 
        '62a636a58d0648eadf7410aa2e4444866174c96e');
    COMMIT;
    
  • Está almacenando session[:person] pero luego busca session[:person_id]. Es necesario cambiarlo para mantener la coherencia, me decanto por el segundo y renombro la clave como :user_id para ser consistente también con el resto del código. Esto obliga ha hacer algún que otro cambio en el método login.

Una vez solucionados estos problemas... ¡ya tengo mi primera aplicación en Ruby!

Registro del proyecto en RubyForge.org

RubyForge es el SourceForge para proyectos Ruby así que lo he elegido para alojar SprintTracker. En unas pocas horas me han aceptado la petición del proyecto y estaba listo el repositorio Subversion. Es una pena que no usen el protocolo https sino svn+ssh.

He aprovechado para subir el código fuente como la revisión 1:

$ mkdir main/ main/branches main/tags
$ mv sprinttracker main/trunk
$ svn import svn+ssh://jferrer@rubyforge.org//var/svn/sprinttracker

Como el artículo original no viene con el código seguramente te resultará útil acceder a la revisión 1 de SprintTracker si tienes problemas.

Experimento
Pues esto es todo por esta segunda sesión. He pensado que podía ser interesante medir los tiempos que tardo en aprender Ruby y Rails y cuanto tiempo tardo en tener lista una primera versión de la aplicación. He usado karm para medir los tiempo y por ahora he dedicado unas 8 horas de aprendizaje.

La siguiente sesión comenzaré a crear las tablas y clases de dominio para proyectos y sprints.

Gracias!