format internet:

…please wait (48% completed)…

Archive for September, 2007

destructores en ruby

Posted by javier ramirez on September 30, 2007

Viniendo de otros lenguajes de programación, cuando empecé con Ruby echaba de menos los métodos destructores de objetos. Igual que existe un método initialize, esperaba que hubiera un método finalize al que se invocase justo antes de que el garbage collector pasase a limpiar los objetos no usados.

Buscando un poco, di con la clase ObjectSpace, con la que se pueden hacer algunas cosas interesantes, como por ejemplo recorrer todos los objetos que existen en memoria en un momento determinado. Esta clase tiene un método, define_finalizer, que permite definir un hook que se llamará cuando el objeto se libere.

El problema es que, por como está implementado, cuando se invoca a este hook el objeto ya no puede existir en memoria, porque si existiese habría una referencia a ese objeto y entonces el garbage collector no lo limpiaría nunca. La verdad es que este comportamiento es bastante mejorable y, como bien dice aquí Charles Nutter, no es lo único mejorable de la clase ObjectSpace.

El caso es que ese comportamiento es el que hay, y con eso tenemos que jugar. Para el caso más sencillo, es tan simple como lo siguiente.

  a='cadena de prueba'
  ObjectSpace.define_finalizer(a,proc{|id| puts "finalizando el objeto #{id}"})
  a=nil
  GC.start

Si ejecutamos eso en la consola, podremos ver cómo, cuando pasamos el collector, se muestra la cadena indicando que se ha destruído.

Si lo que queremos es llamar a un método concreto, hay que tener en cuenta que el objeto ya se habrá destruído cuando se invoque su finalizador, así que podremos llamar solamente a métodos no asociados a la instancia. Lo normal en ese caso es llamar a métodos de la clase de este objeto. Por ejemplo

class TestClass
  def initialize 
    ObjectSpace.define_finalizer(self,self.class.method(:terminate).to_proc)
  end

  def TestClass.terminate(object_id)
    puts "se ha destruÃ�­do el objeto {#object_id}"
  end
end

En el método terminate podríamos hacer lo que necesitásemos, siempre teniendo en cuenta que el objeto ya no existe. Si se tratase de liberar recursos abiertos a la hora de inicializarlo, que es el caso más común, se podría usar una Hash definida como variable de clase y guardar la referencia a los recursos que se abren usando el object_id como clave. De este modo, al llamar al método terminate, podríamos acceder con el id a esa Hash para poder liberar el recurso que nos interese.

En cualquier caso, hay que decir en defensa de Ruby que para muchos de los casos donde en otros lenguajes de programación usaríamos destructores de objetos, en Ruby usaremos bloques, quedando un código mucho más legible.

Por ejemplo

FantasticConnection.open(end_point) do |conn|
  conn.send(my_data)
end

searchwords: finalizer, finalize, destructor, constructor, garbage collector, colector de basura, ObjectSpace

Advertisement

Posted in development, javier ramirez, ruby | Tagged: , , | Leave a Comment »

conferencia rails en versión original (castellano sin subtítulos)

Posted by javier ramirez on September 29, 2007

La conferencia rails europea ya ha terminado… los videos están en internet… los contenidos son muy interesantes, pero… tienen chistes de chiquito? NOOOORL!!!

Tras el éxito de la convocatoria del año pasado y animados por la acogida de la comunidad, la conferencia rails de habla hispana arranca de nuevo. Las fechas para esta edición son el 22 y 23 de noviembre.

Dos días de comunidad, conferencias, libros a buen precio y networking. Además, es el único día del año en que puedes llevarte tu portátil a una cena y no ser “#{you.is_female? ? ‘La rarita’ : ‘ El rarito’} del grupo!”.

El año pasado hubo lleno, así que no esperes mucho para registrarte en el sitio de la conferencia.

Y si te gusta el diseño, me ha contado un pajarito que se va a montar un concurso para el diseño de las camisetas, así que no pierdas de vista el feed del blog, donde se irá dando toda la información relevante.

Por cierto, si quieres dar una charla, todavía estás a tiempo. Puedes ver las propuestas que ya hemos recibido para no repetirte, y algunos temas propuestos de los que desde la organización creemos que estaría bien tener alguna conferencia.

searchwords: conferencia rails 2007

Posted in conferenciarails, conferenciarails2007, javier ramirez, ruby on rails | Leave a Comment »

usando method_missing y alias_method_chain

Posted by javier ramirez on September 16, 2007

En este post voy a comentar cómo usar algunas de las características que más me gustan de Ruby y Rails: la posibilidad de reabrir una clase en cualquier momento, poder capturar las llamadas a métodos que no existen en un objeto, y la posibilidad de hacer alias de métodos (y encadenarlos)

Para ilustrar todo esto y ver qué podemos hacer, vamos a modificar el comportamiento de la clase Hash de forma que pueda hacer lo siguiente

>> me={:name=>'javier'}
=> {:name=>"javier"}
>> me.name
=> "javier"
>> me.last_name
NoMethodError: undefined method `last_name' for {:name=>"javier"}:Hash
from (irb):18:in `method_missing'
from (irb):33
from :0

Si intento hacer eso sobre cualquier Hash, me devolvería un error directamente en la línea donde hago “me.name”, ya que la forma correcta de acceder a los elementos de una hash es mediante el operador []. La forma de acceder al campo “name” sobre una Hash cualquiera sería “me[:name]”. Con las modificaciones que vamos a realizar, conseguiremos un uso un poquito más conciso (a la manera que tiene ActiveRecord con los atributos de nuestro modelo).

Añadir el método ‘name’ a mi Hash sería muy simple. Aprovechándonos de que Ruby me permite reabrir una clase en cualquier momento, podría hacer simplemente esto

class Hash
  def name
    self[:name]
  end
end

Esto sería perfecto, pero implica que conocemos previamente el nombre de los campos a los que queremos acceder y además necesitamos escribir un método por campo. No parece una buena opción. Si conocemos previamente el nombre de los campos podríamos usar Struct y olvidarnos.

Entonces, si no sabemos previamente el nombre de los campos, ¿cómo sabemos qué métodos implementar? La respuesta es simple: no lo sabemos y no lo podemos saber. Pero… y ¿qué hacemos entonces?

Aquí es donde entra en juego una de las características que más me gustan de Ruby. Cuando un objeto recibe un mensaje que no entiende, Ruby automáticamente invoca a un método especial del Kernel. Este método es method_missing. Nota: ‘mensaje’ es el nombre que se usa en teoría de objetos cuando hablamos de un método. Realmente lo que tenemos son objetos que se envían mensajes entre ellos.

El método method_missing es un método del Kernel, pero si yo reabro una clase y defino el método, cuando se invoque a method_missing para esa clase conseguiré interceptar todas las llamadas a métodos no existentes. El intérprete de ruby nos va a pasar dos parámetros, el primero es el nombre del método, y el segundo son los parámetros que se usaban en la invocación, si los había.

Ahora ya podríamos hacer lo siguiente

class Hash
  def method_missing(method_name,*args)
    self[method_name]  if self.member?(method_name) 
  end
end

Y esto ya casi hace lo que queremos, pero hay todavía un pequeño detalle. Si pedimos un campo que no existe, vamos a obtener un nil en lugar de una excepción. Esto no tiene porqué ser malo, y de hecho podría ser el comportamiento deseado. Pero no es lo que planteábamos originalmente. Queremos que si el campo existe nos lo devuelva, y si no existe se comporte como antes de sobreescribir el método.

Para poder hacer este tipo de cosas en Ruby, usamos la posibilidad de hacer alias de los métodos. La forma habitual es hacer un alias del método original con un nombre tal que loquesea_old y después desde el método nuevo que escribimos podemos invocar al antiguo mediante el alias definido. Esto tiene un problema inherente a la propia arquitectura de una aplicación en Ruby. Como las clases por definición se pueden reabrir en cualquier momento, cualquiera puede redefinir un método en cualquier momento, con lo que corremos el riesgo de estar ocultando un método sobreescrito previamente al hacer un alias.

Para evitar este tipo de problemas, en el código interno de rails se usa un patrón que funciona bastante bien. Cuando se define un nuevo método que va a ocultar a uno viejo al que se hará alias, se usa un sufijo para indicar qué tipo de funcionalidad implementa. Después se definen los alias de esta forma

alias :method_missing_without_access_by_key, :method_missing
alias :method_missing, :method_missing_with_access_by_key

A la hora de definir nuestro método nuevo, lo definiremos con el patrón nombre_antiguo_with_sufijo_indicando_funcionalidad. En nuestro caso se definiría como “method_missing_with_access_by_key”. De esta forma, aunque varios plug-ins diferentes intenten hacer alias de un mismo método, mientras no colisione el sufijo con uno que ya existe no tendremos problemas, con lo que se reduce el riesgo de tener conflictos de nombres.

Como este patrón es muy interesante, desde la versión 1.2 está implementado como parte del core de rails, bajo el nombre “alias_method_chain”. Podemos hacer simplemente

alias_method_chain :method_missing, :access_by_key

Esto busca un método que se llame method_missing_with_access_by_key y aplica los dos alias que hemos visto antes.

Así que la versión final de nuestro pequeño hack quedaría del siguiente modo

class Hash
  def method_missing_with_access_by_key(method_name,*args)
  self.member?(method_name) ? self[method_name] : method_missing_without_access_by_key(method_name,*args)
end

alias_method_chain :method_missing, :access_by_key
end

Todavía hay espacio para hacer alguna pequeña mejora, que probablemente explicaré en un post más adelante.

searchwords: method_missing, alias_method_chain, ruby alias method, Struct

Posted in development, javier ramirez, ruby, ruby on rails | Leave a Comment »

nuevo proyecto en rails: www.mirubi.com

Posted by javier ramirez on September 7, 2007

Durante los últimos dos/tres meses he dedicado gran parte de mi tiempo (junto a otras personas del equipo) a desarrollar la web mirubi.com. A pesar de estar en fase Beta está teniendo una acogida bastante interesante y nuestro cliente ya está lanzando versiones en Reino Unido, Alemania, Francia y USA.

Como todos los desarrollos que hacemos en ASPgems es una solución 100% Rails. La aplicación es multi-país, multi-idioma y multi-marca, permitiendo a los partners una personalización de la apariencia.

Para gestionar la complejidad del deployment de los diferentes países a sus entornos de pruebas y producción hemos usado Capistrano 2.0 con tareas personalizadas.

La escalabilidad ha sido una de nuestras preocupaciones en este proyecto, y hemos hecho un uso intensivo de memcached tanto para almacenar consultas como fragmentos de html donde ha sido posible. El resultado está siendo muy satisfactorio, y hasta ahora los picos que ha habido ni se han hecho notar en el rendimiento.

La verdad es que es uno de esos proyectos donde hay mucho más trabajo en todas las tareas que corren por detrás que en la interfaz del usuario en sí misma y nos ha valido para poner a prueba varias cosas. Con cada desarrollo que entregamos me voy afianzando más en la idea de que hoy por hoy para el desarrollo de una aplicación web dinámica (independientemente del tamaño), la mejor opción es Ruby on Rails.

searchwords: rails, mirubi, capistrano, memcached, aspgems, sites

Posted in javier ramirez, ruby on rails, sites | Leave a Comment »

Seleccionar elementos de un Array de forma aleatoria

Posted by javier ramirez on September 4, 2007

A veces viene bien poder mostrar el contenido de un Array de forma aleatoria en Ruby. Por ejemplo, puedes querer mostrar en una tagcloud los tags más populares pero no quieres mostrarlos en orden para que quede más estético.

Normalmente usamos el método sort_by en arrays compuestos de objetos complejos para compararlos por algún campo, por ejemplo para un resultado de una consulta de AR podríamos hacer:

my_array.sort_by{|x| x.amount}

Podemos utilizar el mismo método de una forma simple para “desordenar” cualquier Array:

my_array.sort_by{rand}

Y con esto tendremos un array desordenado “al azar”. Podríamos incluso abrir la clase Array y definir un método shuffle que hiciera justamente esto, tal que así

  class Array
    def shuffle
      self.sort_by{rand}
    end
  end

Y ahora ya podemos hacer:

my_array.shuffle

searchwords: ruby, array, sort_by, shuffle

Posted in development, javier ramirez, ruby, ruby on rails | 1 Comment »