La metaprogramación es una técnica avanzada en Ruby que permite a los programas escribir programas. En otras palabras, es la capacidad de un programa para manipular su propia estructura y comportamiento en tiempo de ejecución. Esto puede incluir la creación de métodos dinámicamente, la modificación de clases y módulos, y la manipulación de objetos de manera flexible.

Conceptos Clave

  1. Métodos Dinámicos: Crear métodos en tiempo de ejecución.
  2. Métodos define_method y method_missing: Utilizados para definir y manejar métodos dinámicamente.
  3. Clases y Módulos Abiertos: Modificar clases y módulos existentes.
  4. Refinamientos: Modificar el comportamiento de clases y módulos en un contexto limitado.
  5. Evaluación de Código: Ejecutar código Ruby desde cadenas de texto.

Métodos Dinámicos

Ruby permite definir métodos de manera dinámica utilizando define_method. Esto es útil cuando necesitas crear métodos basados en datos que no están disponibles hasta el tiempo de ejecución.

class DynamicMethods
  [:foo, :bar, :baz].each do |method_name|
    define_method(method_name) do
      puts "You called #{method_name} method"
    end
  end
end

obj = DynamicMethods.new
obj.foo  # Output: You called foo method
obj.bar  # Output: You called bar method
obj.baz  # Output: You called baz method

Explicación

  • define_method: Define un método con el nombre especificado en tiempo de ejecución.
  • each: Itera sobre un array de símbolos, creando un método para cada uno.

Método method_missing

El método method_missing se invoca cuando se llama a un método que no está definido en un objeto. Esto permite manejar llamadas a métodos indefinidos de manera flexible.

class DynamicResponder
  def method_missing(method_name, *args, &block)
    puts "You tried to call #{method_name} with arguments: #{args.join(', ')}"
  end
end

obj = DynamicResponder.new
obj.any_method(1, 2, 3)  # Output: You tried to call any_method with arguments: 1, 2, 3

Explicación

  • method_missing: Captura llamadas a métodos no definidos y permite manejar la lógica personalizada.
  • *args: Captura todos los argumentos pasados al método.
  • &block: Captura cualquier bloque pasado al método.

Clases y Módulos Abiertos

Ruby permite reabrir clases y módulos para agregar o modificar métodos. Esto es conocido como "monkey patching".

class String
  def shout
    self.upcase + "!!!"
  end
end

puts "hello".shout  # Output: HELLO!!!

Explicación

  • Reabrir clases: Puedes agregar métodos a clases existentes sin necesidad de heredar.

Refinamientos

Los refinamientos permiten modificar el comportamiento de clases y módulos en un contexto limitado, evitando los efectos secundarios globales.

module StringRefinements
  refine String do
    def shout
      self.upcase + "!!!"
    end
  end
end

using StringRefinements

puts "hello".shout  # Output: HELLO!!!

Explicación

  • refine: Define un refinamiento para una clase.
  • using: Activa el refinamiento en el contexto actual.

Evaluación de Código

Ruby permite ejecutar código desde cadenas de texto utilizando eval.

code = "puts 'Hello from eval'"
eval(code)  # Output: Hello from eval

Explicación

  • eval: Ejecuta el código contenido en una cadena de texto.

Ejercicio Práctico

Ejercicio 1: Crear Métodos Dinámicamente

Crea una clase DynamicGreeter que defina métodos de saludo (hello, hi, hey) dinámicamente.

class DynamicGreeter
  # Tu código aquí
end

greeter = DynamicGreeter.new
greeter.hello  # Output: Hello!
greeter.hi     # Output: Hi!
greeter.hey    # Output: Hey!

Solución

class DynamicGreeter
  [:hello, :hi, :hey].each do |method_name|
    define_method(method_name) do
      puts method_name.to_s.capitalize + "!"
    end
  end
end

greeter = DynamicGreeter.new
greeter.hello  # Output: Hello!
greeter.hi     # Output: Hi!
greeter.hey    # Output: Hey!

Ejercicio 2: Usar method_missing

Crea una clase DynamicCalculator que maneje métodos de suma, resta, multiplicación y división utilizando method_missing.

class DynamicCalculator
  # Tu código aquí
end

calc = DynamicCalculator.new
puts calc.add(1, 2)       # Output: 3
puts calc.subtract(5, 3)  # Output: 2
puts calc.multiply(4, 2)  # Output: 8
puts calc.divide(8, 2)    # Output: 4

Solución

class DynamicCalculator
  def method_missing(method_name, *args, &block)
    case method_name
    when :add
      args[0] + args[1]
    when :subtract
      args[0] - args[1]
    when :multiply
      args[0] * args[1]
    when :divide
      args[0] / args[1]
    else
      super
    end
  end
end

calc = DynamicCalculator.new
puts calc.add(1, 2)       # Output: 3
puts calc.subtract(5, 3)  # Output: 2
puts calc.multiply(4, 2)  # Output: 8
puts calc.divide(8, 2)    # Output: 4

Conclusión

La metaprogramación en Ruby es una herramienta poderosa que permite escribir código más flexible y dinámico. Sin embargo, debe usarse con cuidado para evitar complicaciones y mantener la claridad del código. En esta sección, hemos cubierto los conceptos básicos de la metaprogramación, incluyendo métodos dinámicos, method_missing, clases y módulos abiertos, refinamientos y evaluación de código. Con estos conocimientos, estás preparado para explorar y aplicar técnicas de metaprogramación en tus proyectos Ruby.

© Copyright 2024. Todos los derechos reservados