Refactoring de “Fat Methods” – Episodio 5

Pasar programadores PHP a Ruby no siempre es una tarea trivial, mayormente porque lo primero que tratan de hacer es escribir código en lugar de pensar que es lo que quieren hacer :). No es que me queje, a todos nos pasa que resolvemos el problema como primero sale (más aún cuando las fechas de entrega son apretadas :).

El otro día queriendo modificar un código me encuentro con este before_filter en un controller :

def setprofile
  @viewer = false
  if (!params['id'] && !params['profile_id'])
    @profile = @passport
  else
    if params['profile_id']
      pid = params['profile_id']
    else
      pid = params['id']
    end
    @profile = Passport.find_by_id(pid)			
    if (!@profile) 
      redirect_to(:controller => 'my3dg', :action => 'show') and return false
    end
    if (@profile.id != @passport.id)
      @viewer = true
    end			
  end
end

Lo primero que pense “uff, que paja, que complicado” hasta que miré mejor la estructura y ahí comprendí que en realidad era muy simple, solo que estaba escrito de forma complicada. Lo primero que me gustó de Ruby cuando empecé a entenderlo fue el uso de “||” para reemplazar esos molestos if anidados cuando tengo más de una opción. “Si no está a, poner a en el valor default”. En ruby es un simple “a || valor_default” (ojo con los valores de verdad en ruby).

Lo otro que veo mucho (incluso en los trabajos que corrijo en la facultad) es utilizar un if para después setear una variable en true o false, cuando casi todos los lenguajes aceptan expresiones booleanas del lado derecho de la igualdad.

Refactorizando el método anterior obtenemos el siguiente código :

def setprofile
  @profile = Passport.find_by_id(params['profile_id'] || params['id']) || @passport
  @viewer = @profile.id != @passport.id
  redirect_to(:controller => 'my3dg', :action => 'show') unless @profile
  return false unless @profile
end

Como nota, uso find_by_id porque retorna nil en caso de no encontrar el registro, en cambio find tira una excepción RecordNotFound, de esa forma puedo acoplar otro “||” para setear el valor default (@passport es la credencial del usuario loggeado en este caso).

Anuncios

Do it right! – Manejo de dependencias

En estos días he tenido varias discusiones sobre algunas prácticas muy acostumbradas en varios proyectos, que a mi parecer están muy lejos de lo útil. Es por eso que decidí escribir un par de artículos denominados “Do it right!” (hazlo bien!, si mi inglés no es tan malo como pienso :D). En esta primer entrega voy a tratar el tema de manejo de dependencias.

Escenario

Muchas veces uno se encuentra con un software que necesita y le es útil. Vamos a suponer de entrada que no tiene paquete para su distribución y/o sistema operativo favorito. En al leer el README vemos que depende de muchas cosas : alguna biblioteca de procesamiento de imágenes, algún captcha system o lo que sea.

Mirando mejor nos encontramos con una realidad muy fea : todas las dependencias están incluidas en el archivo que nos bajamos originalmente, y alguna veces se cargan por métodos poco ortodoxos.

Cualquiera que haya bajado cosas en PHP o Java seguramente se ha encontrado con esto : Clases bajadas de phpclasses.org o miles de archivos JAR en un directorio lib de la aplicación Java que se cargan en el classpath. También lo he visto en varios proyectos Rails últimamente.

La excusa que siempre escucho es “Because it makes the app more self contained“, si, si, BWTF. Pero la pregunta es : ¿es la forma correcta?. Si respondes que si, estás muy lejos de la realidad y seguramente tu sysadmin te odia mucho, pero mucho :).

El Problema

Entonces, ¿cuál es el problema con esto?. En principio parece una maravilla, nos facilita todo, descomprimimos y ya está!, el sysadmin (nosotros) agradecido, pero si el sysdamin es otra persona y se respeta los va a putear mucho, pero muuuuuucho.

Uno de los problemas con empaquetar todas las dependencias es la facilidad de solucionar problemas a futuro. El mejor ejemplo que he vivido es el típico cambio de time zone en nuestro país. PHP, por ejemplo, tiene su propia DB de timezones, y si actualizamos el tzdata de nuestro servidor, maravillosamente PHP ni se entera. Está bien que en este caso como programadores no podemos hacer mucho, es el lenguaje el que está mal, pero el ejemplo ilustra mi punto.

Un software con el que tuve que trabajar, vBulletin, va aún más allá. Tiene su propia DB de timezones por arriba de la que tiene PHP!, hardcodeada en un array!. ¿La excusa? Que la de PHP no anda bien en Windows y es mejor de esta manera.

Otro ejemplo que puedo dar es el otro día cuando migré unos sitios en el trabajo. Uno de los admines se quejó que dejó de andar porque usaba la función “dl” de PHP para cargar extensiones en tiempo de ejecución! (es decir, cualquier script PHP puede cargar un .so que extiende PHP de cualquier manera, incluso para saltearse otras medidas de seguridad), lo que no solo es una chanchada, sino que es un potencial problema de seguridad.

Tener el manejo de dependencias con la aplicación además nos prohíbe actualizar fácilmente un bug de seguridad, usabilidad o lo que sea de esa dependencia. No podemos en muchos casos hacer un upgrade de ese paquete que está embebido dentro de un todo llamado “aplicación”.

En rails he visto ya muchos que acostumbran a usar “rake gems:unpack” para meter las gemas de las que dependen en el directorio vendors/gems, así no las tienen que instalar aparte. Rubygems disgusta en muchos ambientes, no voy a entrar en eso hoy :). Si uno depende  de una versión específica la forma correcta sería especificar en el environment.rb la versión, y luego que el que hace el deploy haga un “rake gems:install” para asegurar que se cumplan las dependencias. Con Capistrano es una papa hacer esta tarea automáticamente ;).

Entonces, ¿nunca más lo hago?

No me gustan los extremos y creo que estas malas prácticas que se ven hoy tiene usos muy específicos.

Supongamos que el proyecto X usa la lib Y, pero tal cual la provee upstreem no me sirve. Lo lógico es hacer un patch para upstream solucionando el problema o agregando la característica.

Mientras upstream acepta el patch y realiza un nuevo release, ahí si tiene sentido tal vez, y de forma temporal tener esa dependencia embebida.

Un ejemplo donde yo lo estoy haciendo es con el uso de la gema contacts, que depende de la gema json. En una aplicación con Rails 1.2.6 no hay ningún problema, pero en otra que usa Rails 2.1 si, porque este último integra un parser de JSON y conflictua con la gema del mismo nombre. La opción lógica sería migrar la aplicación 1.2.6 a una nueva versión de Rails, cosa que no siempre es fácil y se necesita el tiempo. Como no se pudo, es más fácil meter en uno de los proyectos la gema de manera que no moleste al resto de los proyectos en el transcurso de la actualzación.

Seguro hay algún otro caso, ahora no se me ocurre alguno que realmente lo justifique.

Instalá tus Gemas con apt-get

Finalmente y pese a todas la quejas de los debianista de que era “dificil” o “imposible” ya hay una solución (o al menos eso parece, todavía no la probé :D).

La gente que hizo Passenger (a.k.a mod_rails) creó DebGem, que permite instalar prácticamente cualquier gema usando apt-get. El servicio no va a ser gratuito según parece una vez que pase la fase Beta, pero de todos modos demuestra que las gemas son empaquetables ;).

¿El precio? No lo dicen, pero aclaran que “subscription probably only costs about 1-2 hours of your time per month”. Veremos si tiene éxito y si motiva a terceros a crear algo similar :D.

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}"