Generando Código: Hello World con AjGenesis


Mi proyecto preferido, AjGenesis, consiste en un generador de código, escrito en VB.NET. Es un proyecto de código abierto, con una licencia tipo BSD, que permite utilizarlo en cualquier proyecto que quieran. Se puede usar como librería, invocado desde nuestro proyecto, se puede invocar desde la línea de comando, o puede ser utilizado desde el poderoso NAnt (esto último me permitió organizar mejor las tareas que realiza el generador de código).


En la página del proyecto (y en el proyecto mismo) hay multitud de ejemplos, que generan código desde un modelo, usando plantillas. Los ejemplos generan código PHP, Java, JSP, VB.NET, C#, ASP.NET, y hasta scripts de base de datos y procedimientos almacenados. Quisiera destacar dos puntos:


- El modelo del que parte es totalmente definible por el usuario


- Las tareas y plantillas a aplicar son totalmente programables y controlables


Esto lo diferencia de otros generadores. Ud. puede hacerse su propio modelo, y sus propias plantillas, para generar los artefactos de texto que prefiera. Otros sistemas parten de la base de datos, y sólo generan un grupo de artefactos de textos predefinidos (por ejemplo, POJOs, o DAOs). Pero con AjGenesis puede generar lo que Ud. quiera.


Para ejemplificar, este artículo se dedica a modelar algo que, si bien no es lo último en tecnología, es la primera prueba que debe pasar un generador de código: modelar y generar un viejo pero entrañable “Hello, World” (programa que encontré por primera vez en el venerable “The C Programming Language” de los buenos de Kernighan y Ritchie).


Los archivos de este ejemplo, pueden bajarse de HelloWorld1.zip


Primero, el modelo. Como menciono en mis charlas, el modelo es una simplificación de la realidad. El modelo, por ejemplo, un mapa, no es la realidad, el territorio. En el caso de los modelos de AjGenesis, se trata de crear inicialmente, un modelo que muestre lo variable de nuestro sistema. En el caso de un HelloWorld, lo que quisierámos modelar como variable, es el mensaje. Entonces, creamos en un directorio de trabajo, el archivo Project.xml:


<Project>
<Message>Hello World, by AjGenesis</Message>
</Project>


Este es el primer paso. Vean que no tuvimos que usar un modelo predefinido, simplemente, lo escribimos en un documento XML bien formado. Nada de schemas, nada DTDs, o bichos raros. Solamente nuestra creatividad.


El segundo paso es elegir la tecnología (lenguaje, plataforma, framework, base de datos), que vamos a utilizar, y escribir (o reutilizar) plantillas. Decidimos crear un ejemplo en VB.NET, un simple programa de consola. La plantilla la escribimos en el archivo HelloVb.tpl



‘ Hello World demo
‘ generated by AjGenesis
http://www.ajlopez.com/ajgenesis


Module HelloWorld
 Public Sub Main()
  System.Console.WriteLine(“${Project.Message}”)
 End Sub
End Module


Acá aparece una característica de las plantillas: permiten acceder y utilizar en la salida, los datos del modelo. Al escribir Project.Message entre ${ y }, le indicaros al procesador AjGenesis, que cuando trabaje sobre esta plantilla, reemplace ese texto por el valor de la rama Project/Message en nuestro modelo. Vemos que no usamos una notación XML o XPath para referirnos a algo del modelo. El modelo inicial está serializado en XML, pero AjGenesis lo levanta a memoria, y nos lo deja como un objeto con propiedades.


Hubiéramos podido escribir el modelo como


<Project Message=”Hello World, by AjGenesis”/>


El modelo en memoria sería el mismo. Aunque ahora Message está como atributo, para nuestra plantilla sigue siendo la propiedad Message del objeto Project.


Ya tenemos el modelo, y la plantilla. Necesitamos bajar y expandir el AjGenesis-0.4.3.zip (versión en desarrollo, las anteriores versiones están en el directorio de fuentes SourceForge del proyecto). En esa versión, hay un directorio bin con librerías y algún ejecutable. El AjGenesis.Console.exe es el programa que puede invocarse para tomar el modelo y la plantilla, y generar un archivo, así:


AjGenesis.Console Project.xml HelloWorldVb.tpl HelloWorld.vb


Los parámetros se explican así:


Archivo .xml: cuando encuentra en la lista de parámetros un archivo XML, AjGenesis lo carga en su ambiente de variables. El nodo inicial del XML pasa a ser el nombre de la variable que contiene al modelo.


Archivo .tpl: lo toma como plantilla, y lo procesa, generando un archivo con el nombre del siguiente parámetro.


Por supuesto, para que esto funcione, o deberemos poner la ruta completa del ejecutable AjGenesis.Console, o deberemos incluir el directorio bin de AjGenesis en el PATH de nuestro ambiente.


Los parámetros los procesa en orden. Podríamos poner más de un modelo, más de un archivo XML. Por ejemplo, un segundo archivo XML podría describir la tecnología de base de datos a usar, o el nombre de la empresa que está usando el modelo, o cualquier otra cosa que se nos ocurra. Sería conveniente en este caso que el segundo archivo XML tenga un nodo raíz distinto de Project, porque así no sobreescribe al modelo del anterior archivo. También podríamos colocar varios archivos de plantilla con su correspondiente archivo resultado, en una misma invocación.


Si ejecutamos entonces el comando de arriba (necesitamos un framework .NET instalado, recordemos que AjGenesis está escrito en .NET), se genera el archivo HelloWorld.vb



‘ Hello World demo
‘ generated by AjGenesis
http://www.ajlopez.com/ajgenesis


Module HelloWorld
 Public Sub Main()
  System.Console.WriteLine(“Hello World, by AjGenesis”)
 End Sub
End Module


No vamos a decir “Uy que bruto..”, pero es el primer paso.


Pero vayamos más alla. Supongamos que ahora, con el mismo modelo, queremos generar nuestro gran sistema HelloWorld, en Java. Las presiones del mercado, nuestros clientes, nos piden la nueva versión en el lenguaje multiplataforma Java, y nosotros estamos acá para servirlos. Bien, manos a la obra, creamos un archivo HelloWorldJava.tpl con


/**
* Hello World demo
* generated by AjGenesis
* http://www.ajlopez.com/ajgenesis
*/


public class HelloWorld {
 public static void main(String [] args) {
  System.out.println(“${Project.Message}”);
 }
}


y ejecutamos en la línea de comando

AjGenesis.Console Project.xml HelloWorldJava.tpl HelloWorld.java

y voilá, se genera el archivo HelloWorld.java:


/**
* Hello World demo
* generated by AjGenesis
* http://www.ajlopez.com/ajgenesis
*/


public class HelloWorld {
 public static void main(String [] args) {
  System.out.println(“Hello World, by AjGenesis”);
 }
}


Ya estamos cebados. Vamos por un ejemplo más flexible. Quisiéramos ahora generar el programa VB.NET, el programa C#, el programa Java, y que el nombre de la clase, del programa, sea algo variable (en las plantillas anteriores aparecía siempre HelloWorld como nombre del archivo y de la clase o módulo).


Los archivos de este segundo ejemplo se pueden bajar de HelloWorld2.zip.


Para esto, en otro directorio, creamos el archivo Project.xml con el contenido


<Project>
 <Name>HelloWorld</Name>
 <Description>Hello World Demostration</Description>
 <Message>Hello World, by AjGenesis</Message>
</Project>


Y escribimos las plantillas ModuleVb.tpl



‘ ${Project.Description}
‘ generated by AjGenesis
http://www.ajlopez.com/ajgenesis


Module ${Project.Name}
 Public Sub Main()
  System.Console.WriteLine(“${Project.Message}”)
 End Sub
End Module


y ClassJava.tpl


/**
* ${Project.Description}
* generated by AjGenesis
* http://www.ajlopez.com/ajgenesis
*/


public class ${Project.Name} {
 public static void main(String [] args) {
  System.out.println(“${Project.Message}”);
 }
}


Estudiemos otra característica de AjGenesis: la capacidad de ejecutar un script, en un lenguaje denominado informalmente AjBasic. Creo que esta es una característica poderosa, porque posibilita ir más allá de simplemente la línea de comando. Nos permite manipular programáticamente el modelo y las tareas a realizar. En los ejemplos que encontrarán en la página del proyecto, se usa mucho esta característica, llegándose a producir soluciones completas, con decenas de archivos. Pero veamos nuestro ejemplo.


El programa lo ingresamos en el archivo Build.ajg


PrintLine “Generating ${Project.Name}”


TransformerManager.Transform(“ModuleVb.tpl”, “${Project.Name}.vb”, Environment)
TransformerManager.Transform(“ClassJava.tpl”,”${Project.Name}.java”, Environment)


El lenguaje AjBasic, tiene algún verbo predefinido, como el autodescriptivo PrintLine. Notemos que, como en otros lenguajes de scripting, tiene expansión de expresiones en los strings. Esto es, cuando en un texto entre doble comillas (una constante string), ubica una expresión entre ${ y }, la evalúa y la reemplaza por el valor resultante. Muchos de Uds. reconoceran aquí una conducta similar a otros lenguajes de scripting de Unix/Linux, o a PHP, o al Expression Language de JSP 2.x.


La variable TransformerManager es una de las pocas predefinidas. No he definido muchas, y Uds. pueden agregar variables al ambiente de AjGenesis. Pero TransformerManager sirve para invocar programáticamente lo que hacemos desde la línea de comando. Así, el primer parámetro de su método Transform el archivo plantilla, el segundo parámetro es el archivo a generar, y el tercero, la colección de variables definida (en el ejemplo usamos Environment, otra de las variables predefinidas, que justamente es el diccionario de variables que ahora están disponibles en la evaluación de este script).


Ejecutamos lo anterior con


AjGenesis.Console Project.xml Build.ajg


El programa de consola está preparado para ejecutar cada parámetro que tenga la extensión .ajg, interpretándolo como un texto AjBasic.


Pero podemos ir más allá. El año pasado (el proyecto AjGenesis ya lleva sus años en desarollo), incorporé soporte de NAnt. Esto lo hice escribiendo tareas (Tasks) de AjGenesis que pueden invocarse desde ese gran utilitario. Yo uso además, el utilitario NAnt-GUI para ejecutar los archivos .build desde una ventana gráfica.


Escribamos un archivo default.build con el contenido (revise y modifique la property ajgenesis.dir para que refleje el directorio donde ha dejado al AjGenesis):


<?xml version=”1.0″?>
<project name=”Example 1″ default=”build”>
<property name=”ajgenesis.dir” value=”c:/ajlopez/dev/AjGenesis-0.4.3″/>
<property name=”nanttasks.dir” value=”${ajgenesis.dir}/bin”/>


<target name=”clean” description=”cleans build directory”>
   <delete dir=”${build.dir}” verbose=”true” if=”${directory::exists(build.dir)}” />
</target>

<target name=”loadtasks” description=”loads AjGenesis tasks”>
   <loadtasks assembly=”${nanttasks.dir}/AjGenesis.NAnt.dll” />
</target>


<target name=”init” depends=”loadtasks” description=”init the AjGenesis model”>
   <loadmodel model=”Project.xml”/>
</target>


<target name=”build” depends=”init”>
   <executetask task=”Build.ajg”/>
</target>
</project>


Entonces, desde la línea de comando podemos invocar


nant


Si tenemos configurado a ese utilitario, procesa el target default que de build.xml. Yo uso el NAnt-Gui, que me presenta la pantalla



Espero que este artículo les sirva como inicio de explicación a algunas de las características de AjGenesis. Espero escribir un próximo artículo, con ejemplos de uso de AjBasic dentro de las mismas plantillas, uso más intensivo del NAnt, manipulación del modelo en memoria, y generación de aplicaciones completas, en distintas plataformas y tecnologías.


Si alguien le parece útil, les pido que lo difundan, por ejemplo, con artículo de cómo lo están usando, en su blog.


Gracias por todo, nos leemos!


Angel “Java” Lopez
http://www.ajlopez.com/


 

This entry was posted in 1389, 1390, 1391, 2643, 6145. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>