Mostrando entradas con la etiqueta ruby. Mostrar todas las entradas
Mostrando entradas con la etiqueta ruby. Mostrar todas las entradas

sábado, 18 de enero de 2014

Vuelta el blog

Por diversas razones, talvez mas por dejadez que otra cosa he dejado gradualmente de bloggear hasta que en el 2012 para del todo, muchas cosas cambiaron desde la ultima vez que bloggie, cuando comente mis impresiones de la rubyconf 2012, y ya estamos en el 2014, y se hizo la rubyconf 2013 (a la que le dedicare un justo post)
Otra cosa que cambio, al menos en mi caso, la relevancia que le doy a la parte del desarrollo client-side, digamos que ya estamos bastante lejos de las epocas en que todo se hacia "en el server" y el browser actuaba como un simple renderizador de html. 
En la ultima decada, pero mas que nada en el ultimo par de años las cosas cambiaron mucho con respecto a lo que los browsers son capaces de hacer, de a poco se fueron agregando capacidades que antes solo se podian agregar a la aplicacion por medio de plugins, o directamente haciendo una app tradicional, por ej: WebRTC que permite desarrollar apps de comunicacion (como los softphones) 100% en Javascript, WebGL para aceleracion grafica y un largo etc...

Una muestra de lo que digo, es "Epic Citadel" para el caso de WebGL, y el hangout de google que utiliza lo de webrtc (creo)

Ruby sigue siendo un lenguaje potente y cada vez se extiende mas, pero el ecosistema y comunidad de ruby esta bastante orientado al desarrollo rapido de backend (casi ninguna tecnologia lo supera en eso), como excasas excepciones existen algunas gemas, algunas medio experimentales que ayudan para usar ruby client-side, solo enumero algunas:

  • Shoesrb: framework orientado a desarrollar interfaces de ventanas, para apps "tradicionales" (o sea, no web) con un azucar sintactico bastante sencillo, al estilo los frameworks webs mas conocidos como sinatra
  • Rubygame: Libreria multimedia que ayuda al desarrollo de juegos en ruby
  • Opalrb: Transpiler que convierte codigo Ruby a Javascript para que pueda ser ejecutado en el browser

Y todas son excelentes, pero sufren el problema de ser complejos y aparatosos de instalar, no para el caso del desarrollador que esta canchero con eso, sino mas que nada para el usuario final, recuerden que al ser ruby un lenguaje interpretado, como minimo se debe instalar el interprete del lenguaje, y en el caso de usar librerias como shoesrb y rubygame, se tienen que instalar las dependencias, creo que gtk en el caso de shoes y sdl en el caso de rubygame

Opalrb como concepto se ve muy interesante, pero lamentablemente los browsers no dan soporte realmente bueno para debuggear lenguajes que no sean javascript, se que existe lo de source maps, pero lei por ahi que desarrollar con algo que no sea javascript para el browser sigue siendo complicado igual

En fin, llegue a la conclusion que Javascript es un lenguaje que hay que conocer si o si, al menos si tu intencion es hacer aplicaciones que sean sencillas de instalar y usar para el usuario final, por eso empece a hacer algunas pruebas, y con un tema que me gusta, los graficos 3D:

Para empezar una demo basica con figuras geometricas, muy elemental, pero si miran en el codigo fuente se puede ver hypercomentado, muy util para ir aprendiendo Javascript (como yo en el momento de escribir esas demos)
http://tario.github.io/threejs_demos/

Despues, un proyecto apuntando a algo, no esta terminado, mas tarde le dedicare un merecido post
http://tario.github.io/quadnet




sábado, 20 de octubre de 2012

Rubyconf 2012

Update: ya estan disponibles todos los videos de la conferencia aca

Este fin de semana fui a la rubyconf que se organizo en la Argentina, pais donde vivo en el cual fue la segunda vez consecutiva que se organiza un evento de tal magnitud con Ruby como tema central.
En la foto principal de articulo pueden ver el pin rojo de la RubyConf 2011 y el nuevo pin amarillo de RubyConf 2012.

Como lo plantearon los organizadores del evento en un principio, esperaba la aparición de figuras relacionadas con el desarrollo del lenguaje provenientes de Japon al igual que el año pasado vinieron a presentar Yutaka Hara, Koichiro Eto y Narihiro Nakamura (pueden ver sus charlas y otras mas aca), por lo visto este año no fue posible pero mas alla de eso considero que el nivel de las charlas y la organiacion mejoraron en comparación al año pasado y no faltaron las grandes personalidades del mundo ruby local, paises vecinos y varios paises lejanos del "otro lado del charco", con visitar la pagina se puede saber quienes son

Todas las charlas estuvieron buenisimas, doy mis comentarios de las charlas que mas me gustaron
Update: tambien incluyo los videos de las mismas

Ship it: Staying Lean at LivingSocial


Andy Atkinson de livingsocial nos explica como aplican Lean a la hora de evaluar la incorporación de nuevas features. Una de las cosas mas interesantes que conto es que en lugar de desarrollar la feature completa y largarla, hacen "pedacitos de feature" o incluso solo un "preview" para que todos o un grupo seleccionado de usuarios envien feedback, si el feedback es bueno siguen adelante y van avanzando en el desarrollo de esa funcionalidad en caso contrario la dejan de lado.
Conto como ejemplo el caso de una funcionalidad hypercompleja de implementar (ahora no me acuerdo que era) que a los usuarios no les importaba y una cosa tan sencilla de implementar como un "boton de express checkout" tuvo mucho exito y les genero un monton de ganancia.

The web of tomorrow

Excelente charla de @cuerbot, con una buena presentación y graficas vintage acordes transmite un mensaje de Javascript, P2P y libertarismo digital. Basicamente explico con ejemplos puntuales como las aplicaciones en javascript usan WebRTC pueden comunicar directamente browser con browser sin servidor de por medio a excepcion de una intervencion minima al principio de la conexion similar a como lo hacen hoy VoIP y las redes P2P de intercambio de archivos. Las claras consecuencias de esto segun mi forma de ver:

  • Streaming de audio y/o video entre browsers sin servers como intermediarios
  • Drastica baja de requisitos de performance en el lado del server al trasladar casi la totalidad de la carga de la aplicacion a los browsers
  • Descentralizacion de las comunicaciones mitigando la censura en la web
  • Solo la imaginacion es el limite....
Otra cosa interesante de esta charla es que fue la única en mostrar puro codigo javascript, constituye un puesto de avanzada de invasion de javascript en el espacio de ruby, pero le doy la bienvenida

Aca los slides: https://speakerdeck.com/u/elcuervo/p/the-web-of-tomorrow

Rails 4 en 30 minutos


Introducción a Rails 4 que se liberara en pocas semanas la versión estable, fueron asombrosas las cosas que mostro como por ejemplo las queues (antes habia que recurrir a una gema adicional para eso) y ActionController::Live


Comida

Merienda con brownies de chocolate explosivo, medialunas, budines, sangunches, almuerzo con sanguches, metasushi, comida vegetariana y desayuno también.
La comida es una de las cosas en la que los organizadores se superaron












REST in peace

No se preocupen, todavia REST no murio (de hecho falta muucho para eso). Martin Salias ataca de nuevo y esta vez nos explica como hay que desarrollar interfaces REST como es debido.
Principales lecciones que me quedaron:

  • Usar verbos y status codes que corresponden (ej: no usar un verbo GET para borrar algo)
  • No devolver identificadores de obetos relacionados en el json/xml, porque el cliente tiene que construir las URLs (y estas podrian cambiar con el versionado, etc...)
  • Sinatra parece ser mejor que rails para hacer algo simple como esto :D


Rapid Prototyping with Sass, Compass and Nanoc

No me acuerdo muchos detalles ahora, pero básicamente explicaron el toolkit de herramientas que usan para prototipar interfaces usando lo que ya se sabe de lenguajes markup de ruby, una de las que mas me intereso fue middleman,  un server de paginas estáticas muy util para prototipar cosas sin necesidad de tener que lidiar con alguna complejidad innecesaria de un framework web

Infrastructure as Ruby code



Explica como Ruby es extremadamente util  (mucho mas de lo que yo me imaginaba) al servicio de un Sysadmin, herramientas como vagrant y chef para administrar/deployar múltiples servidores, ya no en la forma "tradicional" de tener una maquina física que dure mucho tiempo, sino de una manera adaptada a la era del cloud computing en la cual los servidores son "efímeros" y el scripteo, la automatización  la escalabilidad horizontal y la posibilidad de repetir fácilmente las configuraciones se vuelve una herramienta crucial para aprovechar bien los recursos que hay ahora.

Slides: https://speakerdeck.com/u/abecciu/p/infrastructure-as-ruby-code

domingo, 1 de abril de 2012

Menos precision por favor

Estoy cursando numerico en fiuba y se dio la necesidad de probar varios algoritmos en distintos grados de precision (el ejemplo mas claro de esto, es el tipo de datos double y float de C)
, como todo rubyfan decidi hacerlo en ruby, pero ruby solo tiene un tipo de dato de punto flotante, llamado Float, que wrappea el double nativo de C

El truco, como en otros lenguajes con solo una precision de punto flotante, es emularlo; es decir, truncarlo a la precision deseada despues de cada operacion. Ruby tiene soluciones interesantes para hacer este tipo de cosas.

El decorator


En un principio, usamos un patron llamado decorator, con la idea de modificar el comportamiento del float:

Nuestro FloatDecorator repite cada operacion en el objeto "decorado", pero al retornar el resultado lo trunca y lo decora tambien asi mantiene el comportamiento

Asi, la raiz cuadrada de dos 2**0.5 da 1.41 y no 1.4142135623730951






La manera mas rubyway de implementar un decorator es usando method_missing En este caso se implementaria asi:
class FloatPrecisionDecorator
  def initialize(inner, factor)
    @factor = factor
    @inner = inner.to_f
  end

  def method_missing(m,*x)
    # todas las operaciones sobre el numero se ejecutan sobre el float verdadero
    # y se obtiene el verdadero resultado con precision completa del Float original
    verdadero_resultado = @inner.send(m,*x)
    # se reduce la precision, multiplicando por el factor, redondeando y volviendo a dividir
    reduced = (verdadero_resultado.to_f * @factor).round.to_f / @factor
    FloatPrecisionDecorator.new(reduced, @factor)
  end
end

# esto da un FloatPrecisionDecorator con un 1.41, adentro, no un 1.4142135623730951
p FloatPrecisionDecorator.new(2,100)**0.5

Inspect


Pero cuando se hace el print por salida estandar, aparece algo asi:

#..floatprecisiondecorator:0x8cd63f8 factor="10," inner="1.4"...

¿ No seria mejor que simplemente mostrara el 1.41 ? Para eso hay que sobrecargar el metodo inspect

class FloatPrecisionDecorator
  def inspect
    # para que llame al inspect del float decorado
    @inner.inspect
  end
end

# ahora si va a mostra simplemente 1.41
p FloatPrecisionDecorator.new(2,100)**0.5

Numeric


Es mejor si se puede hacer asi:


4.0.to_reduced_precision(:decimals => 2)

4.0.to_rp(:decimals => 2)

Para eso lo mejor es agregarle el metodo a la clase numeric:

class Numeric
  def self.reduce_precision(number, factor)
    (number.to_f * factor).round.to_f / factor
  end

  def to_single_precision(options)
    unless options[:factor]
      options[:factor] = (options[:base]||10) ** (options[:decimals]||10)
    end

    factor = options[:factor]

    FloatPrecisionDecorator.new(Numeric.reduce_precision(to_f, factor), factor)
  end
end

Coerce



Puede surgir que se tenga que sumar un numero de precision reducida a un float, pero el metodo + de la clase Float de ruby no tiene forma de saber como sumar el numero que implementamos nosotros. Esos casos ruby lo contempla con el metodo coerce, que habilita conmutar los numeros en una operacion matematica, asi:
class FloatPrecisionDecorator
  def coerce(other)
    return self, other
  end
end

inifinite?, nan? y demas


Si se hace a nuestro numero


p 2.to_sp(:decimals => 10).infinite?


Va a devolver 0.0, cuando se supone que el metodo inifnite debe devolver true o false, esto ocurre porque inifnite? tambien llama a method_missing y este trata de truncar y wrappear lo que sea, lo mejor en estos casos es evitar modificar objetos que no sean numeros. Habra que modificar method_missing:

class FloatPrecisionDecorator
  def method_missing(m,*x)
    # todas las operaciones sobre el numero se ejecutan sobre el float verdadero
    # y se obtiene el verdadero resultado con precision completa del Float original
    verdadero_resultado = @inner.send(m,*x)

    # si es numeric, truncar y wrappear
    if Numeric === verdadero_resultado
      # se reduce la precision, multiplicando por el factor, redondeando y volviendo a dividir
      reduced = (verdadero_resultado.to_f * @factor).round.to_f / @factor
      FloatPrecisionDecorator.new(reduced, @factor)
    else
      # si no, devolve el resultado como es
      verdadero_resultado
    end
  end
end

Codigo completo





class Numeric
  def self.reduce_precision(number, factor)
    (number.to_f * factor).round.to_f / factor
  end

  def to_single_precision(options)
    unless options[:factor]
      options[:factor] = (options[:base]||10) ** (options[:decimals]||10)
    end

    factor = options[:factor]

    FloatPrecisionDecorator.new(Numeric.reduce_precision(to_f, factor), factor)
  end
end

class FloatPrecisionDecorator
  def initialize(inner, factor)
    @factor = factor
    @inner = inner.to_f
  end

  def method_missing(m,*x)
    # todas las operaciones sobre el numero se ejecutan sobre el float verdadero
    # y se obtiene el verdadero resultado con precision completa del Float original
    verdadero_resultado = @inner.send(m,*x)

    # si es numeric, truncar y wrappear
    if Numeric === verdadero_resultado
      # se reduce la precision, multiplicando por el factor, redondeando y volviendo a dividir
      reduced = (verdadero_resultado.to_f * @factor).round.to_f / @factor
      FloatPrecisionDecorator.new(reduced, @factor)
    else
      # si no, devolve el resultado como es
      verdadero_resultado
    end
  end

  def coerce(other)
    return self, other
  end

  def inspect
    # para que llame al inspect del float decorado
    @inner.inspect
  end
end

p FloatPrecisionDecorator.new(2,10).infinite?



martes, 27 de diciembre de 2011

The Ruby Game


Es un site que tira acertijos o problemas ruby en los que todos pueden participar enviando el codigo y ves los resultados al instante, lo copado es que podes ver un ranking de las soluciones que todos enviaron y aprender cosas interesantes de la sintaxis de ruby

Let's the game begins!

miércoles, 30 de noviembre de 2011

¿Que version de ruby esta seleccionada con RVM?

RVM

RVM es una herramienta impresionante que permite entre muchas otras cosas switchear de versiones de ruby o gemsets con simples comandos ahorrandonos lo que seria un "configuration hell" cada vez que querriamos cambiar la version de ruby (no se puede andar instalando y desinstalando ruby todos los dias)

El que hace un uso intensivo de RVM con múltiples versiones de ruby/gemsets sabra que averiguar la version@gemset activada en un momento dado requiere correr el comndo rvm current, esto podria ser tedioso si se abren multiples tabs y se quiere saber en un vistazo cual es la version de ruby activada y en primera instancia se puede recurrir a cambiar los titulos de los tabs, pero hay otra cosa adicional que se puede hacer

Poner la version de ruby en el prompt

Cambiar el COMMAND_PROMPT en .bashrc o .bash_profile para que muestre en todo momento la version de ruby activada (ver screenshot), incluso se pueden usar colores y poner lo que sea, aca va un ejemplo de la linea que yo agregue al final de mi .bashrc para que el prompt salga como en la imagen:
PROMPT_COMMAND="echo -n -e '\033[1;31m'\$(rvm current) '\033[0;37m'-\ "

Y de aca puede haber un montón de derivaciones, como por ej poner ruby -v en lugar de rvm current o incluso algunos lo usan para mostrar a que host están conectados por ssh

Actualizacion

Una versión mejorada del comando podría ser agregar algún texto diciendo que es rvm al principio y al final en lugar de poner un comando ANSI para retornar al color gris claro específicamente, usar el color "default" que es el color del texto que sale en la terminal (que esta sujeto a la configuración de la terminal y no necesariamente sera gris claro)

PROMPT_COMMAND="echo -n -e '\033[1;31m'rvm: \$(rvm current) '\033[0m'-\ "

martes, 29 de noviembre de 2011

¿RVM + bundler es un problema? NO (Solucion)

Estoy armando un entorno para desarrollo con rails, y rvm... el como instalar rvm se puede ver en un montón de sitios, empezando por el sitio oficial http://beginrescueend.com/rvm/install/

En resumen, con RVM instalado, tiro esta secuencia de comando
el primero para instalar ruby 1.9.2:

sudo rvm install 1.9.2

Despues, si todo salio bien, hay que switchear a esa version de ruby

rvm use 1.9.2

Instalar la gem rails

gem install rails
Hasta ahi todo bien, pero al intentar generar un aplicacion con rails asi:
rails new testapp
Algo no funciona...

El Problema

Veran que bundle se cuelga "indefinidame
nte" y mientras ruby podria llevarse una porción apreciable de CPU (yo por mi parte espere varios minutos y lo único que obtuve fue perder un núcleo durante ese tiempo y el comando no se completo). Si bien en muchos lugares se explica que esperando un rato largo (10 o 15 minutos) el comando se completa, no es muy tolerable para lo que es el "desarrollo rapido" con RoR.

La Solucion

Buscando por la web, me encontré con este articulo, que explica cual es el problema y da una posible solución (que al menos funciono en mi caso). Básicamente explica que el problema tiene que ver con rubygems, no con bundler.
Lo que hay que hacer es asegurarse de que el cache de gems esta generado, esto se hace corriendo el comando bundle pack, como dice la explicación que extraje de la pagina:

WHAT YOU CAN DO

So it’s still slow. My general advice is to:

  • Check in your vendor/cache directory with your .gem files. If bundle install doesn’t make one, force it with bundle pack.
  • On new installs, CI runs, and deploys, use bundle --local which will attempt to resolve using only vendor/cache
  • Lock down to specific versions (or use the twiddle-wakka) in your Gemfile
Y despues deberia tardar mucho menos en ejecutarse

Workaround

Si por casualidad la solución que les di no funciona pueden aplicar el workaround de crear la aplicación con rails usando la opcion --skip-bundle:

Recuerden que esto causa que las gems básicas de la aplicación no se instalen, por lo que tendrán que instalarlas a mano, para eso hay que ver el archivo Gemfile que esta en el directorio raíz de la aplicacion recién creada e instalar las gemas que figuran ahí con el comando gem.

Y después si usan gemas nuevas que no se especifican por default no olviden agregarlas al Gemfile igual ya que es muy útil por si quieren deployar la app a heroku después o simplemente para que las dependencias estén explicitas. Recuerden que después se puede correr el comando "bundle install" a mano en cualquier momento.

Links

Pagina oficial de RVM: http://beginrescueend.com/rvm/
Explicación de como instalar RVM: http://beginrescueend.com/rvm/install/


viernes, 11 de noviembre de 2011

Rubyconf Argentina 2011

No podía dejar de ir a este evento, no voy a contar todo lo que vi porque el post se volveria demasiado extenso, pero voy a incluir las cosas que mas me impactaron.

Ruby Fun Day

Asi llamaron a la previa que organizaron para dos dias antes de evento. Estuvo muy bien pensado y realmente cumplio lo que decia el titulo Ruby-Fun-Day, en un principio no estaba seguro de ir pero mas tarde me di cuenta de que si no hubiera ido me hubiera arrepentido.

El RubyFunDay lo organizaron para los que querian iniciarse en el mundo de ruby y no anden tan desorientados en las charlas (el plato fuerte) pero para los que tienen cancha también estuvo muy bueno porque dio la oportunidad para conocer gente copada, actualizarse y por ahí otras actividades relativas a agile.

Ese dia aproveche la jornada para aprender de rails y heroku que los tenia un poco olvidados en un taller dictado por @bendycode (Stephen Anderson).

Vean el sitio de tryruby recargado con dibujitos del libro Poignant Guide de Why, es impresionante y una forma muy copada de aprender el lenguaje ruby

Ruby, Ruby, Ruby, Ruby, POO OO OO, la cancion de ruby

Desde el martes, el primer dia de la conferencia, escuche la canción. La deje de ringtone en mi telefono, es muy pegadiza:



El presente de ruby es 1.9

Yutaka Hara dio la charla llamada "Pasado, Presente y Futuro de Ruby", básicamente la lección que mas me quedo es que hay que migrar todo a ruby 1.9, porque ruby 1.8 es el pasado, basta hacer unos simples benchmarks para ver que ruby 1.9 es mucho mas rápido que su antecesor sin contar otras mejoras como ligeros improvements en la sintaxis del lenguaje, inclusión "out-of-the-box" de rubygems y un sin numero de otras cosas (talvez en la pagina oficial de ruby se encontrara información detallada)
En un momento de la charla @yhara pregunto quien usa Ruby 1.8 y a mi me llego a dar un poco de vergüenza levantar la mano
Mientras que ruby 1.9 esta muy bueno, siempre tuvo una cosa que no me gusto que es la falta de compatibilidad hacia atrás (igual esto seguramente tiene justificación relacionada con las impresionantes mejores que trae), esta característica hace que la migración a 1.9 sea difícil, no tanto por las aplicaciones hechas por uno mismo, sino por las dependencias ya que no todas las gemas funcionan con ruby 1.9.
Esto no me detendrá para migrar y la respuesta al problema de las dependencias no soportadas en ruby 1.9 es forkear las dependencias, en la gran mayoria de los casos la gema que no anda con ruby 1.9 es solo por un par de lineas que usan sintaxis no soportada por ruby 1.9, lo unico que hay que hacer para que funcione es arreglar esas lineas y listo, pero eso es un tema para otro post...

Links:
Slides de la charla que dio @yhara_en: http://ht.ly/7nOL7
Notas acerca de que voy a hacer para migrar proyectos: http://tario-project.blogspot.com/2011/11/migrating-everthing-to-ruby19.html

Cuba Web Micro Framework

En una espacio que organizaron para "Ligthing Talks" una de las charlas que dieron me llamo bastante la atención, Cuba es un framework mas minimalista que sinatra, vale la pena probarlo, me preguntaria como seria factible deployar eso a heroku

Actualizacion: el usuario maxidr aporto la info necesaria via comentarios en este post para deployar un app hecha con cuba (o con cualquier framework basado en rack) a heroku sin mayor esfuerzo, pasense por http://devcenter.heroku.com/articles/rack para mas info

Ahora, por ejemplo, asi es una aplicacion con tests incluidos:
# cat hello_world.rb
require "cuba"

Cuba.use Rack::Session::Cookie

Cuba.define do
on get do
on "hello" do
res.write "Hello world!"
end

on true do
res.redirect "/hello"
end
end
end

# cat hello_world_test.rb
require "cuba/test"

scope do
test "Homepage" do
visit "/"
assert has_content?("Hello world!")
end
end

Links:

¿Quien hace el mejor asado?

Muy graciosa la charla, despues se puso algo mas serio y explico cosas bastante interesantes. @tenderlove (Aaron Patterson), ruby commiter y rails commiter, analizo porque el router (el componente de rails que decide a que controlador llamar, que acción y con que parametros de acuerdo a la url) actual es lento en muchos casos y explico como se metio en las tripas del algoritmo de regular expression para cambiar el orden del algoritmo de búsqueda de rutas que implica evaluar un mismo texto contra varias expresiones regulares.
El resultado de esa investigación fue Journey, un nuevo enrutador con un algoritmo mas optimo y por lo tanto mas rapido para mas cantidad de rutas. Este se integrara a rails seguramente para la version 3.2

Links:


Super Nario GC

Basicamente el GC de ruby corre en un solo thread mientras que ademas tiene que detener todos los threads en ejecución hasta que finaliza el proceso de garbage collection, Parallel Marking en resumidas cuentas hace posible un garbage collection multi-thread con las consecuencias positivas en la performance que eso conlleva.

Para demostrar cuales fueron los resultados de su trabajo, Nari mostro ahi mismo el Super Mario GC que mostraba en pantalla estadisticas del GC e instanciaba objetos a proposito para provocar que se invoque el GC a cada rato.

Cuando corrio el juego con su implementacion del GC las pausas ocasionadas por este casi no se veian ni se notaban, solo se podia apreciar por un texto en el medio de la pantalla que titilaba una fraccion de segundo cuando el GC se ponia a trabajar, sino no habia manera de saber que el GC estaba trabajando, despues cuando corrio exactamente lo mismo con el GC clasico las pausas pasaban a ser dolorosas para la dinamica del juego.
Nari explico que en la implementacion actual el 30% del CPU era invertido en el GC "Stop-the-world" y que la implementacion de Parallel marking ganaba un 40% de ese tiempo invertido, esto considerando que las pruebas las hizo con un procesador de dos nucleos, pero si se ejecutara en procesadores de 4 u 8 nucleos (que mas o menos es lo que tienden a salir los procesadores asi por estos tiempos) el tiempo invertido en GC seria mucho mucho menor

Ojala que esa optimizacion se incorpore pronto.

El Cuento de los Tres Arboles (en ingles TTT)

@chacon explica en detalle lo que pasa con el subcomando reset de git. Los que nunca usaron git quedaron :O porque sin contexto es muy difícil entender de que se estaba hablando, los que usan git a diario seguramente sacaron algo nuevo de la charla. Espero que mucha gente haya perdido el "miedo" al reset y aprendido que reset --hard usualmente es una pesima idea.
Para aquellos experimentados en git (como yo, muahahahahhahaha) la charla no represento ninguna novedad en lo técnico pero fue una buena lección de como dar lecciones (con diagramas, dibujitos, comparaciones, etc...)

Github wants you

Aparentemente github no descarta la busqueda de talentos mas alla de las fronteras de EEUU donde residen sus headquarters, incluso en lugares donde *actualmente* (por lo menos hasta donde yo se), github no tiene oficinas, porque un lema al que puso enfasis fue "work where and when you want".
@mojombo, Tom Preston-Werner (vendria a ser algo asi como el Batman de git con un logo de pulpo en lugar de murcielago) explico como empezaron github y con muchos estímulos visuales (talvez, creo yo, un poco exagerados) se dedico a convencernos de ir (vacaciones flexibles, seguro medico, ambiente distendido, esas cosas).
Yo, personalmente, no creo que github en ese sentido sea muy diferente a otras compañías, pero trabajar ahi seria muy interesante, por las cosas que hacen que es algo que todos lo ven desde la perspectiva de usuario de la plataforma o "superfan"

Aca la deck de @mojombo:
Igualmente nada se comparara con haber visto esa presentacion en vivo

Ruby hasta en el drinkup

Ni sabia que habia una bebida que se llamaba Ruby (el nombre completo es Absolute Ruby Red) , también vi un lambda el domingo cuando iba al RubyFunDay y la patente del taxi en el que volvi despues del drinkup decia "GEM"

RVM

No hubo una charla acerca de eso, pero hablando con otros rubystas durante la conferencia me di cuenta que tendría que probarlo. Al final lo instale y me di cuenta de que es una magnifica herramienta, sobre todo imprescindible cuando se tiene que trabajar con múltiples versiones de ruby de manera comoda (ejemplo: buscar que una gem sea compatible con ruby 1.8 y ruby 1.9)

Como nota final, talvez me este olvidando de algo, pero ya llego la hora de cortar el post... cualquier cosa seguramente habrá una segunda parte de este post si queda algo mas que decir.


domingo, 13 de marzo de 2011

El blog para los proyectos

Se llama Tario's Project Blog ( http://tario-project.blogspot.com/ ) y lo hice especificamente para los proyectos para los que estoy trabajando bajo ese nickname en github (https://github.com/tario) y posiblemente otros. El objetivo de esto es construir un site separado donde se pueda consultar el status de esos proyectos y al mismo tiempo no saturar este blog con esa clase de informacion. Ademas, Tario's Project sera escrito integramente en ingles para tener un mayor alcance (mas tarde evaluo si implemento la version en español del blog o aprovechando alguna que otra feature de internacionalizacion que vi que tiene blogger)

Lo ideal en realidad, seria dedicar una pagina a cada proyecto, pero en este caso los proyectos son chicos y son muchos ademas de que estan muy relacionados unos con otros (por ej, el release de cierto gem puede tener alto impacto en la usabilidad de otro ya existente), mas adelante, si corresponde, habra un site para proyectos que crezcan en importancia.

Enlaces:

El nuevo blog: http://tario-project.blogspot.com/
El sitio de repositorios en github: https://github.com/tario

martes, 25 de enero de 2011

Buscar la raiz cuadrada mas rapida, con Ruby

Se vienen los examenes y uno de ellos es el de Analisis Numerico, y como estoy usando mucho la raiz cuadrada en un proyecto en el que estoy, me decidi a buscar una raiz cuadrada mas rapida que la que trae el propio framework ;)

La verdad, es que por default, las librerias matematicas calculan la raiz cuadrada con una muy buena precision, pero pueden surgir casos en los que esa precision es innecesaria (por ejemplo, si se trunca el resultado de la raiz cuadrada, es decir, tirar a la basura los decimales que se calculo) y lo que si es importante es poder efectuar los calculos rapido

Sea cual sea el lenguaje en e que se va a programar, ruby esta bueno para probar los algoritmos muy facilmente, en este caso se utiliza el metodo de Newton-Raphson para calcular la raiz cuadrada de un numero


# x**2 - a = 0

# calcula la raiz cuadrada y se le puede especificar la precision, es decir
# la cota superior del error del resultado
def Math.my_sqrt(a, precision)

x = 1
oldx=1

iter = 0
# newton raphson
while 1
oldx = x
x = x - ( x**2 - a ) / ( 2 * x)
break if (oldx-x).abs < precision
end

x
end

def show( code )
print "#{code} => #{eval(code)}\n"
end

show "Math.my_sqrt(2.0, 0.5)"
show "Math.my_sqrt(2.0, 0.05)"
show "Math.my_sqrt(2.0, 0.005)"
show "Math.my_sqrt(2.0, 0.0005)"

show "Math.sqrt(2.0)"



Salida del programa


Math.my_sqrt(2.0, 0.5) => 1.41666666666667
Math.my_sqrt(2.0, 0.05) => 1.41421568627451
Math.my_sqrt(2.0, 0.005) => 1.41421568627451
Math.my_sqrt(2.0, 0.0005) => 1.41421356237469
Math.sqrt(2.0) => 1.4142135623731


Enlaces:

Metodo de Newton explicado en la Wikipedia
Explicacion del metodo de newton usado para calcular la raiz cuadrada de un numero

lunes, 16 de agosto de 2010

La magia del TDD: un ejemplo real en ruby

Este fin de semana comence un proyecto, una nueva gema que "desparsea" los arboles generados por la gema ParseTree. Explicar los detalles de ese proyecto no es el tema de este articulo, sino mas bien contar la experiencia con TDD.

Para los que no saben que es TDD la wikipedia explica. Sin son vagos como para clickear el link, TDD es una metodologia que basicamente tiene dos pilares: "write tests first" (escribir los test primero) y "refactoring".

Resulta que lo que estoy desarrollando consiste en una funcion que revierte o encuentra la preimagen de otra funcion (es decir, genera un string con codigo ruby que resulte en un determinado arbol cuando se parsee con ParseTree).

Este requerimiento tiene un grado muy alto de testeabilidad ya que se puede calcular el input correspondiente a partir del output esperado (seria como el testeo oracle, pero al revez porq estamos buscando hallar la inversa de una funcion que ya tenemos, que es ParseTree)

Manos a la obra:

Paso 1) Escribir los tests primero:
http://github.com/tario/unparse_tree/tree/0ca29f14

En este caso se fue desarrollando de manera de ahorrar lineas y probar *todos* los casos (mas adelante se sabe que se omitieron algunas cosas, pero creo q eso es comun en TDD sobre todo cuando el analisis de cobertura no es viable)

Paso 2) Implementar hasta que todos los tests pasen:
http://github.com/tario/unparse_tree/tree/48c9a997

Cambios hasta que funcione, algunos "harcodean" o ponen "magic values", otros agregan funcionalidad a conciencia, lo importante es que cada commit disminuye el numero de errors + failures reportados y no se modifican los tests

Paso 3) Refactoring:

En esta etapa se modificara el codigo hasta que se vea minimamente "bien", es decir, se eliminara codigo duplicado, se implementaran las cosas "de verdad" en lugar de usar "magic values", "harcodeos" o "if extraños" y cualquier cambio que mejore el diseño/la distribucion del codigo, de mas esta decir que por cada cambio todos los tests siempre tienen que pasar. Todavia no alcance esta etapa en el proyecto... ya llegara.

Paso 4) Vuelta al paso 1:

Con nuevos requerimientos y/o bugs encontrados, se genera una nueva bateria de tests y el ciclo vuelve a comenzar.

De todo esto, en la practica me encuentro con las siguientes ventajas de usar TDD:

* La validacion de si el codigo funciona es automatica, con solo ejecutar un comando puedo saber si "rompi" el codigo o si estoy progresando
* TDD guia a escribir el codigo de manera "correcta" o por lo menos mas prolija
* Se puede testear un numero mas grande de casos, con lo que se cubren mayor cantidad de posibilidades

Y, como todo, tiene sus desventajas (aunque se mitigan y son evidentamente superadas por las ventajas)

* Hay que programar las pruebas que si no se usara TDD, no se tendrian que programar (de todas maneras, con o sin TDD siempre hay que testear el software, y sin TDD se hace de forma manual)
* Las pruebas no garantizan cobertura total, es decir, pueden ser %100 exitosas las pruebas y el software fallar en la practica al haber omitido algo (esta claro que la idea en principio no es confiar ciegamente en las pruebas, mas bien TDD tiene que basarse en la construccion de pruebas)
* Durante la fase de desarrollo, me doy cuenta de errores que se ven en el codigo o al escribir el codigo, pero no puedo corregirlos si no existen tests que pasen como resultado de esa correccion (para resolver esto se puede optar principalmente por dos alternativas, una seria registrar como un "ticket" la necesidad de agregar el test para el proximo ciclo, la otra seria agregar el test en ese mismo momento, aunque eso se alejaria del espiritu del TDD y superpondria los pasos 1 y 2)

Cabe destacar que las ventajas del TDD asi como otras metodologias son apreciables a mediano y largo plazo (TDD mas que otras), ya que a medida que avancen los ciclos de TDD, los tests desarrollados en ciclos anteriores cumpliran el rol de automatizar las regresiones, es decir, que si se modifica el codigo en un proyecto que tiene 8 meses de vida, las pruebas automaticas rebelaran si se rompio funcionalidad implementada al principio del proyecto, tres meses atras, dos meses atras, que implemento otra persona, etc...
Despues de varias iteraciones, volvere a postear para hacer un analisis de las ventajas de TDD a largo plazo

Enlaces

http://en.wikipedia.org/wiki/Test-driven_development
http://github.com/tario/unparse_tree/tree/0ca29f14 (tests creados en el primer ciclo de unparse_tree)
http://github.com/tario/unparse_tree

martes, 22 de junio de 2010

Sandbox de codigo ruby (remake 2010)



Anteriormente en este mismo blog anuncie la creacion de un sandbox para ruby. Ese proyecto se cancelo y quedo definitivamente incompleto. El proyecto se llamaba arena-ruby-sandbox.

Hace un tiempo comenze el desarrollo de un nuevo sandbox (llamado shikashi), pero con un diseño y una idea totalmente distinta: mientras arena-ruby-sandbox es un intento de sandbox en codigo ruby puro, shikashi se implementa utilizando una modificacion al interprete de ruby hecha en C en formato de extension (llamada rallhook).

Funciona mejor que su antecesor ademas de permitir usar casi todos los elementos del lenguaje como clases y metodos singleton cosa que arena-ruby-sandbox no permitia.

Recientemente libere la version 0.7.1 de rallhook ademas de la version 0.1.0 del sandbox shikashi. La documentacion del gem (que incluye instrucciones de como instalarlo, ejemplos de uso y descripcion completa del API) esta en http://tario.github.com/shikashi/doc/

NOTA: tambien se puede instalar haciendo

gem install shikashi

Cosa que no menciona el README y lo actualizare en proximos releases.

domingo, 13 de junio de 2010

Renovado diseño del blog

Tarde de domingo sin saber que hacer, renovacion visual del blog. Se hicieron los siguientes cambios.

* Se cambiaron los colores blancos, claros de blogs de psicologia newbies por colores darks, bizarros propios del underground, el H4X0R1NG, el KRAK0RING y PRHEKIRING aunque el blog NO PUBLICARA ARTICULOS DE ESA CATEGORIA :P (para eso hay blogs muy buenos en la web)

* Se cambiaron los colores del syntax highlighting en el style para que tenga sentido con los nuevos colores (todo dark) y se incluyo el style como tag en el template del blog para acelerar la carga de la pagina

* Se incluyeron todos los Javascripts de highlighting en el template del blog (en los tags ) en lugar de que sean referencia remota al sitio donde estaban alojados, con esto se acelera por mucho la carga de la pagina.

* Se cambio el fondo por uno de esos fondos "geeks" que hay en las opciones, pero como esto es ruby, se pinto de rojo (el original era azul)

Lo que mas me ayudo fue el plugin firebug para probar como quedan los colores antes de grabar en el template, y analizar un poco el source code de la pagina.
Tambien me sirvio bastante el irb para escapear el javascript, la libreria CGI:


require "cgi"
CGI.escapeHTML("<script>alert('hello world')</script>");
# => "&lt;script&gt;alert('hello world')&lt;/script&gt;"


Espero que la experiencia blogger sea mas ruby ahora

sábado, 29 de mayo de 2010

Definicion de clases y metodos en un eval

Una de las miles de cualidades que tiene ruby como lenguaje dinamico es la ejecucion de codigo ruby contenido en un string mediante el uso de la funcion eval.
Asi se pueden hacer cosas bastante interesantes ejecutando codigo ingresado dinamicamente ^_^
No obstante es necesario conocer como sortear determinados obstaculos en la ejecucion de codigo dinamico


class Runner
def run(code)
eval(code)
end
end

runner = Runner.new

# ¡¡FAIL!! :(, no se puede definir classes desde adentro de un metodo (Runner#run)
runner.run("
class X
def foo
print \"hello world\n\"
end
end

x = X.new
x.foo
")



Lo que se intento hacer en el ejemplo anterior, para el interprete es equivalente a:


class Runner
def run(code)
# いけない!!!
# en ruby no esta permitido definir clases adentro de metodos
# (aunque si permite hacer otras cosas locas XD )
class X
def foo
print \"hello world\n\"
end
end

x = X.new
x.foo
end
end


Para poder hacerlo correctamente hay que pasarle un segundo parametro a eval que es el "binding", un binding representa el contexto en ruby de donde se llama el binding, que incluye las variables locales, etc... mejor verlo en el ejemplo que explicarlo:


class Runner

class << self
attr_accessor :runner_binding
end

def run(code)
# se especifica un binding al eval para definir el contexto
# en el que se ejecuta el codigo
eval(code, Runner.runner_binding)
end
end

# se copia el binding del contexto actual (fuera de cualquier clase o metodo)
Runner.runner_binding = binding

runner = Runner.new

# se puede definir clases en el codigo pasado a eval, porque el binding
# esta afuera de cualquier declaracion de clase o metodo y el eval se
# llama con ese binding
runner.run("
class X
def foo
print \"hello world\n\"
end
end

x = X.new
x.foo
")




Que para el interprete es lo mismo que reemplazar el codigo en donde se llama a binding en lugar de en donde se llama a eval:


class Runner

class << self
attr_accessor :runner_binding
end

def run(code)
# se especifica un binding al eval para definir el contexto
# en el que se ejecuta el codigo
eval(code, Runner.runner_binding)
end
end

# よろしい
# Runner.runner_binding = binding
class X
def foo
print \"hello world\n\"
end
end

x = X.new
x.foo




Y ya que estamos, les cuento que otra cosa tiene el eval para hacer


def bar
1/0 # ZeroDivisionError
end

def foo(code) # ¿porque siempre foo?
eval(code)
end

foo("bar") #


Resultado:


test.rb:6:in `foo': test.rb:2:in `/': divided by 0 (ZeroDivisionError)
from test.rb:2:in `bar'
from (eval):1:in `foo'
from test.rb:9:in `eval'
from test.rb:6:in `foo'
from test.rb:9



Mejor, pasarle unos argumentos adicionales a eval especificando el "archivo" y el numero de linea de donde viene el codigo evaluado


def bar
1/0 # ZeroDivisionError
end

def foo(code) # ¿porque siempre foo?
eval(code, binding, "foo_eval.rb", 1)
end

foo("bar") #


Resultado:


test.rb:2:in `/': divided by 0 (ZeroDivisionError)
from test.rb:2:in `bar'
from foo_eval.rb:1:in `foo'
from test.rb:6:in `foo'
from test.rb:9


Ahora se puede ver mejor en el backtrace de donde viene el codigo, con esto mejorada la trazabilidad cuando se usa eval.

miércoles, 24 de marzo de 2010

Como escribir una extension ruby en C en 5 minutos

Lo siguiente toma lugar entre las 15:51 pm y las 15:56 pm

Para crear una extension en ruby lo primero que hay que hacer, es crear el "extconf.rb", basicamente tiene que tener el siguiente contenido:


# extconf.rb
require 'mkmf'

dir_config("exttest")

create_makefile("exttest")


Posteriormente, hay que crear el archivo del codigo fuente en C de la extension, que minimamente tiene que haber uno, en este caso creamos uno de prueba, bastante resumido, el cual crea un modulo ruby y le define dos metodos, ambos implementados en C:


// exttest.c

#include "ruby.h"

static VALUE foo(VALUE self) {
printf("Foo invoqued\n");
return Qnil;
}

static VALUE sum(VALUE self, VALUE a, VALUE b) {
return INT2NUM( NUM2INT(a) + NUM2INT(b) );
}


void
Init_exttest()
{
VALUE rb_exttest = rb_define_module("Exttest"); // definir el modulo

rb_define_singleton_method(rb_exttest, // en que modulo
"foo", // nombre del metodo
foo, // puntero a la implementacion C del metodo
0 // cantidad de argumentos del metodo
);

rb_define_singleton_method(rb_exttest, // en que modulo
"sum", // nombre del metodo
sum, // puntero a la implementacion C del metodo
2 // cantidad de argumentos del metodo
);

}




Para generar el makefile (solo hay que hacer esto una vez), se ejecuta el script extconf.rb:

ruby extconf.rb


Para construir la extension, se debe lanzar el siguiente comando:


make


Eso genera, entre otros archivos intermedios, extconf.so, el cual contiene la extension misma compilada a codigo nativo de la plataforma donde estan corriendo todo, para instalarla en el sistema, hay que correr (como root):


make install


Para usar la extension:



# test.rb
require 'rubygems'
require 'exttest'

Exttest.foo # el metodo foo definido en exttest.c!!
print Exttest.sum(4,9),"\n" # el metodo sum definido en exttest.c!!



Posteriormente, se pueden hacer cosas mas interesantes como definir clases, clases anidadas en modulos, metodos, etc...
Para la proxima voy a explicar como encapsular la extension como gem y distribuirla

links:

* http://onlamp.com/pub/a/onlamp/2004/11/18/extending_ruby.html: Fuente que inspiro este articulo y ejemplo practico de como wrappear la libreria GenX como libreria ruby GenX4r
* http://rhg.rubyforge.org/chapter04.html: Tutorial que muestra como definir clases y modulos
* http://ruby-doc.org/doxygen/1.8.4/group__ruby__interp.html: Documentacion completa de funciones C para definir extensiones de ruby incluyendo funciones de conversion de datos

domingo, 29 de noviembre de 2009

Sandbox de codigo ruby

La semana pasada empece el desarrollo de una libreria de ruby que permita crear "sandbox" de codigo ruby

Un sandbox (traducido literalmente del ingles como "caja de arena") es un entorno "protegido" que permite ejecutar programas de una manera en la que no es posible que estos programas afecten a lo que esta "afuera" del sandbox (Por ej: ejecutar un programa de manera que no pueda tener acceso al sistema de archivos de la computadora en la que se esta ejecutando)
Enlace
Para el caso particular de ruby, cuando se da, por ejemplo, el caso del desarrollo de una plataforma que ejecuta dinamicamente codigo enviado por muchos usuarios... es esperable que este codigo no pueda acceder al sistema de archivos o a la bases de datos de la aplicacion (el caso de una aplicacion Rails). En ese caso un "sandbox" que encierre al codigo en su ejecucion restringiria la ejecucion de codigo no deseado.

En lo que se ve a simple vista en la web, no hay mucho desarrollo al respecto... y lo que hay pareciera ser plugins algo mas especificos de rails como acts_as_runnable_code. Es por eso que comence el proyecto arena-ruby-sandbox, que define la posibilidad de ejecutar el codigo asi:




require "sandbox"

class X
def foo(a)
print "foo(#{a})\n"

# estoy afuera del sandbox, por lo tanto puedo
# hacer cosas "privilegiadas" como crear archivos
# cosa que no puede hacerse desde adentro del sandbox
File.open("foo.txt") do |f|
f.write "foo(#{a})\n"
end

a+5
end

def foo_no_invocable
print "foo_no_invocable\n"
end
end

# instanciar el sandbox
s = Sandbox::Sandbox.new

x = X.new

s.serivice_provider = x
# indica que el metodo foo se puede ejecutar desde adentro del sandbox
s.permit :foo

print s.run("2+2") # => 4

print s.run("foo(2)") # => 7 (y escribe el archivo foo.txt con "foo(2)" )

print s.run("foo_no_invocable") # => NoMethodError (nunca se especifico que foo_no_invocable se podia invocar)

s.run("
File.open('foo.txt') do |f|
f.write \"foo\n\"
end
") # => NameError: uninitialized constant File
# ( la clase File no existe adentro del sandbox
# no se puede acceder al filesystem desde el sandbox
links

http://arena-ruby-sandbox.googlecode.com

martes, 24 de noviembre de 2009

Autocarga perversa

Textualmente, la rdoc del core de ruby, dice asi:

mod.const_missing(sym) => obj

Invoked when a reference is made to an undefined constant in mod. It is passed a symbol for the undefined constant, and returns a value to be used for that constant. The following code is a (very bad) example: if reference is made to an undefined constant, it attempts to load a file whose name is the lowercase version of the constant (thus class Fred is assumed to be in file fred.rb). If found, it returns the value of the loaded class. It therefore implements a perverse kind of autoload facility.


def Object.const_missing(name)
@looked_for ||= {}
str_name = name.to_s
raise "Class not found: #{name}" if @looked_for[str_name]
@looked_for[str_name] = 1
file = str_name.downcase
require file
klass = const_get(name)
return klass if klass
raise "Class not found: #{name}"
end


Esa funcion puede utilizarse para implementar "alguna clase perversa de mecanismo de autocarga", para citar un ejemplo, rails lo utiliza como parte de su funcionamiento habitual para recargar las clases automaticamente sin necesidad de reiniciar el servidor ni ejecutar ningun comando explicito (¿rails = perversion?, esto me emociona).

links

http://ruby-doc.org/core/classes/Module.html#M001693

http://www.google.com.ar/search?q=%22It+therefore+implements+a+perverse+kind+of+autoload+facility%22

jueves, 16 de julio de 2009

Ruby RAII

RAII (Resource Acquisition Is Initialization) es una técnica aplicable en muchos lenguajes de programación como C++ y los lengujaes .net que sirve para asegurar que la liberación de un recurso siempre se llevara a cabo al cerrarse un bloque de código sin importar que evento ocurrise (retorno de una funcion, excepcion, etc...). Ruby por supuesto que cuenta con esta posibilidad a través del uso de métodos con bloques de código (palabra clave yield)

He aqui un ejemplo:


#
# La clase X tiene un metodo open para inicializar (open)
# Y uno para cerrar, seria una tragedia si el metodo open
# se invocara y no se invoque el close correspondiente al final
#
#
class X
def open( argument1, argument2 )
print "open with argument1=#{argument1} and argument2=#{argument2}\n"
end

def operation1
print "running operation1\n"
end

def operation2
print "running operation2\n"
end

def close
print "closing\n"
end
end


#
# Mejor usar RAII
#

class X
def self.open( *args )
x_instance = X.new
x_instance.open( *args )
begin
yield(x_instance)
ensure
x_instance.close
end
end
end


#
# Y se usa asi
#
X.open( "arg1", "arg2" ) do |x|
x.operation1
x.operation2
end




Espero que os haya iluminado

martes, 23 de junio de 2009

NilClass horror

Un horror




# el nil class es la clase de la cual nil es instancia
class NilClass
# todo metodo desconocido que es invocado sobre el nil
def method_missing(method_obj,*args)
method_obj.to_s
end
end

print nil.koiiiewiiriieiw,"\n"

print @oooooooooooooooooooooo.blahhhhhhhhhhhhhhhhhhhhhhhhh,"\n"
print @ewewewwwewo.dakishimetakokoronokusumo,"\n"

# un horror
print $uqhwuiqwhrqrqwrkjwqhrjkqwhrqwkjrhqwrjkwqrhkqwrjqwrj.l,"\n"



Un horror

domingo, 21 de junio de 2009

Comparacion de rangos

Es muy común al programar, encontrarse con la necesidad de verificar algún rango de algo:


# la manera "dificil"
if a > 3 and a < 9 then
print "a esta entre 3 y 9\n"
end


Mejor asi, usando el operador ===


# la manera "canchera"
if (3..9) === a then
print "a esta entre 3 y 9\n"
end


Como si esto fuera poco, el operador === tiene todos los usos que uno se pueda imaginar, como por ej, para verificar si un texto matchea una expresion regular:


# la manera "canchera"
if /^ruby/ === txt then
print "'#{txt}' tiene ruby al principio\n"
end


Y si se les da la gana, pueden usar este operador en sus propias clases (también obviamente definirlo o redefinirlo para la clase existente que quieran)



class X
def ===(a)
print a,"\n"
end
end

viernes, 5 de junio de 2009

Ruby hooking

Les traigo un truco que los asombrara... con una tenica de hooking aplicada al ruby para poder enganchar los metodos que quieran de cualquier objeto y controlar su ejecucion, asi como registrar llamados a ese metodo , filtrar cosas, etc.

Aca el module q lo hace posible:

hooking.rb


module Hook
def hook( method_name, hook_name = nil, old_method_name = nil)

obj = self

# si no se le pasa el nombre de la funcion que controla el hook
# arma un nombre de funcion
hook_name ||= "#{method_name}_hook"

#lo mismo con el nombre de la variable de instancia
# que almacena la referencia al metodo original
old_method_name ||= "__old__#{method_name}"

# almacenar el metodo original
eval("@#{old_method_name} = self.method( method_name.to_sym)")

# redefinir el metodo para que invoque el metodo de hook, pasandole
# un bloque de codigo que ejecute el metodo original, entonces el metodo
# de hook puede invocar al metodo original usando yield
eval("
def obj.#{method_name}(*x)
self.#{hook_name}(@#{old_method_name},*x) do |*x|
yield(*x) if block_given?
end
end
")

end
end

class Object
# includir el modulo de Hook en Object para que se pueda aplicar a todo Object
include Hook
end



Y ahora un pequeño ejemplo de como podria usarse

main.rb


require "hooking"

class X

def foo( num )
print "metodo foo invocado con num = #{num}\n"
end

def bar
print "metodo bar invocado\n"
[1,2,3].each do |x| yield(x) end
end

end

x = X.new

x.hook(:foo)

def x.foo_hook( foo_original, num )
print "foo invocado\n"

# se invoca al metodo original, pero alterando los parametros
foo_original.call( num+1)

print "fin de foo\n"
end

x.hook :bar
def x.bar_hook( bar_original )

print "bar invocado\n"

bar_original.call do |x|
# tambien se puede alterar y controlar
# el uso de yield del metodo
yield(x+2)
end

print "fin de bar\n"
end


x.foo(3)
x.bar do |x|
print x,"\n"
end