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

No hay comentarios: