sábado, 20 de mayo de 2006

UNO: aplicación web

Si, soy un adicto*. ¿Adicto a qué? Las opciones son muy variadas: TV, Internet, código limpio y elegante, maní japonés, cine, Joaquín Sabina, trotar… la lista sigue, dependiendo de las circunstancias y el momento. Ese no es el problema. La cuestión es que cuando algo me gusta en muy poco tiempo, incluso en cuestión de días, ya me ha llegado a la médula, y se convierte en un "imperativo categórico kantiano"... Si, también soy un poco adicto a la exageración ;-)

Uno de mis días normales incluye al menos 12 horas cosido al computador. A veces, no muy raras las veces, incluso algunas horas más. Siempre hay algo que hacer, que leer, que revisar, que aprender, etc. Así que cuando logro descoserme de la silla y salir al mundo real, suelo disfrutar al máximo cada una de esas otras actividades a las que dedico tiempo (si, normalmente alguna otra adicción).

Entre esas otras adicciones está la regular reunión para jugar UNO. Un lugar tranquilo, un grupo variopinto de amigos que, al igual que yo, deja cualquier cosa que les perturbe fuera de la mesa de juego, y se dedican a sólo pasarla bien, a dejar que un poco de estrategia, mucha de suerte, y el pacto de caballeros de siempre jugar limpio y sin trampas, nos haga la noche, nos haga reír, y a veces, nos haga ganar.

En algún momento de sus inicios, cuando la cosa se empezó a regularizar, alguien empezó a llevar en un cuaderno (y luego en Excel) el registro de los juegos, las estadísticas de la adicción. Hace poco alguien sugirió que sería bueno tener una web para esas estadísticas. A ver, ¿ya mencioné entre mis adicciones a Internet, el código limpio y elegante, y el UNO? ¿Acaso me faltó mencionar que entre las nuevas adquisiciones a mi lista de adicciones está Ruby on Rails? El resultado era inevitable.

UNO: la aplicación web

El diseño de la aplicación es bastante sencillo: tenemos temporadas, jugadores, juegos (cada noche de reunión es un "juego") y partidas (en cada juego hacemos cuatro partidas, que están conformadas por n manos, y que duran hasta que algún jugador alcanza los 500 puntos en la partida; las manos no se registran en la aplicación, sólo el orden final de los jugadores en la partida).

 Posición  Puntos 
110
26
34
43
52
61
 7 y posteriores 0

A los lugares resultantes de cada partida se les asigna puntos según la tabla de al lado, sistema de puntos basado en las carreras de Fórmula 1 (¿Quién de Uds. ya había adivinado esa adicción en mi lista, adicción también compartida por la mayoría de los recurrentes a esta mesa de UNO?). Esos puntos se suman para generar los resultados generales del juego. Y las posiciones resultantes de cada juego se pasan por esta misma tabla de puntos para generar los resultados generales de la temporada.

La página inicial muestra un resumen de cada temporada: posiciones generales de la temporada (lugares, puntos y promedios, en texto y gráficos) y posiciones resumidas de cada juego uno de los juegos.

Y la página de cada jugador incluye estadísticas del jugador, por temporada: frecuencia de posiciones finales (en texto y gráficos), total de puntos y promedios.

Cada uno de estos elementos es bastante flexible, desde la aplicación misma, que no necesariamente tiene que ser usada solo para registros de UNO, sino que pudieran registrarse juegos en general, o cualquier serie de eventos con registro de lugares o posiciones finales. Las temporadas pueden ser un período de tiempo, o pueden usarse como registro de equipos o ligas, o cualquier otra cosa en que se quieran separar un grupo de juegos. Cada juego puede tener desde una hasta el número de partidas que se quiera, y desde uno hasta el número de jugadores que se quiera. Lo único fijo, por ahora, es el sistema de puntuación para las posiciones resultantes, tanto por juego, como por temporada.

En este momento el acceso está restringido sólo a nosotros, los que jugamos en este grupo de UNO, pero si alguien está interesado en tener acceso a una web de este tipo para su propio grupo, podría escribirme un correo: si hay suficiente interés yo estaría dispuesto a publicar la web para crear grupos nuevos. Incluso, si me mandas una buena historia de obsesos de UNO, te podría mandar el código de la aplicación :-)

* Uso el término "adicción" como una manera de fácil de hacerme entender, sin explicar mucho mis razones detrás de cada una de estas actividades. Yo en realidad las considero "las cosas que me apasionan". En el blog Creating Passionate Users, Kathy Sierra tiene un excelente artículo sobre las cosas que nos apasionan, y las razones detrás de ellas

¿Te gustó este artículo? Digg it!

sábado, 13 de mayo de 2006

Clase para parámetros de una aplicación en Rails

Estaba buscando algún plugin para Rails para manejar los parámetros (preferencias, opciones, settings, etc.) de una web que estoy haciendo.

En el wiki de Rails encontré este ConfigurationGenerator que parecía muy atractivo, y que se adaptaba exactamente a lo que yo estaba necesitando, pero no pude encontrar el código de él por ningún lado, ni he obtenido respuesta a un post que coloqué en el foro de Rails sobre este generador.

Luego conseguí este otro Settings plugin, que me pareció fantástico, pero no era exactamente lo que yo quería.

Así que decidí emprender la tarea de hacer mi propio modelo para el manejo de los parámetros de mi aplicación web, tomando como base un poco de esos otros pulgins que ya había visto.

Lo que me interesaba era tener una tabla que siempre tendría un sólo registro, y con una columna para cada opción que hubiese en los parámetros. Eso me permitiría guardar cada opción en el tipo de dato y tamaño de columna adecuado. Además me interesaba poder acceder a cualquier parámetro, desde cualquier lugar de la aplicación, con algo simple como:

Parametros.opción

Los parámetros con que arranqué eran el nombre de la aplicación y un texto del tipo "about us", o "quienes somos", para la página principal de la web. La migración quedó asi:

class AgregarParametros < ActiveRecord::Migration

  def self.up

    # Archivo de parámetros de la aplicación
    create_table :parametros do |table|
      table.column :aplicacion, :string, :limit => 100
      table.column :quienes_somos, :text
      table.column :updated_at, :datetime
    end
    
    execute "Insert into parametros (aplicacion, quienes_somos) Values ('App. web', 'Somos...')"
    
  end

  def self.down
    drop_table :parametros
  end

end

Este archivo contendrá siempre un registro único, ni más, ni menos, así que se incluye automáticamente durante la migración misma, con valores iniciales cualesquiera. Los valores reales se asignaran en la aplicación web.

Y el modelo de Rails quedó de esta manera:

class Parametros < ActiveRecord::Base
  
  set_table_name "parametros"
  validates_presence_of :aplicacion, :quienes_somos
  private_class_method :new, :create, :destroy, :destroy_all, :delete, :delete_all
  
  @@p = find(:first)

  def self.method_missing(method, *args)
    opcion = method.to_s
    if opcion.include? '='
        # Asignar un valor a la opción
        nombre_var = opcion.gsub('=', '')
        valor = args.first
        @@p[nombre_var] = valor
      else
        # Retornar el valor de la opción
        @@p[opcion]
    end
  end
  
  def self.save
    @@p.save
  end

  def self.update_attributes(atributos)
    @@p.update_attributes(atributos)
  end

  def self.errors
    @@p.errors
  end

end

Me interesaba accesar los parámetros con la forma en plural Parametros (en contra de las convenciones de Rails), y la tabla que uso tiene el mismo nombre en plural (esta vez si como lo indican las convenciones de Rails), así que debí incluir set_table_name "parametros" para saltarme la convención del nombre del modelo.

Haría uso de la clase directamente, sin instanciarla, así que debía prevenir que se crearan objetos de la clase con la línea private_class_method :new, :create. A esa lista se agregan :destroy, :destroy_all, :delete, :delete_all porque ciertamente no me interesa que se borre ese registro único de la tabla de parámetros. La lista de métodos se puede alargar a cualquier otro del que se quiera bloquear el acceso.

No quería ir a la base de datos cada vez que necesitara un parámetro, así que usé una variable de clase para hacer un cache de los valores, que sería inicializada la primera vez que se llame a los parámetros, con el contenido de ese registro único de la tabla: @@p = find(:first)

Los métodos save, update_attributes y errors se definen como métodos de clase, con self, para poder llamarlos sin tener necesidad de instanciar la clase.

Se pueden asignar todos los valores a los parámetros, uno por uno, y al final llamar a save para grabarlos en una sola operación en la base de datos. O se puede llamar a update_attributes con un "hash" como parámetro, con todos los valores de los parámetros a grabar (al grabar desde una forma, en una página de parámetros, por ejemplo). El método errors se define para poder utilizar el "helper" error_messages_for 'parametros' en una forma.

Para actualizar los parámetros se puede hacer algo como lo que sigue. En el controlador adecuado:

def parametros
  @parametros = Parametros
end

def grabar_param
  @parametros = Parametros
  if @parametros.update_attributes(params[:parametros])
    flash[:notice] = 'Parámetros grabados'
    redirect_to :action => "index"
  else
    render :action => "parametros"
  end
end

Y en la vista hacer:

<h1>Parámetros de la web</h1>
<br>
<%= start_form_tag :action => 'grabar_param' %>
  <%= error_messages_for 'parametros' %>
 <p><label for="parametros_aplicacion">Nombre de la web:</label><br/>
 <%= text_field_tag 'parametros[aplicacion]', @parametros.aplicacion %></p>
  <p><label for="parametros_quienes_somos">Quienes somos:</label><br/>
  <%= text_area_tag "parametros[quienes_somos]", @parametros.quienes_somos %></p>
  <br>
  <%= submit_tag 'Aceptar' %>
<%= end_form_tag %>

Esta es la mejor manera que se me ha ocurrido para manejar los parámetros de la manera que yo quería. Si conocen de una manera mejor, estaré gustoso de escucharla.

¿Te gustó este artículo? Digg it!