Uno de los temas a encarar cuando uno usa un generador de código, o de artefactos de texto en general, es determinar si lo generado se va a usar como “puntapié” inicial y luego modificar, o si cada vez se va a regenerar, para reflejar cualquier cambio en el modelo inicial (modelo abstracto, clases en UML, esquema de la base). Esta segunda opción permite usar la generación de código como si fuera un compilador de modelo: cambia el modelo, regeneramos el código.
Pero a veces, dentro del código generado, queremos poner algun fragmento nuestro, agregado luego de haberlo creado por primera vez. En la reunión de Agosto pasado:
Reunión de Generación de Sistemas con AjGenesis
organizada por el Microsoft User Group de Argentina se planteó esa necesidad. Bien, ni lento ni perezoso, agregué algo de código a AjGenesis. Gracias a los que lo probaron en el grupo de generación de código, ahora puedo escribir un breve “post” sobre su uso.
Lo pueden probar desde la versión 0.5:
a descargar desde CodePlex.
El ejemplo
En esa versión, en el directorio examples\HelloPreserve encontraremos un ejemplo de uso.
El ejemplo se construye desde la línea de comando ejecutando
make
que es un archivo .bat que contiene:
..\..\bin\AjGenesis.Console Model.xml Build.ajg
Esto carga un modelo tipo Hello World de Model.xml:
<Project Name="HelloWorld"> <Messages> <Message>Hello</Message> <Message>World</Message> <Message>Again</Message> </Messages> </Project>
(si es su primer contacto con un ejemplo de este tipo, leer:
Generando Código- Hello World con AjGenesis
Generando aplicaciones con AjGenesis
)
El make invoca a trabajar un pequeño programa AjBasic Build.ajg:
PrintLine "Generating HelloWorld" TransformerManager.Transform("ModuleVb.tpl", "HelloWorld.vb", Environment, "PRESERVE")
Lo nuevo es el tercer parámetro de Transformer.Transform. Recordemos: Transformer es un objeto ayudante, que está implantado en el “environment” de variables de AjGenesis, accesible desde nuestros programas escritos en AjBasic. Es el objeto que nos permite invocar al transformador de plantillas. Pero aparte de tener un método Transform con dos parámetros (el primero es el nombre de archivo de la plantilla a procesar, el segundo el nombre de archivo a generar en el proceso), ahora tiene una variante adicional: si pasamos un tercer parámetro, lo usa como texto a buscar para preservar código. Veamos su uso en la plantilla.
La plantilla del ejemplo es ModuleVb.tpl:
' ' Project ${Project.Name} ' Automatically generated by AjGenesis ' http://www.ajlopez.com/ajgenesis ' ' PRESERVE comment start ' PRESERVE comment end Module Module1 Sub Main() System.Console.WriteLine( _ <# n = 0 for each msg in Project.Messages if n then Print "& " end if n = n + 1 #> "${msg}" _ <# end for #> ) End Sub ' PRESERVE code start ' PRESERVE code end End Module
Todo lo que esté desde una línea que contenga la marca PRESERVE (el tercer parámetro del método Transform), hasta otra línea que contenga PRESERVE, es mantenido de generación a generación.
ATENCION: el algoritmo empleado “matchea” las líneas que coinciden, es decir, si en el template hay un linea
‘ PRESERVE comment start
ésa es la que busca en el código original para no pisarla. Los bloques se identifican por el CONTENIDO COMPLETO de la línea que inicia el bloque.
Si luego, en el código generado, agrega texto en esos bloques, al regenerarse, esos textos se respetan:
' ' Project HelloWorld ' Automatically generated by AjGenesis ' http://www.ajlopez.com/ajgenesis ' ' PRESERVE comment start ' This is my comment two ' PRESERVE comment end Module Module1 Sub Main() System.Console.WriteLine( _ "Hello" _ & "World" _ & "Again" _ ) End Sub ' PRESERVE code start Sub MyMethod() Console.WriteLine("My own inmortal method") End Sub ' PRESERVE code end End Module
Cuestiones
Analicemos algunos puntos. ¿Por qué ahora hay que especificar un parámetro? ¿Por qué no solamente tomar lo que está entre PRESERVE como código a preservar? Primero, por compatibilidad con las anteriores plantillas. Pero más importante: el programador decide cuál es la marca a usar para preservar código o texto. Recordemos que podemos usarlo tanto en generar Java, como cuando armamos VB.NET, CSharp, PHP o scripts de base de datos. Cada uno de nosotros podrá decidir cuál es la forma de distinguir el texto invariante.
Si quieren ver cómo está implementado, pueden ver el Utilities\FileMerger.vb del proyecto AjGenesis.GenericTransformer. Lo que hace, en resumen, es que al aplicar la transformación, genera un archivo temporario en disco (si la transformación se interrumpe por error, el archivo en disco nos sirve para darnos pistas de dónde estuvo el error, si hubiera sido procesado en memoria sería más difícil ubicar el problema), y luego se hace un “merge” con el archivo ya previamente generado. Se asocian las zonas que comienzan y terminan con la palabra marca (PRESERVE en el ejemplo anterior) y que coinciden en toda su línea inicial (recuerden la nota de atención de la sección anterior).
Espero que les sirva y les resulte útil. Cada vez estoy más convencido que podemos aplicar lo que conocemos de generación de sistemas, para ir construyendo software ayudado por el propio software. La generación de código es uno de las herramientas a emplear.
Nos leemos!
Angel “Java” Lopez
http://www.ajlopez.com/