[#Oferta Trabajo] Experience Design Manager

Os paso una oferta de trabajo de mi empresa, por si alguno pudiera estar interesado.

La oferta es para Avanade España: http://www.avanade.com

 

Si te preguntas qué tal es Avanade como destino, te comento que ha sido seleccionada por tercer año consecutivo entre las mejores empresas de Europa para trabajar.

http://www.topemployers.eu/TopEmployersEurope2012/C/tabid/5864/C/1394/Avanade.aspx

 

es-es

 

Van los datos de la oferta.

 

Buscamos una persona de más de 6 años de experiencia en la creación de interfaces, web sites y aplicaciones de gran escala

· Imprescindible, tener experiencia demostrada en las siguientes tecnologías: HTML5, CSS3, Javascript, jQuery, XAML Expresion Blend, Photoshop, Illustrator, Expression Design

· Así como una sólida trayectoria en actividades relativas a UX gestionando equipos de personas

· Habilidades en gestión de clientes

· Tener experiencia y conocimiento en la aplicación de las metodologías de diseño de interacción y de investigación para el desarrollo de software, web y móvil

· Facilidad de integración y participación con el equipo

 

Si estás interesado, envíame un correo a

Your gMail image

XSL for Dynamics CRM Plug-in Documentation

Apunto por aquí este XSL que me ha venido bien para generar la documentación del código de unos plug-in para Dynamics CRM.

Como ya sabréis, cuando Visual Studio compila un proyecto, además ofrece la opción de generar un fichero XML con todos los comentarios de las cabeceras de los métodos.

image

Imagen: Opción que hay que activar en VS2008 para que se genere el XML de documentación.

 

image

 

 

image

Imagen: XML de documentación generada por Visual Studio.

 

Este fichero puede ser tratado con herramientas como Sandcastle que nos permite generar a partir de este XML la documentación del proyecto usando el formato de la MSDN, u otro personalizado.

image

Imagen: Ejemplo de documentación generada con Sandcastle.

 

Estas herramientas están muy bien, pero… (siempre hay un pero) este formato puede no ser el que necesitemos. O las opciones que nos ofrezca la herramienta pueden no satisfacer nuestras necesidades.

Entonces lo que podemos hacer, en lugar de abrir el Word y empezar a escribir literatura, es crear un pequeño fichero XSL, que se encargue de filtrar los métodos que necesitamos y que genere la documentación en el formato preciso, a partir del XML que ya genera Visual Studio.

Además, de esta forma, podemos añadir algunos atributos adicionales, que en el caso de los plug-ins de CRM pueden venir muy bien, ahora veremos porqué.

image

Imagen: Elementos añadidos a la documentación que posteriormente reconocerá el XSL.

 

En este proyecto en concreto, necesitaba documentar sólo algunos métodos, además de indicar un identificador asociado a cada método, el requerimiento asociado, y la información de registro del plugin. Todo esto cumpliendo un formato específico, que no era el de la MSDN.

Toda esta información debía ir posteriormente en un Word de millones de páginas, que impreso quedaría fenomenal para coger polvo en la estantería.

image

Imagen: Fichero HTML generado por el XSL.

 

 

 

 

Una de las cosas que hemos ganado utilizando este enfoque ha sido dejar la documentación en el lugar en el que mejor está: junto al código. Hemos pasado de documentar en Word a documentar directamente en la cabecera de cada método. Si para ver la descripción de un parámetro debo abrir un Word tan grande que tarda 2 minutos en cargarse, lo más probable es que prefiera depurarlo para ver qué hace.

Para indicar qué clases se documentan, y qué clases no, se usa el elemento <pluginhandler>. Sólo aquellas clases que lo lleven aparecerán en la documentación.

Para los métodos, ocurre algo parecido. Sólo los métodos que lleven el elemento <plugin> serán documentados.

Pego finalmente el código XSL que genera el HTML de la imagen anterior.

 

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
    xmlns:mf="urn:my-scripts">
  <xsl:output method="html" indent="yes"/>
 
  <xsl:template match="/">
 
    <style>
      body{
      font-size:"9pt";
      font-family:"Arial";
      }
      .Table
      {
      border:solid black 1px;
      }
      .TableTR
      {
      border:none;
      }
      .TableTD
      {
      border-right:solid black 1px;
      border-bottom:solid black 1px;
      padding:5px;
      }
    </style>
 
    <xsl:for-each select="/doc/members/member/pluginhandler">
      <xsl:variable name="typeName" select="substring-after(../@name,'T:')" />
      <!-- parent node name attr-->
      <h4>
        <xsl:value-of select="@entity"/>
        <xsl:text> Plugins</xsl:text>
      </h4>
 
      <table border="0" cellspacing="0" cellpadding="0" width="700px" class="Table">
        <col width="100px" />
        <col width="100px" />
        <col width="200px" />
        <col width="300px" />
        <tr class="TableTR" style="background:#CCCCCC;">
          <td class="TableTD">
            <b>Requirements</b>
          </td>
          <td class="TableTD">
            <b>Plugin Id</b>
          </td>
          <td class="TableTD">
            <b>Registration Info</b>
          </td>
          <td class="TableTD">
            <b>Description</b>
          </td>
        </tr>
 
        <xsl:for-each select="/doc/members/member[starts-with(@name,concat('M:',$typeName))]">
          <xsl:if test="count(plugin)>0">
            <tr class="TableTR">
              <td class="TableTD">
                <xsl:call-template name="TextOrHyphen">
                  <xsl:with-param name="pText" select="requiremementid" />
                </xsl:call-template>
              </td>
              <td class="TableTD">
                <xsl:call-template name="TextOrHyphen">
                  <xsl:with-param name="pText" select="pluginid" />
                </xsl:call-template>
              </td>
              <td class="TableTD">
                <!--Registration Info -->
                <div style="font-size:x-small;">
                  <span style="text-decoration:underline">
                    <xsl:value-of select="plugin/@type"/>
                  </span>
                  <xsl:text> Plug-in</xsl:text>
                  <br />
                  <br />
                  <b>
                    <xsl:text>Events:</xsl:text>
                  </b>
                  <br />
                  <xsl:for-each select="event">
                    <xsl:text>- </xsl:text>
                    <xsl:value-of select="@name"/>
                    <xsl:text> (</xsl:text>
                    <xsl:value-of select="@type"/>
                    <xsl:text>)</xsl:text>
                    <br />
                  </xsl:for-each>
                  <br />
                  <b>
                    <xsl:text>Images:</xsl:text>
                  </b>
                  <br/>
                  <xsl:choose>
                    <xsl:when test="count(image)>0">
                      <xsl:for-each select="image">
                        <span style="text-decoration:underline">
                          <xsl:value-of select="@name"/>
                        </span>
                        <br />
                        <xsl:text>(</xsl:text>
                        <xsl:value-of select="@fields"/>
                        <xsl:text>)</xsl:text>
                        <br />
                      </xsl:for-each>
                    </xsl:when>
                    <xsl:otherwise>
                      <xsl:text>None required</xsl:text>
                    </xsl:otherwise>
                  </xsl:choose>
                </div>
                <!-- End Registration Info -->
              </td>
              <td class="TableTD">
                <xsl:copy-of select="summary" />
              </td>
            </tr>
 
            <!-- Parameters -->
            <xsl:if test="count(param)">
              <tr class="TableTD">
                <td colspan="4" style="background:#E0E0EB">
                  <b>
                    <xsl:text>Parameters</xsl:text>
                  </b>
                </td>
              </tr>
              <tr class="TableTD">
                <td colspan="4">
                  <!-- Parameters -->
                  <table border="0" cellspacing="0" cellpadding="0" width="700px" class="Table">
                    <col width="200px" />
                    <col width="150px" />
                    <col width="350px" />
 
                    <tr class="TableTR" style="background:#CCCCCC">
                      <td class="TableTD">
                        <b>Name</b>
                      </td>
                      <td  class="TableTD">
                        <b>Type</b>
                      </td>
                      <td  class="TableTD">
                        <b>Description</b>
                      </td>
                    </tr>
                    <xsl:choose>
                      <xsl:when test="count(param)=0">
                        <tr class="TableTR">
                          <td class="TableTD">-</td>
                          <td class="TableTD">-</td>
                          <td class="TableTD">-</td>
                        </tr>
                      </xsl:when>
                      <xsl:otherwise>
                        <xsl:for-each select="param" >
                          <tr class="TableTR">
                            <td class="TableTD">
                              <xsl:call-template name="TextOrHyphen">
                                <xsl:with-param name="pText" select="@name" />
                              </xsl:call-template>
                            </td>
                            <td class="TableTD">
                              <xsl:choose>
                                <xsl:when test="not(string(@type)='')">
                                  <xsl:value-of select="@type"/>
                                </xsl:when>
                                <xsl:otherwise>
                                  <xsl:value-of select="mf:GetParameterType(../@name, position())"/>
                                </xsl:otherwise>
                              </xsl:choose>
                            </td>
                            <td class="TableTD">
                              <xsl:copy-of select="." />
                            </td>
                          </tr>
                        </xsl:for-each>
                      </xsl:otherwise>
                    </xsl:choose>
                  </table>
                  <!-- End Parameters -->
                </td>
              </tr>
            </xsl:if>
            <!-- End Parameters -->
 
            <!-- Exceptions and Summary -->
            <xsl:if test="count(exception)>0 or count(remarks)>0">
              <tr class="TableTD">
                <td class="TableTD" colspan="4" style="background:#E0E0EB">
                  <b>
                    <xsl:text>Remarks</xsl:text>
                  </b>
                  <br />
                  <xsl:copy-of select="remarks"/>
                  <br />
                  <br />
                  <xsl:if test="count(exception)>0">
                    <b>
                      <xsl:text>Exceptions</xsl:text>
                    </b>
                    <br />
                    <xsl:for-each select="exception">
                      <u>
                        <xsl:value-of select="@cref"/>
                      </u>
                      <br />
                      <xsl:copy-of select="."/>
                    </xsl:for-each>
                  </xsl:if>
                </td>
              </tr>
            </xsl:if>
            <!-- End Exceptions and Summary -->
 
          </xsl:if>
        </xsl:for-each>
 
      </table>
 
    </xsl:for-each>
  </xsl:template>
 
  <xsl:template name="TextOrHyphen">
    <xsl:param name="pText" />
    <xsl:choose>
      <xsl:when test="string($pText)=''">
        <xsl:text>-</xsl:text>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$pText"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
 
  <msxsl:script language="C#" implements-prefix="mf">
    <![CDATA[ 
    
    public string SubstringAfterLast(string text, string delimiter)
    {
      string retVal = "";
      
      if(!string.IsNullOrEmpty(text)&&text.Contains(delimiter))
      {
          retVal = text.Substring(text.LastIndexOf(delimiter)+1);
      }
      return retVal;
    }
    
    // Splits a method signature and returns the value type of
    // the parameter in the specified position
    public string GetParameterType(string parametersString,int index)
    {
      string retVal = "";
      if(!string.IsNullOrEmpty(parametersString)&&parametersString.Contains("(")){
        parametersString=parametersString.Substring(parametersString.IndexOf('(')+1);
        parametersString=parametersString.Substring(0,parametersString.Length-1);
        string[] parameters = parametersString.Split(',');
        if(index<=parameters.Length)
        {
          retVal = parameters[index-1];
          if(!string.IsNullOrEmpty(retVal))
          {
            if(retVal.Contains("."))
            {
              retVal = retVal.Substring(retVal.LastIndexOf('.')+1);
            }
            retVal = retVal.Replace("}","?");
          }
        }
      }
      return retVal;
    }
    ]]>
  </msxsl:script>
 
</xsl:stylesheet>

 

Son 280 líneas, que aunque parezcan muchas, no se tarda demasiado en escribir. El XSL es un lenguaje muy… verboso (cuál es la traducción de verbose?)

Quizá lo más complicado ha sido obtener el tipo de datos de cada parámetro. Para esto está la función GetParameterType, que aunque está definida dentro del XSL es una función c#. Esta es una funcionalidad XSL no estándar. Sólo está disponible con el parser MSXML.

 

Para ejecutar el XSL se puede usar la opción de menú que aparece cuando abrimos el XSL con visual Studio.

image

La primera vez, Visual Studio nos preguntará qué XML de entrada queremos usar. Habrá que indicarle el XML de documentación que se encuentra en la carpeta bin del output de Visual Studio.

 

También puede hacerse desde la línea de comandos. Inicialmente, pensaba que con la utilidad msxsl.exe

http://www.microsoft.com/en-us/download/details.aspx?id=21714

image

 

 

 

 

Pero resulta que no, que esta utilidad no admite usar funciones en línea, como la que usamos para averiguar los tipos de los parámetros.

 

Por lo que necesitamos hacernos una aplicación de consola. Pero, es sencillito sencillito.

La clase que utilizamos para realizar la magia es XslCompiledTransform.

http://msdn.microsoft.com/en-us/library/42d26t30.aspx

 

Aplicación de consola para ejecutar la transformación:

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.XPath;
using System.Xml;
using System.Xml.Xsl;
 
namespace XslTransform
{
    class Program
    {
        static void Main(string[] args)
        {
            if (args.Length != 3)
            {
                Console.WriteLine("You have not entered the correct parameters");
                return;
            }
 
            string xmlfile = args[0];
            string xslfile = args[1];
            string outfile = args[2];
 
            try
            {
                using (XmlReader reader = XmlReader.Create(xmlfile))
                {
                    XmlWriterSettings wSettings = new XmlWriterSettings();
                    wSettings.ConformanceLevel = ConformanceLevel.Auto;
                    using (XmlWriter writer = XmlWriter.Create(outfile,wSettings))
                    {
                        // Create and load the transform with script execution enabled.
                        XslCompiledTransform transform = new XslCompiledTransform();
                        XsltSettings settings = new XsltSettings();
                        settings.EnableScript = true;
                        transform.Load(xslfile, settings, null);
 
                        // Execute the transformation.
                        transform.Transform(reader, writer);
                        writer.Close();
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }
    }
}

Primer parámetro: Path del fichero Xml con la documentación generado por Visual Studio

Segundo parámetro: Path del fichero Xsl que hemos construido.

Tercer parámetro: Path de salida. Sería el Xml con la documentación HTML Generada.

 

Y la llamada desde la línea de comandos sería así:

XslTransform.exe MyBusiness.Plugnis.xml PluginsDoc.xslt PluginsDoc.html

 

 

 

El código se puede descargar desde aquí

 

Referencias:

http://www.w3schools.com/xsl/

http://www.w3schools.com/xpath/

http://msdn.microsoft.com/en-us/library/ms256471.aspx