You are currently browsing the tag archive for the ‘AjGenesis’ tag.

Una práctica fundamental en la Generacón Automática de Código es la Preservación del Código No Generado. En otras palabras que entre un ciclo de generación y el siguiente no perdamos el código que hayamos escrito manualmente.

Para esto AjGenesis prevee un mecanismo muy sencillo y seguro.

Este se basa en pequeñas modificaciones a los archivos .ajg y los Templates dónde exista código que se quiera preservar.

Para ejemplificar vamos a seguir con nuestro ejemplo del post anterior sobre AjGenesis, el ignoto SyP.

Comenzamos con el archivo BuildVbNet2.ajg. Como ya vimos en estos archivos se define cómo aplicar los Templates, precisamente con los comandos AjBasic TransformerManager.Transform. En los TransformerManager.Transform que generan archivos que contienen código que hayamos generado manualmente y no queremos perder, tendremos que agregar un parámetro en su invocación. Este parámetro es un string que define la palabra clave que el AjGenesis entenderá que delimita los bloques de código que se pretenden preservar, en el ejemplo este argumento será PRESERVE, pero podría ser cualquier otro String.

Nosotros vamos a suponer que en el SyP vamos a necesitar preservar código en todos sus archivos .vb

Tomo cómo ejemplo el primer TransformerManager.Transform que genera un archivo .vb que encuentro en BuildVbNet2.ajg:

TransformerManager.Transform(“Templates/VbNet2/HomeAspxVb.tpl”,”${Project.BuildDir}/Src/${Project.Name}.WebClient/Default.aspx.vb”,Environment)

y quedará:

TransformerManager.Transform(“Templates/VbNet2/HomeAspxVb.tpl”,”${Project.BuildDir}/Src/${Project.Name}.WebClient/Default.aspx.vb”,Environment,”PRESERVE”)

SI el parámetro existe y luego no se usa no es problema.

En cada archivo donde se quiera preservar código se debera definir en que lugar quedará alojado, esto se realiza ubicando dos Comentarios PRESERVE,

‘ PRESERVE

……….

‘ PRESERVE

todo el código contenido entre estos dos Comentarios se preservará a través de las sucesivas generaciones.

A continuación de la palabra clave se puede escibir lo que se necesite para hacer claro el código. En este ejemplo defino dos bloques de preservación de código, uno al comienzo y otro al final del archivo. Ambos bloques quedan definidos con los siguientes dos pares de Comentarios:

‘ PRESERVE 1

‘ PRESERVE

y

‘ PRESERVE 2

‘ PRESERVE

 El 1 y el 2 son capricosos, podría haber escrito cualquier otra cosa que los diferencie.

En el ejemplo, por una razón de claridad en el código, prefiero usarlo así.

‘ PRESERVE 1 comment start

‘ PRESERVE 1 comment end

y

‘ PRESERVE 2 comment start

‘ PRESERVE 2 comment end

Lo que trae problemas es no diferenciar los bloques, como en el siguiente ejemplo;

‘ PRESERVE

‘ PRESERVE

y

‘ PRESERVE

‘ PRESERVE

Ahora, si estás trabajando con un sólo bloque de código, no hay problema en que se use así:

‘ PRESERVE

‘ PRESERVE

pero tiene que ser un sólo bloque a preservar.

Para que esto quede establecido, tengo que agregar estas lineas en cada Template dónde quiera que se apliquen y este es todo el trabajo.

Es importante destacar que si se define un bloque PRESERVE en un archivo cualquiera, pero no se define en su correspondiente Template este bloque no se preservará y se perderá con sus Comentarios PRESERVE incluidos.

El código de ejemplo pueden descargarlo desde acá SyPP.zip, ahora a generar escribir y volvera regenerar sin caer en ataque de pánico.

Espero sus comentarios. :)

Para quien no tenga experiencia en desarrollar software con AjGenesis, recomiendo leer Introducción a NAnt, Cómo generar código con AjGenesis sirviéndonos de NAnt – Parte I y Cómo generar código con AjGenesis sirviéndonos de NAnt – Parte II.

El código utilizado en esta nota puede descargarse desde acá.

Partiendo de ejemplos similares que usé en las notas anteriores, comenzamos con Natalia Requejo, estudiante avanzada de ingeniería electrónica, y avezada desarrolladora de software, a generar código.

Al ir diseñando el modelo de datos nos encontramos con la necesidad de referenciar a una misma tabla, más de una vez, desde otra única tabla. Por ejemplo, supongamos una Entidad Cliente, que se relaciona con Contacto, pero por cada Cliente podría tener varios Contactos, pero especializados, uno para Pagos y otro para Recepción. Planteamos el Modelo con los siguientes dos archivos XML:

<?xml version=”1.0″ encoding=”ISO-8859-1″ standalone=”yes”?>
<Entity>
    <Name>Cliente</Name>
    <Description>Cliente</Description>
    <SetName>Clientes</SetName>
    <Descriptor>Cliente</Descriptor>
    <SetDescriptor>Clientes</SetDescriptor>
    <SqlTable>Cliente</SqlTable>
    <Properties>
        <Property>
            <Name>Id</Name>
            <Description>Id</Description>
            <Type>Id</Type>
            <SqlType>int</SqlType>
        </Property>
        <Property>
            <Name>RazonSocial</Name>
            <Description>Razón Social</Description>
            <Type>Text</Type>
            <SqlType>varchar(64)</SqlType>
        </Property>
        <Property>
            <Name>IDContactoPago</Name>
            <Description>Contacto de Pago</Description>
            <Type>IdRef</Type>
            <SqlType>int</SqlType>
            <Reference>Contacto</Reference>
        </Property>
        <Property>
            <Name>IDContactoRecepcion</Name>
            <Description>Contacto de Recepción</Description>
            <Type>IdRef</Type>
            <SqlType>int</SqlType>
            <Reference>Contacto</Reference>
        </Property>
    </Properties>
</Entity>

y

<?xml version=”1.0″ encoding=”ISO-8859-1″ standalone=”yes”?>
<Entity>
    <Name>Contacto</Name>
    <Description>Contacto</Description>
    <SetName>Contactos</SetName>
    <Descriptor>Contacto</Descriptor>
    <SetDescriptor>Contactos</SetDescriptor>
    <SqlTable>Contacto</SqlTable>
    <Properties>
        <Property>
            <Name>Id</Name>
            <Description>Id</Description>
            <Type>Id</Type>
            <SqlType>int</SqlType>
        </Property>
        <Property>
            <Name>Nombre</Name>
            <Description>Nombre</Description>
            <Type>Text</Type>
            <SqlType>varchar(64)</SqlType>
        </Property>
        <Property>
            <Name>Apellido</Name>
            <Description>Apellido</Description>
            <Type>Text</Type>
            <SqlType>varchar(64)</SqlType>
        </Property>
    </Properties>
</Entity>

Al generar vimos que el actual esquema no contempla este caso. Hay nombres que se repiten y artefactos que se sobreescriben…

El tema de la nota es justamente cómo, modificando unas pocas línes de sólo cuatro Templates y agregando un nodo a un archivo XML, pudimos resolver muy rápidamente la situación.

Como primera medida agregamos un Nodo nuevo al archivo XML Cliente:

<?xml version=”1.0″ encoding=”ISO-8859-1″ standalone=”yes”?>
<Entity>
    <Name>Cliente</Name>
    <Description>Cliente</Description>
    <SetName>Clientes</SetName>
    <Descriptor>Cliente</Descriptor>
    <SetDescriptor>Clientes</SetDescriptor>
    <SqlTable>Cliente</SqlTable>
    <Properties>
        <Property>
            <Name>Id</Name>
            <Description>Id</Description>
            <Type>Id</Type>
            <SqlType>int</SqlType>
        </Property>
        <Property>
            <Name>RazonSocial</Name>
            <Description>Razón Social</Description>
            <Type>Text</Type>
            <SqlType>varchar(64)</SqlType>
        </Property>
        <Property>
            <Name>IDContactoPago</Name>
            <Description>Contacto de Pago</Description>
            <Type>IdRef</Type>
            <SqlType>int</SqlType>
            <Reference>Contacto</Reference>
            <ReferenceClasification>Pago</ReferenceClasification>
         </Property>
        <Property>
            <Name>IDContactoRecepcion</Name>
            <Description>Contacto de Recepción</Description>
            <Type>IdRef</Type>
            <SqlType>int</SqlType>
            <Reference>Contacto</Reference>
            <ReferenceClasification>Recepcion</ReferenceClasification>
        </Property>
    </Properties>
</Entity>

Las modificaciones están resaltadas en verde con negrita. Se trata del Nodo ReferenceClasification, que voy a usar en los Templates, justamente como un clasificador de estas dos referencias, inicialmente iguales.

Vamos ahora a los Templates. Tomo cómo base los mismos del ejemplo Cómo generar código con AjGenesis sirviéndonos de NAnt – Parte II.

Las Plantillas a modificar son cuatro, EntityFormWebVb.tpl, EntityCodeWebVb.tpl, EntityFormCodeWebVb.tpl y EntityWebVb.tpl

En el caso de EntityFormWebVb.tpl:

<#
message    “Generating Form for Entity ${Entity.Name}…”
include “Templates/VbNet2/VbFunctions.tpl”
include “Templates/EntityFunctions.tpl”
EntitySqlProperties    = SqlProperties(Entity)
EntityNoIdSqlProperties    = SqlNoIdProperties(Entity)
EntityIdProperty = IdProperty(Entity)
#>
<%@ Page Language=”vb” MasterPageFile=”~/MasterPages/MainMasterPage.master” AutoEventWireup=”false” CodeFile=”${Entity.Name}Update.aspx.vb” Inherits=”${WebPage.Prefix}${Entity.Name}UpdatePage” Title=”${Entity.Descriptor}”%>
<asp:Content ID=”Content1″ ContentPlaceHolderID=”MainContentPlaceHolder” Runat=”Server”>
            <br />
            <div class=”Normal”>
                <a href=”${Entity.SetName}.aspx” mce_href=”${Entity.SetName}.aspx”>${Entity.SetDescriptor}</a>
<%
    if IdEntity>0 then
%>
                <a href=”${Entity.Name}.aspx?Id=<%=IdEntity%>” mce_href=”${Entity.Name}.aspx?Id=<%=IdEntity%>”>${Entity.Descriptor}</a>      
<%
    end if
%>
            </div>
            <asp:Label id=”lblMensaje” runat=”server” CssClass=”Error” Visible=”False”></asp:Label>
            <asp:ValidationSummary id=”ValidationSummary1″ runat=”server” CssClass=”Error” DisplayMode=”List”></asp:ValidationSummary>
            <br />
            <table class=”DataTable” id=”tblDatos” cellspacing=”1″ cellpadding=”1″ width=”80%” border=”1″>
<#
for each Property in Entity.Properties where Property.Type <> “Id”
    if Property.Reference then
        RefDescriptorProperty = Property.Reference.DescriptorProperty
        RefIdProperty = IdProperty(Property.Reference)
#>
                <tr>
                    <td>
                        ${Property.Description}</td>
                    <td>
                        <asp:DropDownList id=”ddl${Property.Reference.SetName}${Property.ReferenceClasification}” runat=”server” DataTextField=”${RefDescriptorProperty.Name}” DataValueField=”${RefIdProperty.Name}” DataSource=”<%# ${Property.Reference.SetName}${Property.ReferenceClasification} %>”>
                        </asp:DropDownList>
                    </td>
                </tr>
<#
    else
        if Property.Enumeration then
#>
                <tr>
                    <td>
                        ${Property.Description}</td>
                    <td>
                        <asp:DropDownList id=”ddl${Property.Name}” runat=”server” DataTextField=”Description” DataValueField=”Id” DataSource=”<%# ${Property.Enumeration.Name}List %>”>
                        </asp:DropDownList>
                    </td>
                </tr>
<#
        else
#>          
                <tr>
                    <td>
                        ${Property.Description}</td>
                    <td>
                        <asp:TextBox id=”txt${Property.Name}” runat=”server” Text=”<%# Entity.${Property.Name} %>”>
                        </asp:TextBox></td>
                </tr>
<#              
        end if
    end if
end for
#>
            </table>
            <asp:Button id=”btnAccept” runat=”server” Text=”Accept”></asp:Button>
            <br />          
</asp:Content>

Nuevamente las modificaciones se encuentran en verde con negrita.

Verán que la modificación es simplemente insertar una variable que contenga el valor del nuevo Nodo ReferenceClasification del archivo XML, de esa forma logramos diferenciar las dos referencias.

Veamos que se modifica en los otros tres Templates:

EntityCodeWebVb.tpl

<#
include “Templates/VbNet2/Prologue.tpl”
#>
Imports ${Project.Name}.Services
Imports ${Project.Name}.Entities
Public Partial Class ${WebPage.Prefix}${Entity.Name}Page
    Inherits System.Web.UI.Page
    Public Entity As ${Entity.Name}
    Public Property IdEntity() As Integer
        Get
            Return DirectCast(ViewState(“IdEntity”), Integer)
        End Get
        Set(ByVal Value As Integer)
            ViewState(“IdEntity”) = Value
        End Set
    End Property
<#
    for each Property in Entity.Properties where Property.Reference
#>
    Public Entity${Property.Reference.Name}${Property.ReferenceClasification} as ${Property.Reference.Name}
<#
    end for
#>
<#
    for each Property in Entity.Properties where Property.Enumeration
#>
    Public ReadOnly Property ${Property.Name}Description as String
        Get
            return Enumerations.Translate(Enumerations.${Property.Enumeration.Name}List, Entity.${Property.Name})
        End Get
    End Property
<#
    end for
#>
    Protected Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Me.Load
        ‘Put user code to initialize the page here
        If Not IsPostBack Then
            IdEntity = CInt(Request(“Id”))
            Entity = ${Entity.Name}Service.GetById(IdEntity)
<#
    for each Property in Entity.Properties where Property.Reference
#>
            Entity${Property.Reference.Name}${Property.ReferenceClasification} = ${Property.Reference.Name}Service.GetById(Entity.${Property.Name})
<#
    end for
    for each Relation in Entity.Relations where Relation.RelationType=”Referenced”
#>
            ‘gvwData${Relation.Entity.SetName}.DataSource = ${Relation.Entity.Name}Service.GetBy${Entity.Name}Ex(IdEntity)
<#
    end for
#>
            DataBind()
        End If
    End Sub
End Class

Como en el Template anterior, con insertar la misma variable en dos líneas alcanza.

EntityFormCodeWebVb.tpl

<#
message    “Generating Form Code Behind for Entity ${Entity.Name}…”
include “Templates/VbNet2/VbFunctions.tpl”
include “Templates/EntityFunctions.tpl”
EntitySqlProperties    = SqlProperties(Entity)
EntityNoIdSqlProperties    = SqlNoIdProperties(Entity)
EntityIdProperty = IdProperty(Entity)
include “Templates/VbNet2/Prologue.tpl”
#>
Imports System
Imports System.Data
Imports ${Project.Name}.Services
Imports ${Project.Name}.Entities
Public Partial Class ${WebPage.Prefix}${Entity.Name}UpdatePage
    Inherits System.Web.UI.Page
    Public Entity As ${Entity.Name}
    Public Property IdEntity() As Integer
        Get
            Return DirectCast(ViewState(“IdEntity”), Integer)
        End Get
        Set(ByVal Value As Integer)
            ViewState(“IdEntity”) = Value
        End Set
    End Property
<#
    for each Property in Entity.Properties where Property.Reference
#>
    Public ${Property.Reference.SetName}${Property.ReferenceClasification} as DataView
<#
    end for
#>
<#
    for each Property in Entity.Properties where Property.Enumeration
#>
    Public ReadOnly Property ${Property.Enumeration.Name}List as IList
        Get
            return Enumerations.${Property.Enumeration.Name}List
        End Get
    End Property
<#
    end for
#>
    Protected Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Me.Load
        ‘Put user code to initialize the page here
        If Not IsPostBack Then
            If Request(“Id”) Is Nothing then
                IdEntity = 0
                Entity = new ${Entity.Name}
            else
                IdEntity = CInt(Request(“Id”))
                Entity = ${Entity.Name}Service.GetById(IdEntity)
            End If
<#
    for each Property in Entity.Properties where Property.Reference
#>
            ${Property.Reference.SetName}${Property.ReferenceClasification} = Get${Property.Reference.SetName}${Property.ReferenceClasification}
<#
    end for
#>
                DataBind()
            if IdEntity>0 then
<#
    for each Property in Entity.Properties where Property.Reference
#>
                ddl${Property.Reference.SetName}${Property.ReferenceClasification}.SelectedValue = Entity.${Property.Name}
<#
    end for
#>
<#
    for each Property in Entity.Properties where Property.Enumeration
#>
                ddl${Property.Reference.SetName}${Property.ReferenceClasification}.SelectedValue = Entity.${Property.Name}
<#
    end for
#>
            else
<#
    for each Property in Entity.Properties where Property.Reference
#>
                if not Request(“${Property.Name}”) is nothing then
                    ddl${Property.Reference.SetName}${Property.ReferenceClasification}.SelectedValue = CInt(Request(“${Property.Name}”))
                end if
<#
    end for
#>
            end if
        End If
    End Sub
    Private Function FormValidate() As Boolean
        Return True
    End Function
<#
    for each Property in Entity.Properties where Property.Reference
#>
    Private Function Get${Property.Reference.SetName}${Property.ReferenceClasification}() As IList
        Dim ds As DataSet
        ds = ${Property.Reference.Name}Service.GetList
        Dim dr As DataRow
        dr = ds.Tables(0).NewRow
<#
        if not Property.Required then
#>
        dr(“Id”) = 0
        dr(“${Property.Reference.DescriptorProperty.Name}”) = “”
        ds.Tables(0).Rows.Add(dr)
<#
        end if
#>
        Dim dw As New DataView(ds.Tables(0))
        dw.Sort = “${Property.Reference.DescriptorProperty.Name}”
        Return dw
    End Function
<#
    end for
#>
    Private Sub Update()
        if IdEntity>0 then
            Entity = ${Entity.Name}Service.GetById(IdEntity)
        else
            Entity = New ${Entity.Name}()
        end if
<#
for each Property in EntityNoIdSqlProperties
    if Property.Reference then
#>
        Entity.${Property.Name} = ddl${Property.Reference.SetName}${Property.ReferenceClasification}.SelectedValue
<#
    else
        if Property.Enumeration then
#>
        Entity.${Property.Name} = ddl${Property.Reference.SetName}${Property.ReferenceClasification}.SelectedValue
<#
        else
#>      
        Entity.${Property.Name} = txt${Property.Name}.Text
<#
        end if
    end if
end for
#>      
        If IdEntity = 0 Then
            ${Entity.Name}Service.Insert(Entity)
        Else
            ${Entity.Name}Service.Update(Entity)
        End If
    End Sub
    Private Sub btnAccept_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAccept.Click
        If Not IsValid Then
            Return
        End If
        Try
            If FormValidate() Then
                Update()
                If IdEntity = 0 Then
                    Server.Transfer(“${Entity.SetName}.aspx”)
                Else
                    Server.Transfer(“${Entity.Name}.aspx?Id=” & IdEntity)
                End If
            End If
        Catch Ex As Exception
            lblMensaje.Visible = True
            lblMensaje.Text = Ex.Message
        End Try
    End Sub
End Class

Acá tuvimos que modificar más líneas, pero en todos los casos se trata de insertar el valor del Nodo ReferenceClasification

EntityWebVb.tpl

<#
message    “Generating Page for Entity ${Entity.Name}…”
include “Templates/VbNet2/VbFunctions.tpl”
include “Templates/EntityFunctions.tpl”
EntitySqlProperties    = SqlProperties(Entity)
EntityNoIdSqlProperties    = SqlNoIdProperties(Entity)
EntityIdProperty = IdProperty(Entity)
#>
<%@ Page Language=”VB” MasterPageFile=”~/MasterPages/MainMasterPage.master” AutoEventWireup=”false” CodeFile=”${Entity.Name}.aspx.vb” Inherits=”${WebPage.Prefix}${Entity.Name}Page” Title=”${Entity.Descriptor}”%>
<%@ Register src=”~/Controls/Subtitle.ascx” mce_src=”~/Controls/Subtitle.ascx” TagName=”Subtitle” TagPrefix=”uc1″ %>
<asp:Content ID=”Content1″ ContentPlaceHolderID=”MainContentPlaceHolder” Runat=”Server”>
            <br />
            <div class=”Normal”>
                <a href=”${Entity.SetName}.aspx” mce_href=”${Entity.SetName}.aspx”>${Entity.SetDescriptor}</a>
                <a href=”${Entity.Name}Update.aspx?Id=<%=IdEntity%>” mce_href=”${Entity.Name}Update.aspx?Id=<%=IdEntity%>”>Update</a>
                <a href=”${Entity.Name}Delete.aspx?Id=<%=IdEntity%>” mce_href=”${Entity.Name}Delete.aspx?Id=<%=IdEntity%>”>Delete</a>
            </div>
            <br />
            <table class=”DataTable” id=”tblDatos” cellspacing=”1″ cellpadding=”1″ width=”80%” border=”1″>
<#          
for each Property in Entity.Properties
    if Property.Reference then
        RefDescriptorProperty = Property.Reference.DescriptorProperty
        RefIdProperty = IdProperty(Property.Reference)
#>
                <tr>
                    <td>
                        ${Property.Description}</td>
                    <td>
                        <asp:HyperLink id=”lnk${Property.Name}” runat=”server” Text=”<%# Entity${Property.Reference.Name}${Property.ReferenceClasification}.${RefDescriptorProperty.Name} %>” NavigateUrl=’<%# “${Property.Reference.Name}.aspx?Id=” & Entity${Property.Reference.Name}${Property.ReferenceClasification}.Id %>’>
                        </asp:HyperLink>
                    </td>
                </tr>
<#
    else
        if Property.Enumeration then
#>
                <tr>
                    <td>
                        ${Property.Description}</td>
                    <td>
                        <asp:Label id=”lbl${Property.Name}” runat=”server” Text=”<%# ${Property.Name}Description %>”>
                        </asp:Label></td>
                </tr>
<#
        else
#>
                <tr>
                    <td>
                        ${Property.Description}</td>
                    <td>
                        <asp:Label id=”lbl${Property.Name}” runat=”server” Text=”<%# Entity.${Property.Name} %>”>
                        </asp:Label></td>
                </tr>
<#
        end if
    end if      
end for
#>
            </table>
</asp:Content>

En este último Template hay que insertar esta variable en una única línea.

Estas son todas las modificaciones que hay que realizar para extender el generador de forma tal que resulva estas relaciones. Con esto queda demostrado que AjGenesis presenta un modelo de generación de código fácilmente extendible.

Para quien se inicia en el mundo de estos Templates, una forma de sencilla de entenderlos, es leer paralelamente el Template y el Artefacto que genera, de esa forma se facilita la comprensión de este lenguaje de scripts que nos propone Ángel “Java” López.

Cuando tuve esto funcionando le mostré a Ángel la idea y me respondió:

……Podrias poner, en algun momento, para completar el modelo, algo como:    if Property.Reference and not Property.ReferenceClassification and Property.Name.BeginsWith(“Id”) then
        Property.ReferenceClassification = Property.Name.Substring(….)
    end if……

 

 

Para el que guste, se puede seguir por allí.

Ángel suele hacer hincapié en esta interesante idea de Completar el Modelo

Si desean pueden bajarse el código de ejemplo desde acá, hacer pruebas y espero quieran dejar sus comentarios. :)

Sigo en la misma linea de la nota anterior.

Ahora la idea es generar una aplicación web completa en VB.NET para Visual Studio 2005, trabajando con SQL Server 2005.

Esta vez subo un conjunto de archivos somo ejemplo para que quien quiera haga sus pruebas.

Los archivos pueden bajarse desde este link SyP.zip 

Pero para esto recomiendo tener funcionando el HelloWorld del ejemplo anterior, de esta forma ya tenemos garantizado el funcionamiento de AjGenesis con NAnt correctamente.

AjGenesis, el generador de código de Ángel “Java” López, permite, entre otras cosas, definir el lenguaje en el que se quiere trabajar, Ángel tiene ejemplos publicados en VB.NET, C#, PHP, Java, en cuanto a bases de datos hay para MySQL, SQL Server 2000, y SQL Server 2005, cualquiera que quiera extender este modelo sólo debe escribir el Template y agregarlo en la Tarea que corresponda.

Como si fuera poco Ángel publica ejemplos con Modelos en Domain Driven Design, usando NHibernate, por separado y combinados también.

En esta versión de AjGenesis, Ángel, agregó un archivo .xml de configuración, en nuestro caso de nombre VbNet2.xml, en el que se define el Dialecto de Programación, el Dialecto de Base de Datos y valores de la conexión a la misma.

Los Dialectos definen el juego de Templates que se van a utilizar, en nuestro ejemplo serán los correspondientes a VB.NET para Visual Studio 2005 y SQL Server 2005.

Los Templates o Plantillas son archivos con extensión .tpl y las Tareas o Tasks son archivos .ajg, ambos archivos de texto.

Los Templates son la base del código que se autogenerará a partir de AjGenesis, los Templates “leen” el Modelo, la estructura de datos, de un conjunto de archivos .xml.

En estos archivos .xml se definen los nombres de las Entidades y campos, los tipos, longitudes y las relaciones entre Entidades.

Existe un archivo .xml que define al conjunto de las Entidades que componen al Modelo, en nuestro ejemplo será Project.xml, luego por cada Entidad tendremos un archivo .xml.

Como escribí antes, los Templates “leen” el Modelo de estos archivos .xml, los Templates se aplican al ejecutar las Tareas.

Por último para ejecutar las Tareas necesitamos NAnt, en un archivo .build se orquestan las Tareas y por línea de comando se ejecuta el archivo NAnt.El ejemplo que voy a desarrollar, como escribí antes, se continúa con el HelloWorld de la nota anterior, es decir, voy a seguir usando la misma estructura de directorios, a quién le agrade podrá usar la misma, sino el modelo permite cualquier otra, pero hay que tener particular cuidado de corregir todas las referencias necesarias.

El ejemplo que voy a tomar es el la aplicación AjSaaS que Ángel “Java” López publica en AjGenesisExamples3.

Pueden bajarse el conjunto de archivos desde acá: SyP.zip, en mi ejemplo yo lo ubico en la carpeta SyP, debajo de la misma veremos el siguiente árbol:

Carpeta Raiz

En la carpeta SyP, nuestra raiz, encontramos el archivo NAnt, en este caso, AjSaaSVbNet2.build.

En Libraries encontraremos las dlls que necesitaremos, este ejemplo usa AjFramework para acceder a datos, entonces encontraremos una carpeta AjFramework y dentro de ella AjFramework.Core.dll y AjFramework.Data.dll. Para quien quiera conocer el AjFramework, otro desarrollo de Ángel “Java” López lo puede encontrar acá, AjFramework, con código fuente disponible.

En Projects se almacenan los Modelos, un Modelo por carpeta, nosotros vamos a ver una carpeta con nombre AjSaaS, que contiene Project.xml, en el que se definen las Entidades intervinientes, veamos: 

<?xml version=”1.0″ encoding=”ISO-8859-1″ standalone=”yes”?>

<Project>

            <Name>AjSaaS</Name>

            <Description>A sample Software as a Service application</Description>

            <Company>ajlopez.com</Company>

            <CompanyName>ajlopez</CompanyName>

            <SystemName>AjSaaS</SystemName>

            <Language>En</Language>

            <Prefix>AjSaaS</Prefix>

            <OpenSource>true</OpenSource>

            <Model>

                        <Entities>

                                    <Entity Source=”Entities/Tenant.xml”/>

                                    <Entity Source=”Entities/Provider.xml”/>

                                    <Entity Source=”Entities/User.xml”/>

                                    <Entity Source=”Entities/Application.xml”/>

                                    <Entity Source=”Entities/Role.xml”/>

                                    <Entity Source=”Entities/UserRole.xml”/>

                                    <Entity Source=”Entities/TenantApplication.xml”/>

                                    <Entity Source=”Entities/Entity.xml”/>

                                    <Entity Source=”Entities/EntityProperty.xml”/>

                                    <Entity Source=”Entities/EntityApplication.xml”/>

                                    <Entity Source=”Entities/Contact.xml”/>

                                    <Entity Source=”Entities/ToDoItem.xml”/>

                        </Entities>

             </Model>

</Project>

 

 

En la carpeta Projects también encontramos otras dos carpetas más, Entities y Technologies. En Entities tenemos un archivo .xml por cada Entidad que compone al Modelo, por ejemplo Role.xml: 

 

<?xml version=”1.0″ encoding=”ISO-8859-1″ standalone=”yes”?>

<Entity>

            <Name>Role</Name>

            <Description>Role</Description>

            <SetName>Roles</SetName>

            <Descriptor>Role</Descriptor>

            <SetDescriptor>Roles</SetDescriptor>

            <SqlTable>roles</SqlTable>

            <Properties>

                        <Property>

                                    <Name>Id</Name>

                                    <Description>Id</Description>

                                    <Type>Id</Type>

                                    <SqlType>int</SqlType>

                        </Property>

                        <Property>

                                    <Name>RoleName</Name>

                                    <Description>Role Name</Description>

                                    <Type>Text</Type>

                                    <SqlType>varchar(40)</SqlType>

                                    <Required>true</Required>

                        </Property>

                        <Property>

                                    <Name>Description</Name>

                                    <Description>Description</Description>

                                    <Type>Text</Type>

                                    <SqlType>varchar(200)</SqlType>

                                    <Required>true</Required>

                        </Property>

                        <Property>

                                    <Name>IdApplication</Name>

                                    <Description>Application</Description>

                                    <Type>IdRef</Type>

                                    <SqlType>int</SqlType>

                                    <Reference>Application</Reference>

                        </Property>

            </Properties>

</Entity>

 

 

 

Los primeros nodos son respectivamente para definir Nombre, Descripción, Nombre de Conjunto, Descriptor, Descriptor de Conjunto, y nombre de tabla de base de datos, esto es definir para una determinada Entidad cómo se la conocerá y describirá en el código que se autogenere acerca de ella.

Después está Properties, una colección de nodos Property, que definen los campos de la tabla de base de datos y las propiedades de la Entidad relacionada, todas las Property tienen nodos Name, Description, Type y SqlType.

Type puede valer:

Id: Clave Primaria de la tabla de base de datos.

Int: Cualquier entero.

Date: para fechas.

Decimal: para decimales.

Text: Cualquier tipo de cadena.

IdRef: para definir relaciones con otras Entidades y Foreign Keys en las relaciones de tablas de bases de datos.

 

Si Type vale IdRef, entonces hay que agregar el nodo Reference, para referenciar a la tabla con que se quiere vincular.

El nodo SqlType define el tipo SQL Server.

Con esto tenemos bien entendido el Modelo, ahora vamos a la carpeta Technologies, en ella encontraremos el archivo VbNet2.xml: 

<Technology>

            <Programming>

                        <Dialect>VbNet2</Dialect>

            </Programming>

            <Database>

                        <Dialect>MsSql2005</Dialect>

                        <Name>AjSaaS</Name>

                        <Username>sa</Username>

                        <Prefix>AjSaaS_</Prefix>

                        <Trusted_Connection>yes</Trusted_Connection>

                        <Host>SPICA\SQLEXPRESS</Host>

            </Database>

</Technology> 

 

 

Podemos leer claramente cómo se definen los Dialectos y los valores de conexión a base de datos, como escribí más arriba.

Ya estamos en condiciones de ver el archivo NAnt, AjSaaSVbNet2.build, acá su contenido: 

 

<?xml version=”1.0″?>

 <project name=”AjSaaS VB.NET 2.0 Web Site” default=”04-Build”>

     <property name=”project.name” value=”AjSaaS”/>

     <property name=”technology” value=”VbNet2″/>

     <property name=”src.dir” value=”.”/>

     <property name=”templates.dir” value=”${src.dir}/Templates”/>

     <property name=”tasks.dir” value=”${src.dir}/Tasks”/>

     <property name=”ajgenesis.dir” value=”C:.5″/>

     <property name=”nanttasks.dir” value=”${ajgenesis.dir}/bin”/>

     <property name=”website.dir” value=”${src.dir}/Build/${project.name}/${technology}/${project.name}/src/${project.name}.WebClient”/>

     <property name=”project.dir” value=”${src.dir}/Projects/${project.name}”/>

     <property name=”lib.dir” value=”${src.dir}/Libraries”/>

     <property name=”build.dir” value=”${src.dir}/Build/${project.name}/${technology}”/>

 

     <target name=”00-Clean” description=”Cleans Build Directory”>

         <delete dir=”${build.dir}” if=”${directory::exists(build.dir)}” />

     </target>

     <target name=”01-LoadTasks” description=”Loads AjGenesis Tasks” >

         <loadtasks assembly=”${nanttasks.dir}/AjGenesis.NAnt.dll” />

     </target>

     <target name=”02-Init” depends=”01-LoadTasks” description=”Init the AjGenesis model, Create build directory”>

         <mkdir dir=”${build.dir}”/>

         <loadmodel model=”${project.dir}/Project.xml”/>

         <loadmodel model=”${project.dir}/Technologies/${technology}.xml”/>

        <executetask task=”${tasks.dir}/BuildProject2.ajg”/>

        <executetask task=”${tasks.dir}/BuildTechnology.ajg”/>

    </target>

    <target name=”03-BuildSQL” depends=”02-Init”>

        <executetask task=”${tasks.dir}/BuildSql.ajg”/>

    </target>

    <target name=”04-Build” depends=”02-Init”>

        <executetask task=”${tasks.dir}/BuildProg.ajg”/>

        <copy todir=”${build.dir}/Src/Libraries”>

            <fileset basedir=”${lib.dir}/AjFramework”>

                <include name=”**/*”/>

            </fileset>

        </copy>

    </target>

    <target name=”05-DeploySQL” depends=”03-BuildSQL”>

        <exec program=”${build.dir}/Sql/CreateDatabase.bat”/>

    </target>

</project

 

 

 

Las primeras líneas del archivo ya están explicadas en la nota anterior, veremos algunas pequeñas diferencias como la definición de la carpeta de Technologies.

En la Target 02-Init podemos ver cómo se carga el Modelo y las Technologies a utilizar, luego se ejecutan las Tasks BuildProject2.ajg y BuildTechnologies.ajg.

Las Targets que más nos interesan son:

 

 

03-BuildSql, crea un script que creará todas las tablas, stored procedures, claves primarias y foráneas y se establecen las relaciones

04-Build, genera la solución con sus proyectos.

05-DeploySql, ejecuta 03-BuildSql y a continuación crea un archivo de lotes de nombre CreateDatabase.bat que ejecuta el script creado en la Targer 03-BuildSql y lo ejecuta. De esta forma se crea la base de datos automáticamente.

Las Tareas se ejecutan de la siguiente forma, abrimos una ventana de línea de comando y nos ubicamos en la carpeta que contiene al archivo NAnt AjSaaSVbNet2.build.

Si tipeamos NAnt y <Enter> se ejecutará la Target por default que es 04-Build, como se define en la segunda linea del archivo: 

 

 

<project name=”AjSaaS VB.NET 2.0 Web Site” default=”04-Build”> 

 

 

Si quisiéramos ejecutar una Target que no sea la definida como default, por ejemplo, 03-BuildSql, tipeamos lo siguiente: 

 

 

NAnt 03-BuildSql 

En nuestro caso ejecutando las Targets 04-Build y 05-DeploySql estaría todo hecho.

Para ejecutar dos Tareas con NAnt tipeamos lo siguiente: 

 

 

NAnt 04-Build 05-DeploySql 

La salida de esto es una larga lista de mensajes que al final incluye el feliz BUILD SUCCEEDED, sino algo falló. Hay que revisar las definiciones y reintentarlo. NAnt tiene que haber creado unas carpetas debajo de SyP con la siguiente estructura: 

Carpeta Build

 

En SyP\Build\AjSaaS\VbNet2\Sql están CreateDatabase.bat y Database.sql, que son respectivamente el encargado de ejecutar el script contra la base de datos y el script en si mismo.

En SyP\Build\AjSaaS\VbNet2\src está el archivo de la solución con todo lo que lo compone.

La solución se genera de tal modo que haciendo un doble click sobre el archivo .sln, para abrirlo con Visual Studio 2005 y con un simple F5 en ese ambiente, ya ejecuta sin ningún error ni ajuste adicional.

Como dice Ángel “Java” Lópezesto es ver la luz

Ahora sólo tienen que defiir un Modelo propio, crear su propio Project.xml y los archivos .xml de las Entidades y ya están en condiciones de generar una nueva aplicación.

Bienvenidos. )

Ya les conté someramente qué es NAnt, ahora…¿qué es AjGenesis?…AjGenesis es un generador de artefactos de texto de código abierto.  Para más información dirigirse a http://www.ajlopez.com/ajgenesis

Puede bajarse el generador, desde AjGenesis-0.5, ejemplos desde AjGenesisExamples.zip, más ejemplos en AjGenesisExamples3.zip, documentación en http://www.ajlopez.net/ajgenesis/documentation.php

AjGenesis se basa en un modelo soportado en XML y plantillas.Para utilizar este generador sólo hay que descargarlo y descomprimirlo en una carpeta.

AjGenesis funciona con el Framework 1.1 en adelante, nosotros vamos a necesitar la versión 2.0 que es el que corresponde a Visual Studio 2005. Tambien hay que tener instalado NAnt 0.85La idea es, mediante NAnt, ejecutar Tareas de AjGenesis en un orden que puede definirse. En algunos casos serán comandos NAnt (por ejemplo, crear o eliminar carpetas, archivos, etc.) y en otros archivos de lotes .ajg donde se define, por medio de los templates, que hacer con el modelo (los archivos XML), en sintaxis AjBasic, por ejemplo: 

TransformerManager.Transform(“Templates/VbNet2/EntityVb.tpl”,”${Project.BuildDir}/Src/${Project.Name}.Entities/${Entity.Name}.vb”,Environment) 

En esta linea se está aplicando la plantilla EntityVb.tpl a la entidad que se soportará en un archivo cuya ubicación y nombre resulte de  

 ${Project.BuildDir}/Src/${Project.Name}.Entities/${Entity.Name}.vb 

dónde;

${Project.BuildDir} es la carpeta en la que se guarda el código autogenerado, se define en NAnt, en el archivo .build,  

${Project.Name} se define en el archivo Project.xml, es el archivo raiz del modelo de datos y

${Entity.Name} se define en el archivo XML del mismo nombre, descriptor de la entidad. 

Una vez copiados NAnt y AjGenesis como se describe anteriormente, para asegurarse que todo funciona correctamente es conveniente ejecutar un ejemplo de los que Ángel Lopez nos deja.

Obviamente ejecutaremos alguna variante de Hello World

Para verificar la instalación de NAnt abramos una ventana de línea de comandos y tipeamos NAnt y Enter, como no va a encontrar ningún archivo .build mostrará lo siguiente: 

NAnt 0.85 (Build 0.85.2478.0; release; 14/10/2006)

Copyright (C) 2001-2006 Gerry Shaw

http://nant.sourceforge.net  

BUILD FAILED 

Could not find a ‘*.build’ file in ‘C:\…….’ 

For more information regarding the cause of the build failure, run the build again in debug mode. 

Try ‘nant -help’ for more information 

Ahora verifiquemos que funcione AjGenesis.

Supongamos que ya bajamos el archivo AjGenesis-0.5.zip, lo descomprimimos y lo copiamos en C:\Lab\AjLopez\AjGenesis-0.5, también descomprimimos los archivos AjGenesisExamples.zip y AjGenesisExamples3.zip dentro de C:\Lab\AjLopez\AjGenesis-0.5\examples, los nombres de las carpetas que doy son arbitrarios, si los querés modificar sólo tendrías que tener en cuenta de modificar los valores en el archivo .build de NAnt, en las tareas .ajg y en los templates .tpl

Busquemos el ejemplo HelloWorldNAnt, en su carpeta encontraremos un archivo de nombre default.build, este es el archivo NAnt con el siguiente contenido: 

<?xml version=”1.0″?><project name=”Example 1″ default=”build”>

    <property name=”src.dir” value=”.”/>

    <property name=”build.dir” value=”${src.dir}/Build”/>

    <property name=”templates.dir” value=”${src.dir}/Templates”/>

    <property name=”models.dir” value=”${src.dir}/Models”/>

    <property name=”tasks.dir” value=”${src.dir}/Tasks”/>

    <property name=”ajgenesis.dir” value=”${src.dir}/../..”/>

    <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, create build directory”>

            <mkdir dir=”${build.dir}”/>

            <loadmodel model=”${models.dir}/Project.xml”/>

      </target>

     <target name=”build” depends=”init”>

            <executetask task=”${tasks.dir}/Build.ajg”/>

    </target>

</project> 

Vemos un archivo XML, en los primeros tags se definen variables que contienen la rutas de algunas carpetas que se irán utilizando más adelante:

src.dir es la carpeta raiz,

build.dir es dónde se aloja el código autogenerado,

templates.dir es dónde el generador encuentra sus plantillas,

models.dir contiene los archivos XML que definen el modelo,

tasks.dir acá encontramos los archivos .ajg que definen las tareas,

ajgenesis.dir es la carpeta dónde se encuentra el generador de código, revisar bien esta definición,

nanttask.dir contiene las dll’s de AjGenesis que trabajan con NAnt.

A continuación hay cuatro nodos target, cada uno representa una tarea en NAnt, básicamente tiene un atributo name, con el nombre, que es obligatorio, description es opcional y depends, que lo encontramos en el último tag target, indica que esta tarea no se ejecutará hasta que se finalice init.

Volvamos a la ventana de linea de comandos que ya teníamos abierta, ubiquémonos en la carpeta que contiene el archivo default.build del ejemplo HelloWorldNAnt, una vez posicionados en esta carpeta tipeamos NAnt y Enter, si todo anda bien, veremos lo siguiente: 

C:\Lab\AjLopez\AjGenesis-0.5\examples\HelloWorldNAnt>NAnt

NAnt 0.85 (Build 0.85.2478.0; release; 14/10/2006)

Copyright (C) 2001-2006 Gerry Shaw

http://nant.sourceforge.net

Buildfile: C:/Lab/AjLopez/AjGenesis-0.5/examples/HelloWorldNAnt/default.build

Target framework: Microsoft .NET Framework 2.0

Target(s) specified: build  

loadtasks: 

[loadtasks] Scanning assembly “AjGenesis.NAnt” for extensions. 

init:  

build: 

Generating Solution HelloWorld

Creating Directories

Creating Solution File

Creating VB.Net Project

Creating C# Project 

BUILD SUCCEEDED 

Total time: 0.4 seconds. 

Al ejecutar NAnt, este busca en la misma carpeta un archivo con extensión build, si lo encuentra lo ejecuta, si no, da el error que vimos anteriormente.Si vamos a ver en la carpeta C:\Lab\AjLopez\AjGenesis-0.5\examples\HelloWorldNAnt\Build veremos que hay una carpeta de nombre HelloWorld que contiene la solución y dos carpetas que contienen sendos proyectos uno en C# y otro en VB. 

Si funcionó, a festejar, sino escribime y vemos cómo te puedo ayudar. :)

Asesoramiento a Organizaciones Públicas y Privadas.

Cursos a grupos ya constituidos.

Charlas de orientación.

Metodologías Ágiles.

Startups.

Visitas

  • 115,613 hits

Carlos Marcelo Santos

Mejor calificado

del.icio.us

Seguir

Recibe cada nueva publicación en tu buzón de correo electrónico.