Skip navigation.
Home Home

Gestión de usuarios en Rails: hacia SprintTracker v0.1

Experiencias e informes
Experiencias e informes

Ya me hago a la idea de lo que es una aplicación con Rails con suficiente claridad como para embarcarme en la tarea de crear la primera versión de SprintTracker. El objetivo de esta versión es que permita gestionar usuarios y proyectos, crear sprints asociados a proyectos, crear tareas dentro de un sprint y asignárselas a un desarrollador y por último ir añadiendo (re)estimaciones de la tarea durante los días que dura un sprint. La interfaz de usuario no me preocupa por ahora y dejaré la que genere Rails.

Primeros pasos

En primer lugar me decido probar un IDE específico de Ruby y encuentro RadRails así que digo adios a kate. También me pongo a buscar más artículos de Rails y me encuentro con una recopilación de los 12 mejores artículos de Ruby on Rails. A leer se ha dicho.

Una de las primeras cosas que se aprende de Rails es obliga a desarrollar de abajo a arriba: es decir empezando por el modelo de datos y llegando hasta la interfaz. Por ello es interesante comenzar la aplicación pensando bien el modelo de datos. El de SprintTracker es este:

Generación de código de persistencia en Rails

Rails ofrece dos modelos de generación de código (scaffolding) que podríamos denominar generación dinámica y generación por script

En la generación dinámica el desarrollador nunca llega a ver el código dado que este se genera en tiempo de ejecución. Un caso típico de uso sería ejecutar:

$ ruby script/generate controller User

que genera una serie de ficheros mínimos (sin código) para dar soporte a una herramienta de gestión de usuarios. Para añadir soporte de persistencia edito el fichero user_controller.rb generado y añado (en negrita):

  class UserController < ApplicationController
    scaffold :user
  end

Tras hacer esto abro el navegador y voy a /user/list. Rails ha generado las pantallas y el código necesario para crear, listar, editar y borrar usuarios. Si tienes la oportunidad pruébalo, es muy impresionante.

La generación via script consiste en generar el código con la ejecución de un comando. El código se inserta en los ficheros de nuestro entorno y el desarrollador puede verlo y modificarlo. Siguiendo con el ejemplo anterior, para la herramienta de gestión de usuarios ejecutaría:

$ ruby script/generate scaffold User

Inicialmente me gustaba mucho más el primer modelo, sin embargo rápidamente me encontré con su principal inconveniente. La aplicación generada por defecto obligaba al usuario a introducir la contraseña cifrada (dado que es el campo que hay en base de datos) ¡y no parece haber ninguna manera de cambiarlo!

Esto me obligaba a usar la generación por script. A la generación de código de este tipo le veo dos inconvenientes fundamentales que también están presentes en Rails:

  • Te obliga a entender el código que ha generado para poder modificarlo: afortunadamente es bastante fácil de entender con lo que no ha sido un problema tan grave
  • Si lo modificas y cambias el modelo de datos no puedes regenerar sin perder los cambios

Sin embargo es la única opción que tenía porque necesitaba hacer cambios sobre lo generado. Leyendo el código generado y estudiando un poco la API de rails consigo crear una herramienta de gestión de usuarios en poco más de una hora.

Os muestro un par de ejemplos de cómo ha quedado. El formulario de creación y alta de usuarios es así:

<p><label for="user_login">Login</label><br/>
<%= text_field 'user', 'login'  %></p>

<p><label for="user_password">Password</label><br/>
<%= password_field 'user', 'password'  %></p>

<p><label for="user_password_confirmation">Password Confirmation</label><br/>
<%= password_field 'user', 'password_confirmation'  %></p>

Y la página de listado de usuarios así:

<table>
  <tr>
  <% for column in User.content_columns %>
    <% unless (column.name == 'salt' or column.name == 'crypted_password') %>
      <th><%= column.human_name%></th>
    <% end %>
  <% end %>
    <th> </th>
    <th> </th>
    <th> </th>
  </tr>
  
<% for user in @users %>
  <tr class="<%= cycle("even", "odd") %>">
  <% for column in User.content_columns %>
    <% unless (column.name == 'salt' or column.name == 'crypted_password') %>
     <td><%=h user.send(column.name) %></td>
    <% end %>
  <% end %>
    <td><%= link_to 'Show', :action => 'show', :id => user %></td>
    <td><%= link_to 'Edit', :action => 'edit', :id => user %></td>
    <td><%= link_to 'Destroy', { :action => 'destroy', :id => user }, 
             :confirm => 'Are you sure?' %></td>
  </tr>
<% end %>
</table>

Y el resultado final:

Por cierto, las columnas updated_at y created_at las gestiona automáticamente Rails. Este es un ejemplo de como gracias a su especialización para crear aplicaciones web te ofrece ayudas muy concretas que ahorran bastante tiempo.

Conclusión

Con Rails voy más rápido de lo que puedo contarlo y ya tengo casi lista la versión 0.1. Mañana escribiré sobre como he implementado la gestión de proyectos, sprints, tareas y estimaciones y cómo establecer las relaciones entre ellas (con validación incluida).

Nota: Si vas a hacerte tu propia aplicación en Rails con gestión de usuarios hay extensiones al generador de código que generan una herramienta de gestión de usuarios completa con más funcionalidades que esta. Si por el contrario quieres aprender si me parece un buen ejercicio didáctico.

Migraciones

Si tu aplicación va a tener un esquema no trivial (como así parece que será), es interesante que te mires una de las que es -en mi opinión- una de las prestaciones más brillantes de Rails: poder definir el esquema de la base de datos usando el propio lenguaje Ruby en lugar de SQL, y permitiendo "migrar" el esqema de una base de datos de una versión antigua de tu aplicación de manera rápida e indolora a cualquier versión no sólo futura sino también anterior...

En mi blog he publicado algo al respecto (perdón por la autopromoción :-)

Estoy de acuerdo

Estoy totalmente de acuerdo contigo. En estas pruebas he estado usando el sistema de migración de Rails que permite hacer operaciones con el modelo de datos en Ruby y sin preocuparse con las peculiaridades de la base de datos que finalmente se use (MySQL, PostgreSQL, SQLite, SQL Server, y Oracle están soportadas por este sistema).

Por algún motivo en casi todos los tutoriales que he leído explican como hacer el proceso con SQL pero el sistema de migraciones me resulta mucho más cómodo para trabajar y además así la aplicación crea el modelo de datos la primera vez que se arranca si no existe ya por lo que se simplifica mucho la instalación.

Por cierto, tu blog es muy interesante. Ya lo conocía y estoy suscrito a él :)