Entendiendo la metaprogramación con Ruby
¿Qué es metaprogramación?
Según la definición que encontramos en Wikipedia :
“La meta-programación consiste en escribir programas que escriben o manipulan otros programas (o a sí mismos) como datos, o que hacen en tiempo de compilación parte del trabajo que, de otra forma, se haría en tiempo de ejecución. Esto permite al programador ahorrar tiempo en la producción de código.”
Pero una definición mas concisa y a mi parecer más clara la encontré en este artículo:
“Es la habilidad de usar código para generar código”
Existen multiples técnicas para lograr este objetivo, una de la más sencilla y en su tiempo bastante utilizada, consistía en generar instrucciones de código utilizando variables del tipo String
y concatenandolas o escribiéndolas en archivos planos.
Con la creación y evolución de nuevas técnicas para llevar a cabo esta tarea, nos encontramos con nuevos conceptos que hacen parte del universo de la metaprogramación como son la Introspección y la Reflexión.
Introspección
La definición general de la palabra según Wikipedia:
“Introspección o inspección interna designa la idea de «mirar al interior».”
En nuestro caso se convertiría en la habilidad que tiene un programa para analizar su entorno durante la ejecución, habilidad que le permite interrogar a los objetos que lo componen.
Reflexión
La definición general de la palabra según Wikipedia:
“Es el proceso que permite pensar detenidamente en algo con la finalidad de sacar conclusiones.”
Esto sería la habilidad de ver y alterar la información de los elementos del programa durante la ejecución, habilidad que le permitiría extender o modificar los objetos sobre la marcha.
¿Cómo aplicar metaprogramación con Ruby?
“Ruby es un lenguaje de programación interpretado, reflexivo y orientado a objetos, creado por el programador japonés Yukihiro “Matz” Matsumoto”
Si vemos la definición en Wikipedia del lenguaje de programación Ruby, podemos notar una característica muy particular, que es reflexivo, esto nos da una pista sobre que el lenguaje esta construido con esta propiedad en mente y que es muy posible que nos proporcione herramientas para lograr este cometido de una manera más amigable.
En algún momento hemos escuchado la frase “dentro del lenguaje Ruby todo es un objeto”, pero pienso que en la arquitectura en la que fue inspirado y diseñado el lenguaje, lleva este concepto a un nivel diferente.
Así podemos ver como un aparentemente y simple numero o cadena de caracteres, en realidad son objetos o instancias de Clases y de igual manera podemos ver aplicado el concepto de Introspección al utilizar el método .class
en un objeto para saber a que Clase pertenece.
Incluso una clase es un objeto en si; su clase pertenece a una clase especial llamada Class
y todas las clases creadas en Ruby en realidad son una instancia de la clase Class
:
Class implementa algunos interesantes métodos de introspección como:
Y de reflexión como:
Gracias a métodos como estos dentro de la clase Class
, podemos manipular nuestras propias clases de manera Reflexiva:
Conociendo la existencia de estos métodos, podemos entender de una mejor manera lo que se denominan “Macros a nivel de clases” o simplemente métodos a nivel de clase que generan código detrás de escena:
Como podemos ver attr_accessor
no pertenece o es una palabra clave (keyword) dentro del lenguaje Ruby, sino que representa un método de la clase Class
que genera código Ruby dentro de otras clases de manera dinámica.
Veamos con un ejemplo cómo podemos definir nuestras propias macros a nivel de clases, en este caso realizaremos nuestra propia implementación del método attr_accessor
el cual realizara la creación de las variables de instancia, de los métodos setter y getter y adicional imprimirá por consola un mensaje personalizado:
Para este ejemplo hicimos uso del método define_method
de la clase Class
el cual nos permite realizar la definición de un método dentro de la clase de manera dinámica y en tiempo de ejecución.
Otro de los métodos más populares utilizados para cambiar el flujo de un programa en tiempo de ejecución sería method_missing
es un método gancho (“hook method” o “callback”) llamado por Ruby cuando algún método no es encontrado durante el proceso de “búsqueda de método” (method lookup):
Nos puede surgir la pregunta de como este “manejo particular de una excepción” nos puede ayudar a construir programas dinámicos que cambien su ruta de ejecución, bueno para esto si debemos usar bastante nuestra creatividad e imaginación para poder obtener resultados como estos:
En este ejemplo vemos como con el uso del método method_missing
y la habilidad de pasar bloques de código (blocks, procs o lambdas) como parámetros, en pocas lineas de código podemos construir un hermoso DSL para manipular y generar código HTML.
Conclusión
Vimos el concepto de metaprogramación como técnica para el desarrollo de software, detallamos conceptos asociados a esta como lo son la Introspección y Reflexión y revisamos como un lenguaje de programación como Ruby nos entrega diversas herramientas para utilizar esta técnica de una manera amigable, pero como bien conocemos la famosa frase del universo de spider-man:
“Un gran poder conlleva una gran responsabilidad”
esta en manos de cada uno de nosotros el buen o mal uso (o abuso) que le demos a estas herramientas.
Referencias:
https://es.wikipedia.org/wiki/Metaprogramaci%C3%B3n
https://codigofacilito.com/articulos/que-es-metaprogramacion
https://es.wikipedia.org/wiki/Introspecci%C3%B3n
https://hackademy.io/tutoriel-videos/introduction-ruby-reflexion
https://es.slideshare.net/kim.mens/reflection-in-ruby
http://ruby-metaprogramming.rubylearning.com/html/ruby_metaprogramming_2.html
http://rubymonk.com/learning/books/2-metaprogramming-ruby/chapters/25-dynamic-methods/lessons/72-define-method