State Machine

Varios días atrás tuve que hacer un juego simple, un memotest para ser exacto, para correr en unos «kioskos» para un cliente. Ya que tenía pendiente aprender a usar RubyGame, lo hicimos con este framework para ver que onda, ya que hasta ahora veníamos usando pyGame.

El juego salió super rápido, sin mayores problemas, pero la lógica de juego no me gustaba porque teníamos que andar trackeando el estado actual a mano, muchos ifs y comprobaciones que hacían del loop de juego un choclo de código.

Es por eso que me puse a ver un poco como aprovechar el tener bloques de código para encapsular la lógica del juego un poco más prolijo. Antes de comenzar encontré la gema Statemachine pero a primera vista no la entendí mirando los ejemplos 🙂 y luego de jugar un rato no me terminó de convencer ya que parece mucho más de lo que yo necesitaba.

El resultado de un par de horas de tirar «magia» fue poder definir la lógica de la siguiente manera (el ejemplo está simplificado, omitiendo los efectos y parte de la lógica) :

class Logic
  include StateMachine
  # Esperando interacción del usuario
  state :user_input do
    @events.each { |event|
      case event
      when MouseDownEvent
        selected event.pos
      when QuitEvent
        end_game
      end
    }
  end

  # Oculta las piezas seleccionadas cuando no hubo match
  state :clear do
    @selected.each {|f| f.hide }
    @selected = []
  end

  # Cambio de estado
  transition :user_input, :clear do
    @selected.size == 2
  end

  # Cambio de estado
  transition :clear, :user_input do
    true
  end
end

Cada declaración de state tiene el código que se debe ejecutar cuando estamos en dicho estado, mientras que las transition son usadas automáticamente para saber a qué estado nos debemos mover. La primer transition que retorne true, se toma el estado destino y se asigna como el actual.

Por el lado del game loop, lo único que se debe hacer es llamar a un método que se encarga de ejecutar el estado actual y luego verificar si alguna transición retorna «true» y se cambia al nuevo estado.

class Game
  include Rubygame
  include Logic

  def event_loop
    loop do
      current_state

      return if game_ended?

      draw
      @clock.tick
      @screen.update
    end
  end
end

En Game hay otros métodos auxiliares como game_ended, draw y selected, que no vienen mucho al caso en este momento.

El próximo paso ahora es limpiar un poco esto, ver si no hay una forma mejor de hacerla y publicar el esqueleto completo (la idea a futuro es tener un generator) para poder tener un mini framework para hacer juegos simples.

Si buscan un framework interesante les recomiendo Shattered Ruby (git repo), aunque al momento de escribir este post el sitio principal no responde.

state_machine.rb

Ruby Argentina – Resumen

Ya pasó la reunión planeada para hoy, y la verdad que fue todo un éxito :). A pesar del calor y de ser un sábado tuvimos unas 25 personas que vinieron, un gran avance contra las 4 de la primera reunión.

Arrancamos a las 11hs donde íbamos a tener una charla de introducción a ruby. Lamentablemente por un caso de fuerza mayor se suspendió pero improvisamos Lightning Talk y para que la gente que venía por primera vez se diera a conocer.

Luego Luis nos presentó Rake Compiler, que tiene como objetivo simplificar la construcción de gemas que tienen extensiones nativas y permite además a los programadores hacer cross-compiling.

Siguiendo Pelle Braendgaard dio una charla de OAuth muy interesante. La idea básica es poder proveer identificación entre sitios sin necesidad de introducir passwords (al estilo de OpenID) pero teniendo mayor flexibilidad, pudiendo revocar esos permisos o que expiren por período de tiempo.

Para terminar Pedro dio una charla de cómo ser freelance, los miedos y desafíos, que fue muy interactiva con el público. La dio como en la vieja escuela con un proyectos de diapositivas :).

Para el cierre hicimos el sorteo de los libros que aportó Cafelug y una remera de aportada por Pedro.

http://picasaweb.google.com/s/c/bin/slideshow.swf

Reunión Mensual de Ruby Argentina

Hoy anunciamos la próxima reunión mensual que se va a desarrollar la semana que viene. Cualquier interesado en dar alguna charla puede dejarme un comentario.

El día 29/11 se presentará un conjunto de charlas y se lanzará el ciclo mensual de conferencias sobre Ruby y Ruby on Rails y organizado por Argentina on Rails y Ruby Argentina.

El evento se llevará a cabo entre las 11 y las 16hs, con un break para almorzar sobre las 13hs. Al momento contamos con la presencia de Luis Lavena, actual mainteiner del one-click installer de Ruby para Windows, Pedro Vistinin de Argentina on Rails. A medida que tengamos confirmación de los demás oradores la iremos comunicando.

Luis hablará del rake-compiler y draco
rake-compiler es una serie de tareas de rake que simplifican la compilacion de tanto extensiones como gemas en Ruby, cosa que hasta ahora viene bastante descuidada y caoticamente un desastre entre developers. Cositas lindas para que no tengan mas excusas de no hacer cosas para Windows…

Pedro Visintin hablará de como hacerse freelance (trabajando con rails
🙂 ) y no morir en el intento.

Si estás interesado en dar alguna charla referida a Ruby o Rails, mandá un mail  que veremos como proveerte un espacio en ésta o próximas ediciones del evento.

Lugar
La cita es en la Facultad de Ingeniería de la UBA, Paseo Colón 850 en el Aula 402, 4to Piso. Capital Federal

La entrada es libre y gratuita y no requiere registración previa.

Además, CaFeLug sorteará 2 libros de Ruby/Rails de la editorial O’Reilly entre los asistentes.

Parseando HTML desde Ruby

Con todo este lío de la crisis mundial, corridas bancarias y demás en la oficina los días tranquilos jugamos a «Adiviná cuánto va a salir el dolay hoy». Como es muy molesto entrar a «Dolar hoy dot com» donde miramos el valor oficial del juego, me puse a armar un script para robarme el valor actual y así ir tirando el dato minuto a minuto.

Siempre es una molestia tener que parsear HTML, sobre todo cuando es tan feo como el del sitio en cuestión, que no solo no tiene un solo class de CSS ni id, sino que usa el tag FONT, dios. Por suerte Hpricot está para ayudarnos :).

Hpricot es «a fast, flexible HTML parser written in C» con su interfaz a Ruby, obvio :). Hace cosas muy copadas, como por ejemplo agarrar un HTML, buscar todos los <a> que tengan el class custom, o todos los P seguidos de un DIV seguidos de un IMG, etc. Podemos consultar por ID, borrar, agregarle class a las cosas que encontramos y muchas cosas más, útiles para manipular HTML.

Lo primero que tuve que hacer fue saltear la «protección» del sitio, ya que para acceder a la página con las cotizaciones verifican que vengas del dominio principal. Un básico chequeo contra el HTTP_REFERER. Como suelo usar open-uri, a ésta le puedo pasar el referer que yo quiero enviar en el header, como cualquier biblioteca para manejar URIs que tenga auto-respeto :).

Para encontrar los valores fue fácil. Mirando el HTML de la página se ve que el valor del dolar está dentro de un «div/font/b/font», por lo que solo tuve que buscarlo. Después hago un cleanup para sacarle un non-breaking space que me molestaba y los espacios que también quedan feos.

Y eso es todo. Ahora puedo consultar el valor del dolar desde mi consola :). Seguramente hay miles de formas más cómodas de hacerlo, algún widget para Gnome, página que te de un RSS, etc, pero fue un lindo ejercicio.

Dejo acá el script para el que quiera jugar un rato.

require 'rubygems'
# gem install hpricot si no lo tienen instalado
require 'hpricot'
require 'open-uri'


doc = Hpricot(open("http://dolarhoy.com/indexx.php", "Referer" => "http://dolarhoy.com/"))

div = (doc/"div/font/b/font")[1]

dolar = div.inner_html

dolar = dolar.gsub(/[ |$]/, "")
dolar.strip!

puts "Dolar Hoy : $#{dolar}"

Lo que viene en Rails 2.2

Hace poco se anunció el RC1 de lo que será la versión 2.2 de Ruby on Rails. Si bien la fecha de salida es «cuando esté lista», ya se pueden utilizar para aquellos que gusten vivir «on the edge».

Para aquellos que quieran un rejunte completo de todo lo que se viene pueden ver el post : What’s New in Edge Rails: Rails 2.2 Released – Summary of Feature.

Yo acá voy a comentar solo lo que tuve la oportunidad de probar y que me resultó útil.

I18n

La verdad es que es una buena noticia, no para mi porque hago todo para un solo idioma :P, pero algún día puede resultar útil. Lo único que a mi me deja con sabor a poco es la forma en que se hace que no me termina de gustar. Antes que nada quiero aclarar, porque ya vi la pregunta en un par de listas de correo, que esto localiza textos, no el contenido de una instancia de ActiveRecord. Si uno quiere que el contenido que cargan los usuarios sea traducido, es otra historia :).

Las traducciones se escriben en ruby o en archivos YAML, con «hashes» que pueden estar anidados (como si fueran «namespaces«) :

# lib/locale/en-US.rb
{ 'en-US' => {
  :hello_world => "Hello World",
  :hello_flash => "Hello Flash"
}}
 
# lib/locale/pirate.rb
{ 'pirate' => {
  :hello_world => "Ahoy World",
  :hello_flash => "Ahoy Flash"
}}
 

Y luego en las vistas (o mailers o donde sea) en lugar de poner el texto se ingresa el symbol asociado al texto que cargamos en las traducciones, por ejemplo :

Mi problema particular con este método es que estoy mucho más acostumbrado a cómo se hace con gettext (que no necesariamente es la mejor forma) donde se ponen todos los textos en inglés en la aplicación y después se escriben las traducciones.

Hay un demo online acá, que en estos momentos está caído, espero que para cuando lean esto ya este funcionando de nuevo.

Join Tables Conditions

Esto es algo que realmente hacía falta. Vamos a ver la mejora con un ejemplo. Supongamos que tenemos los siguientes modelos :

class Article < ActiveRecord::Base
  belongs_to :user
end

class User < ActiveRecord::Base
  has_many :articles
end

y queremos obtener todos los usuarios que tengan al menos un artículo publicado. Para eso deberíamos hacer :

User.find(:all, :joins => :article,  :conditions => ["articles.published = ?", true])

Con la nueva sintaxis es posible especificar este tipo de queries de una manera más amena como sigue :

User.find(:all, :joins => :article, :conditions => { :articles => { :published => true } })

ActionMailer Layouts

Algo que si me tocó vivir es tener varios emails HTML con un mismo formato y que cambiaba el contenido (Tu amigo te invitó, Tu nueva clave es, Ganaste un premio, etc). Con el mailer actual casi que hay que hacer un copy & paste del marco y despuer cambiar para cada tipo de email la lógica de qué se muestra.

En rails 2.2 ahora vamos a tener layouts como los tenemos en ActionView, de manera de tenerlo una vez y si arreglamos o cambiamos algo cambia para todos.

Memoization

Es muy frecuente que uno agregue lógica simple de caching en los modelos para las variables de instancia como se ve en el siguiente ejemplo :

  class User < ActiveRecord::Base
    def full_name
      @full_name ||= "#{first_name} #{last_name}"
    end
  end

Esto lo hacemos para evitar el overhead de crear el full_name si lo llegamos a usar varias veces en una misma vista, de manera que el string se crea en la primer llamada y en las siguientes solamente lo retorna.

Los puristas dicen que en realidad está mal, porque se está responsabilizando al método de algo que en realidad no debería importarle : la política de caching. ¿Suena exagerado? Seguramente :).

Para solucionar esto se agregó el método memoize que nos permite a agregar a un método común este tipo de lógica de manera separada :

  class User < ActiveRecord::Base
    def full_name
      "#{first_name} #{last_name}"
    end

    memoize :full_name
  end

Esto se encarga de que cuando llamemos a @user.full_name se comporte de la misma manera que el primer ejemplo, sin tener que modificar el método. También nos permite saltarnos el «cache», por ejemplo si en la lógica acabamos de cambiar el nombre de pila, debemos forzar para que el nombre completo cambie :

   @user = User.new :first_name => 'Test', :last_name => 'Test'
   @user.full_name # => Test Test
   @user.first_name = 'Do'
   @user.full_name # => Test Test
   @user.full_name(true) # => Do Test

Y más

Hay varios cambios más, como Thread Safety, ETag, partial GETs y demás que a mi por ahora no me interesan y por eso no me entiendo mucho más. Pueden consultar el anuncio oficial para ver de que se tratan o cómo pueden hacerle la vida más fácil :).

Terminó el Rails Summit Brasil

Ya de vuelta en Argentina y descansando, puedo decir que el viaje a Sao Paulo fue muy productivo y entretenído. Del evento realmente no vale la pena que me extienda mucho más de lo que ya dijo Luis Lavena en su blog, por lo que les recomiendo leer lo que él dice :).

Durante el evento aproveché para conocer en persona algunos miembros de Ruby Argentina, como Luis y Pedro, con los que pasamos un momento realmente agradable durante el evento. También tuve el agrado de conversar con David Chelimsky (maintainer de rspec) y aprender más sobre BDD y cómo comenzar a usarlo.

Por el lado de la ciduad, Sao Paulo es realmente enorme y no tuve más que dos tardes para tratar de conocer algo, que aproveché en visitar algunas expos y museos que había cerca del hostel. Si van les recomiendo visitar el Museo de la lengua Portuguesa, realmente interesante las diferentes influencias que tiene su idioma y su cultura.

Hubo varias cosas que me sorprendieron, ¿será que estoy muy acostumbrado a vivir en argentina? Eso denlo por hecho :).

Lo primero fue que desde que llegué no pararon de darme monedas en todos lados :). Como segundo punto el que existan carriles exclusivos para buses y más sorprendente que los 5 taxistas con los que hablé están de acuerdo!. Suena irreal que pase acá, ¿no?.

El punto sin duda que más me gustó fue el metro. Una red realmente bien pensada (o eso pienso al menos) donde las combinaciones se hacen con solo unos pasos, y con frecuencias de un minuto o por ahí en hora pico. Un gusto andar en metro en Sao Pablo.

Esperemos que el año que viene vuelva este evento, aunque sería genial que sea en alguna ciudad con playa :).

Update: Algunas de las fotos.

http://picasaweb.google.com/s/c/bin/slideshow.swf