Vicente Rodríguez

Nov. 2, 2016

Metaprogramación en ruby parte 2

En este tutorial veremos mas métodos de ruby que nos harán expertos en metaprogramación.

Métodos dinámicos

Ruby tiene un método llamado send, lo que hace es llamar a un método de un objeto o clase pero tiene una sintaxis especial. Primero vamos a requerir el archivo anterior para tener la clase Fabrica:


require_relative "mruby1.rb"



nissan = Fabrica.new("nissan", "motos")

puts nissan.send("do?")

require_relative funciona para requerir archivos que estén en la misma carpeta.

El método send toma dos parámetros como argumentos, el primero el nombre del método y el segundo los parámetros que toma ese método, si es que tiene alguno.

Como send puede llamar a métodos que son privados, existe public_send, que regresara un error si intentamos llamar a un método privado.

Este sencillo método, puede hacer cosas muy poderosas llamando a los métodos que queramos, podríamos combinarlo con ARGV[0] para pasar el nombre del método por consola y ejecutarlo inmediatamente:


method = ARGV[0]



puts nissan.public_send(method)

Define method

Ya vimos la manera de llamar métodos pasando su nombre como texto, esto nos permite llamar un método cualquiera en tiempo de ejecución, también existe una función para crear métodos, igualmente se pasa el nombre del método y sus parámetros si es que tiene.


method = ARGV[0]



define_method "#{method}" do |num|

  puts "hola desde #{method}, #{num * 2}"

end



send(method,12)

Como se puede notar, se le manda un bloque al método donde se pueden definir los argumentos.

Hay algo interesante sobre los bloques, que se refiere al scope, pero lo veremos mas adelante.

Method missing

Otra forma de definir métodos dinámicamente, es con la función method_missing, esta función es llamada cuando ejecutas un método que no existe, pero podemos sobre escribirlo para que actúe como queramos, aunque es importante seguir llamando a la función principal o manejar errores:


class Fabrica

  def method_missing(name)

   begin

    puts "Error, el metodo no es valido: "

    puts "Lista de metodos: #{Fabrica.instance_methods(false)}"

   rescue NoMethodError

     puts "error en el metodo"

   end

 end

end



nissan = Fabrica.new("honda", "coches")

nissan.hey

Bloques

Para hacer memoria sobre el uso de bloques checaremos lo básico sobre ellos.

Se puede pedir a un método que se pase como parámetro un bloque:


def sumar

  puts yield(2,4)

end



sumar() {|x,y| x + y}

Hay dos sintaxis para pasar bloques {} o do, funcionan igual, solo depende de cuantas instrucciones quieras pasar, si es mas de una es mejor usar do.

Los bloques funcionan de una forma muy peculiar, ruby crea un nuevo scope cuando se crean funciones (entre def y end), clases y módulos, entonces si intentáramos hacer algo como esto:


x = "hola"

def saludar

  puts x

end

saludar()

Tendríamos un error porque no logra encontrar la variable x, sin embargo si en lugar de usar def para construir el método, usáramos define_method que es un bloque:


x = "hola"

define_method :saludar do

  puts x

end

saludar()

No tendríamos error, porque los bloques funcionan como closures, arrastran el scope de donde son definidos.