Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

19 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
domingo, 7 de junio de 2009
Code TagHace unos días publiqué CodeTag, un sencillo plugin para Windows Live Writer que permite insertar pequeñas porciones de código fuente en línea en los contenidos de los posts. Resumidamente, lo único que hace esta utilidad es envolver entre etiquetas <code> y </code> el texto que tengamos seleccionado en el momento de su utilización, pero evita tener que entrar al modo de edición HTML para hacer estos ajustes.

Basándome en el código fuente de CodeTag (que podéis descargar al final de este post), voy a describir, paso a paso, cómo podemos crear complementos para Writer que nos faciliten un poco las tareas cotidianas de edición.

0. Antes de empezar…

Para poder desarrollar el plugin necesitáis básicamente dos cosas:

  • en primer lugar, el propio software Live Writer, más que nada porque necesitamos referenciar algunos de sus ensamblados, y por ir probando el complemento durante el desarrollo.
  • en segundo lugar, Visual Studio 2005 o 2008, aunque sea una edición express. Este desarrollo vamos a realizarlo en C#, pero la traducción a Visual Basic .NET sería trivial.
En este post voy a incluir capturas de pantalla correspondientes a Microsoft Visual C# 2008 Express, que es el único que tengo a mano.

1. Preparación del proyecto

Los plugins para Live Writer son archivos .dll, es decir, ensamblados .NET, que se colocan en el directorio "plugins" de la carpeta de instalación del programa. En mi caso, están en la carpeta C:\Archivos de programa\Windows Live\Writer\Plugins, y si no habéis cambiado las rutas de instalación por defecto, será allí donde los podéis encontrar también. Durante el proceso de arranque, Writer examinará dicha carpeta y cargará los complementos que encuentre en ella.

Crear librería de clasesLo que vamos a hacer es crear desde Visual Studio un proyecto de librería de clases. Cada vez que compilemos, copiaremos el ensamblado resultante en dicho directorio (veremos cómo podemos automatizar esta tarea) y podremos lanzar Live Writer para comprobar el funcionamiento.

Por tanto, en primer lugar creamos un proyecto de librería de clases como siempre, al que le damos el nombre CodeTag. Obviamente, podéis dar al proyecto el nombre que estiméis conveniente.

Una vez que el IDE crea la estructura, debemos añadir dos referencias al proyecto, que serán necesarias para poder continuar:

  • Añadir referencias La primera referencia es al ensamblado que contiene el API de Live Writer, que podéis encontrar en el directorio de instalación de la herramienta. El  archivo a incluir es WindowsLive.Writer.Api.dll .
  • La segunda es a System.Windows.Forms. Hay que tener en cuenta que Live Writer es una herramienta de escritorio, y esta referencia es importante para poder interactuar con el mismo.

Otro detalle más que nos va a facilitar las cosas: vamos a indicar a Visual Studio que el resultado de la compilación lo copie en el directorio de plugins de Live Writer. Para ello, lo único que tenemos que hacer es acudir a las propiedades del proyecto, pestaña “eventos de generación” e incluir la siguiente orden en el cuadro “línea de comandos del evento posterior a la generación” (obviamente, modificando la ruta si es necesario):

XCOPY /D /Y /R "$(TargetPath)" "C:\Archivos de Programa\Windows Live\Writer\Plugins\"

Evento posterior a la generación

Ojo, es importante tener en cuenta algo más cuando vayamos a compilar: si Live Writer está abierto, no podremos sobrescribir nuestro plugin con una nueva versión, pues éste se encontrará en uso. En estos casos debemos cerrar el programa antes de copiar el archivo, o antes de compilar.

Con estas operaciones ya tenemos el proyecto listo para empezar a codificar el plugin.

2. Escribimos la clase principal

Un plugin sencillo como este no requiere demasiada programación, pero sí hay que cumplir una serie de requisitos para que se integre correctamente en Live Writer.

En primer lugar, creamos una clase, a la que llamaremos CodeTag, e insertamos el siguiente código:

  public class CodeTag: ContentSource
{
public override DialogResult CreateContent(IWin32Window dialogOwner,
ref string content)
{
if (string.IsNullOrEmpty(content))
content
= "YourCodeHere";

content
= "<code>" + content + "</code> ";
return DialogResult.OK;
}
}

Como se puede observar, la clase hereda de ContentSource, un tipo definido en WindowsLive.Writer.Api, que sirve como base para la creación de complementos sencillos como el que nos ocupa. Para casos más complejos, que permitieran por ejemplo edición de contenidos bidireccional entre el contenido de la página y un editor personalizado podríamos heredar de SmartContentSource, pero no profundizaremos en ello ahora.

La codificación de la lógica del plugin puede realizarse en varios puntos, dependiendo de la forma en que Writer lo active; en nuestro caso, el complemento se lanzará cuando el usuario presione un botón en la barra lateral o seleccione la opción correspondiente en el menú "insertar", por lo que simplemente deberemos sobrescribir el método CreateContent.

Dicho método recibe dos parámetros. El primero de ellos hace referencia a la ventana activa, que podemos utilizar como "padre" si quisiéramos crear un cuadro de diálogo desde nuestro código. El segundo parámetro contendrá una referencia al texto seleccionado en el momento de lanzar el plugin, pudiendo darse los dos casos que se contemplan en la codificación:

  • Si la variable content viene vacía o con valor nulo, es que el complemento ha sido lanzado en modo inserción, es decir, no existía ningún texto seleccionado y por lo tanto lo que se pretende es añadir las etiquetas de código para introducir posteriormente contenido. Como puede observarse en el código, lo que se hace en este caso es insertar las etiquetas <code> con un texto arbitrario para que el usuario lo modifique a su antojo más adelante.
  • En caso contrario, si la variable content trae algún contenido, lo que se hace es rodear éste por la apertura y cierre de la etiqueta <code>.

En ambos casos se retorna un valor DialogResult.OK, que indica a Live Writer que debe insertar el texto contenido en content en la ubicación donde se encuentre el cursor, o bien sustituir el texto seleccionado por el nuevo valor.

3. Añadimos metadatos

Heredar de la clase ContentSource no es el único requisito para que esta sea considerada como un componente de Writer, es necesario adornarla con un conjunto de atributos como los mostrados en el siguiente código:
[WriterPlugin("98497c2b-bbfd-4bd1-b343-226f3c9e766b", "Code Tag",
Description
= "Crea etiquetas <code></code> en el contenido",
PublisherUrl
= "http://www.variablenotfound.com")]
[InsertableContentSource(
"Etiqueta <code>")]
public class CodeTag: ContentSource
{
[...]
}

El atributo [WriterPlugin] es el que realmente identifica la clase como plugin de Live Writer. Los parámetros que se le están enviando son los siguientes:
  • El primer parámetro es un GUID, es decir, un identificador único que debemos generar para el plugin, utilizando este servicio on-line u otras herramientas como la incluida en Visual Studio.
  • El segundo parámetro es el nombre del plugin. Es la identificación que aparece en el cuadro de diálogo de configuración de complementos de Live Writer.
  • Description permite añadir una descripción textual ampliada del plugin.
  • PublisherUrl es una dirección web de referencia al autor. Pura propaganda ;-)


El atributo [InsertableContentsource] indica que el complemento debe aparecer en el menú "insertar" de las barra de tareas de Writer y en el menú principal. El parámetro que le estamos enviando es simplemente el texto que aparecerá en estos menús.

4. Compilamos el proyecto

Con lo hecho hasta el momento ya podemos compilar e intentar probar nuestro complemento. Este procedimiento lo repetiremos varias veces durante el desarrollo, por lo que es posible que nos encontremos a menudo con un error como el siguiente:

The command "XCOPY /D /Y /R "CodeTag.dll" "C:\Archivos de Programa\Windows Live\Writer\Plugins\"" exited with code 4

Esto simplemente quiere decir que Live Writer está usando CodeTag.dll y no puede ser sobrescrito, por lo que ¡cerrad Live Writer antes de compilar!

CodeTag en el menú "insertar", sin icono Una vez superado este leve impedimento, si aparece, ya podremos comenzar a disfrutar de nuestro plugin. Veremos que aparece en el menú "insertar", con la descripción apropiada, y funcionando correctamente.

Pero vaya, es cierto que en el menú aparece, pero destaca sobre el resto de complementos porque es el único que no tiene un icono, así que habrá que mejorarlo un poco…

5. Añadirle un icono al plugin

Incluir un icono a nuestro complemento le dará sin duda un aspecto más profesional, vamos a ello.

image Lo primero que necesitamos es una imagen de 16x16 píxeles, por ejemplo en formato .bmp, que actuará como icono. Como el diseño gráfico decididamente no es lo mío, he creado una imagen muy simple pero creo que bastante ilustrativa de la tarea que realiza el complemento: la propia etiqueta <code> que pretendemos crear. Incrustar la imagen como recurso en el ensamblado

El archivo debemos incluirlo, para no tener problemas, en el directorio raíz del proyecto, y a continuación, hay que indicar a Visual Studio que dicha imagen será un recurso incrustado (embedded resource) en el ensamblado. Este paso es importante, pues si no se hace correctamente, la imagen no será incluida en la DLL.

A continuación es necesario indicar a Live Writer la imagen a utilizar como icono, lo que se consigue añadiendo un parámetro más (ImagePath) en la definición del atributo [WriterPlugin] con la ruta hacia el fichero que hemos incrustado. Eso sí, no me refiero a la ruta física del archivo .bmp, sino a la representación como espacio de nombres de la misma (por ejemplo, si la imagen se llama logo.bmp y está en (raíz)\recursos, la ruta hacia ella será “recursos.logo.bmp”).

Como en este caso hemos depositado la imagen en el directorio raíz, la declaración de atributos quedará como sigue:
    [WriterPlugin("98497c2b-bbfd-4bd1-b343-226f3c9e766b", "Code Tag",
Description
= "Crea etiquetas <code></code> en el contenido",
ImagePath
= "CodeTag.bmp",
PublisherUrl
= "http://www.variablenotfound.com")]
[InsertableContentSource(
"Etiqueta <code>")]
public class CodeTag : ContentSource
{
[...]
}

Un último apunte relativo a este tema: si al iniciar Live Writer éste es incapaz de localizar el recurso indicado en el parámetro ImagePath, el plugin funcionará, pero aparecerá el siguiente mensaje en el arranque de la aplicación:

Advertencia: no se ha podido cargar el mapa de bits para el complemento CodeTag desde la ruta especificada (CodeTag.bmp)

6. O, por si no quieres teclear…

CodeTag en el menú "insertar", con icono … he colgado en SkyDrive el código fuente del proyecto, para el que quiera utilizarlo como base de creación de sus propios complementos, o simplemente para trastearlo un poco.

Requiere, como mínimo, Visual C# 2008 Express, con su correspondiente Service Pack 1.


Publicado en: Variable not found.
martes, 2 de junio de 2009

¡Menuda vista! El framework ASP.NET MVC utiliza en varios puntos la filosofía que suele denominarse convención sobre configuración, ahorrando tiempo y esfuerzos al desarrollador que decida asumir una serie de normas preestablecidas, a la vez que facilita la homogeneidad y coherencia en las soluciones que las implementen.

Por ejemplo, todos sabemos que las vistas de una aplicación ASP.NET MVC deben encontrarse en la carpeta Views. Si se trata de una vista compartida como puede ser una página maestra, debemos colocarlas en la subcarpeta Shared; en caso contrario, deberá existir una subcarpeta con el nombre del controlador en la que se encontrarán las vistas para las distintas acciones, nombradas igual que éstas. Esto, a la postre, evita que sea el desarrollador el que tenga que decidir dónde ubicar dichos archivos y configurar el sistema para que sea capaz de localizarlos cuando  sean necesarios: simplemente deberá seguir la convención preestablecida.

Pero, ¿qué ocurre cuando estas convenciones no encajan con nuestras necesidades o preferencias?

En este post voy a describir cómo es posible modificar la ubicación de las vistas en una aplicación ASP.NET MVC 1.0, saltándonos las convenciones establecidas por el framework. Pero sobre todo, ojo:

Saltarse las convenciones = malo

Hazlo sólo cuando realmente esté justificado... o por diversión, como es el caso ;-)

1. Cómo localiza ASP.NET MVC las vistas

Cada vez que el framework necesita localizar una vista, por ejemplo después de ejecutar una acción cuyo resultado indica que debe mostrarse al usuario una página,  recorre los directorios donde, por convención, se supone que debe estar. Esta información se encuentra definida a nivel de código en el constructor de la clase WebFormViewEngine, es decir, en el motor de vistas Webforms que se utiliza por defecto en ASP.NET MVC. Su definición es la siguiente (recordad que está disponible el código fuente del framework):

public class WebFormViewEngine : VirtualPathProviderViewEngine
{
[...]
public WebFormViewEngine()
{
base.MasterLocationFormats = new string[]
{
"~/Views/{1}/{0}.master",
"~/Views/Shared/{0}.master"
};

base.ViewLocationFormats = new string[]
{
"~/Views/{1}/{0}.aspx",
"~/Views/{1}/{0}.ascx",
"~/Views/Shared/{0}.aspx",
"~/Views/Shared/{0}.ascx"
};
base.PartialViewLocationFormats = base.ViewLocationFormats;
}
[...]
}

Como se puede observar, existen tres propiedades en el motor del vistas, heredadas de su antecesor, que indican los directorios donde se encuentran los archivos con la definición del interfaz:

  • MasterLocationFormats es un array con las rutas en las que se buscarán sucesivamente las páginas maestras. En el código puede verse que por defecto el sistema intentará localizar las masters en ~/Views y ~/Views/Shared; los parámetros {0} y {1} corresponden al nombre de la master y del controlador que están siendo buscados, respectivamente.

    Es importante tener en cuenta que el valor de esta propiedad sólo se aplica cuando se establece desde el código la página maestra con la que debe ser mostrada una vista.

  • ViewLocationFormats contiene las rutas donde se intentarán localizar las vistas. En la implementación por defecto, puede verse cómo el framework intenta localizar primero en ~/Views/{1} (recordad que este parámetro se sustituirá por el nombre del controlador) y luego en ~/Views/Shared una página .aspx o .ascx con el nombre de la vista (parámetro {0}). 
  • Por último, PartialViewLocationFormats indica las carpetas donde se deberán buscar las vistas parciales. Por defecto, se buscarán en los mismos directorios que las vistas normales.

2. Lo que pretendemos

Nueva estructura de carpetas para las vistasUna vez visto en qué se basa el framework para localizar las vistas, vamos al ataque.  Nuestra intención es saltarnos las convenciones creando una nueva estructura de carpetas para las mismas, como la que puede observarse en la imagen lateral. Existirá un directorio raíz, llamado "Interfaz", en el que podemos encontrar dos subdirectorios: "Vistas" y "Masters". En ellos almacenaremos, respectivamente, las vistas y las páginas maestras de nuestra aplicación.

La estructura de carpetas del proyecto será, por tanto, diferente a la propuesta por las plantillas de Visual Studio. Además, no tiene demasiado sentido, desde el punto de vista práctico, pero nos valdrá como ejemplo.

En los siguientes apartados vamos a ver dos formas distintas de conseguirlo.


3. Cómo lograrlo

La forma más sencilla de conseguir reemplazar las rutas de búsqueda de las vistas es, sin duda, alterar el contenido de las propiedades del motor descritas en el primer punto (MasterLocationFormats, ViewLocationFormats y PartialViewLocationFormats).

Esto puede conseguirse introduciendo el siguiente código en la inicialización de la aplicación, en el archivo global.asax:

public class MvcApplication : System.Web.HttpApplication
{
[...]
protected void Application_Start()
{
ReplacePaths();
// Reemplaza las rutas en el View Engine actual
RegisterRoutes(RouteTable.Routes);
}

private void ReplacePaths()
{
WebFormViewEngine eng
= ViewEngines.Engines[0] as WebFormViewEngine;
eng.MasterLocationFormats
=
new string[] {
"~/Interfaz/Masters/{0}.master"
};
eng.ViewLocationFormats
=
new string[] {
"~/Interfaz/Vistas/{0}.aspx",
"~/Interfaz/Vistas/{0}.ascx"
};
eng.PartialViewLocationFormats
= eng.ViewLocationFormats;
}
[...]
}

Como se observa en el código anterior, en el método ReplacePaths() en primer lugar se obtiene una referencia al motor de vistas por defecto, que sabemos que es del tipo WebFormViewEngine. Después, modificamos las propiedades para introducir las rutas que nos interesan. Tan simple como eso.

Es obvio que de esta forma estamos realizando una actualización destructiva de las propiedades. Si quisiéramos mantener las localizaciones por defecto del WebFormViewEngine pero añadir nuevas ubicaciones de búsqueda deberíamos copiar el contenido original del array, añadirle las rutas adicionales, y establecerlo en las propiedades.

Veamos otra forma de conseguir lo mismo.

4. Cómo lograrlo, toma 2

ASP.NET MVC framework ha sido diseñado desde sus orígenes con la extensibilidad en mente. Prácticamente cualquiera de sus comportamientos puede ser personalizado sustituyendo componentes como si se trataran de piezas de un mecano.

Una de las posibilidades de extensión incluidas en la plataforma es la sustitución del motor de vistas que utiliza para componer los interfaces de usuario. Esto, llevado al extremo, permite la utilización de motores distintos al proporcionado por defecto (WebForms) y utilizar alternativas como NHaml, Spark, Brail, o NVelocity, entre otros.

Pero nosotros no iremos tan lejos. Para cumplir con nuestros objetivos simplemente necesitamos crear y registrar en el sistema un nuevo motor de vistas que herede de WebFormViewEngine y reemplace la inicialización de las propiedades que definen la localización de las vistas.

El nuevo motor, al que llamaremos AcmeViewEngine, será algo como lo siguiente:

 public class AcmeViewEngine : WebFormViewEngine
{
public AcmeViewEngine()
{
MasterLocationFormats
=
new string[] {
"~/Interfaz/Masters/{0}.master"
};

ViewLocationFormats
=
new string[] {
"~/Interfaz/Vistas/{0}.aspx",
"~/Interfaz/Vistas/{0}.ascx",
};

PartialViewLocationFormats
= base.ViewLocationFormats;
}
}

Y ahora sólo nos faltaría registrar el motor de vistas en ASP.NET MVC framework, tarea que podemos realizar en el global.asax, así:

protected void Application_Start()
{
ReplaceViewEngine();
// Registra el nuevo View Engine
RegisterRoutes(RouteTable.Routes);
}

private void ReplaceViewEngine()
{
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(
new AcmeViewEngine());
}

Observad que antes de añadir el nuevo motor de vistas a la colección estamos vaciándola (llamando a su método Clear()), lo cual asegurará que nuestro AcmeViewEngine será el utilizado por la plataforma.

5. Conclusión

No podremos saltar directamente a la vista desde el controladorComo ya he comentado al principio, saltarnos las convenciones no es buena idea. Por ejemplo, un daño colateral de romper con la convención propuesta por el framework MVC para la localización de las vistas es que Visual Studio no será capaz de proporcionarnos la función que nos  permite abrir la vista asociada con una acción seleccionando la opción “Go to View” del menú contextual, o intentará insistentemente crear las nuevas vistas (con la opción “Add View”) en las carpetas en las que deberían encontrarse según la convención.

Pero en cualquier caso, es un ejercicio interesante para conocer las interioridades de este framework y comprobar de primera mano la flexibilidad con la que ha sido implementado.

Si lo deseas, puedes descargar el código fuente de los ejemplos (requiere Visual Studio 2008 o Web Developer Express con SP1 y ASP.NET MVC 1.0):



Publicado en: www.variablenotfound.com

domingo, 31 de mayo de 2009

No sé si será un cambio en la forma de comunicarse a través de la red, pero sin duda es una idea conceptualmente atractiva: unir en una única plataforma todas las conversaciones que mantenemos sobre Internet… pero entendiendo el término conversación de una forma más amplia de lo habitual.

Mensajes tradicionales frente al concepto Google WavesMensajes de correo, foros, mensajería instantánea, compartición de imágenes y documentos, posts, comentarios en blogs, sus respuestas, twits, intercambio de mensajes vía redes sociales, documentos colaborativos en tiempo real, wikis, juegos… todo cabe en Google Wave, la criatura en la que los hermanos Lars y Jens Rasmussen (famosos por haber creado Google Maps) llevan trabajando más de dos años, y que fue presentada hace unos días en el Google I/O.

Básicamente, Google Wave es la respuesta a una serie de preguntas que se formularon al comienzo del proyecto:

  • ¿cómo sería el correo electrónico si hubiera sido inventado hoy, y no hace más de tres décadas?
  • si en el fondo todo es comunicación, ¿por qué tenemos que diferenciar entre emails, chats, conversaciones o documentos?
  • ¿es posible que un único modelo de comunicaciones sea capaz de unir de forma sencilla los sistemas web que utilizamos hoy?
  • dado que las herramientas de comunicación habituales son meros reflejos digitales de los procesos del mundo analógico (email vs. correo tradicional, mensajería instantánea vs. teléfono…), ¿cómo sería un sistema diseñado para beneficiarse de las capacidades de las redes y ordenadores actuales, en lugar de limitarse a emular los medios no electrónicos?

Pero no penséis que Google Wave sólo es una aplicación más, como GMail o Google Reader, capaz de hacer de forma diferente cosas que ya se podían hacer antes. Se trata de un trío compuesto por tres elementos: producto, plataforma y protocolo:

  • image El producto, que es la aplicación cliente que mostraron en el evento, muy al estilo Google, desde la que se pueden iniciar y gestionar nuestras conversaciones. Se trata de una aplicación HTML5 construida sobre Google Web Toolkit, que todavía sólo está disponible para un grupo reducido de desarrolladores, y que han confirmado que será open source.
  • La plataforma, el conjunto de APIs y especificaciones que permiten integrar Wave en cualquier sistema web, o extenderlo mediante la creación de Robots o Gadgets.
  • El protocolo, Google Wave Federation Protocol, aún en borrador, se encuentra definido en una serie de documentos que detallan su arquitectura, el modelo de datos, las comunicaciones entre clientes y servidores y, en general, toda la información necesaria para que puedan existir distintas implementaciones del servicio. De hecho, el producto de Google será únicamente una implementación de referencia.

Esta nueva forma de colaboración gira en torno a la figura del Wave, que según dicen los entendidos es una mezcla entre una conversación y un documento sobre los que trabajan de forma colaborativa una serie de participantes. Y la verdad, tiene un aire a las conversaciones de GMail, y también a la edición multiusuario de Google Docs, pero va bastante más allá. Un Wave puede contener mensajes, comentarios, anotaciones y elementos más avanzados, como mapas, encuestas, juegos, o tomar datos desde otras aplicaciones y servicios, como blogs, twits, foros, sistemas de seguimiento de errores, y todo integrado dentro de un mismo contexto (la conversación). La cuestión es tener agrupadas todas las comunicaciones relativas a un mismo tema en un único punto.

Además, los participantes no tienen que ser necesariamente humanos, es decir, por ejemplo es posible que en un Wave intervenga un robot que se encargue de ir traduciendo los mensajes al idioma de cada participante, lo cual abre unas posibilidades inimaginables. En el vídeo al que hago referencia abajo hay una demo de Rosy, un robot traductor muy apañado que realiza estas funciones.

Otro aspecto interesante es la facilidad para organizar la información, y me ha gustado mucho la capacidad de temporalizar las conversaciones, de forma que sea posible reproducir la secuencia de aportaciones realizadas al Wave por cada uno de los participantes.

Si tenéis una horita y media libre, y podéis haceros de coca-cola y palomitas, no os perdáis el vídeo de la presentación del producto de mano de sus responsables. Muy divertida y sin duda espectacular. Eso sí, si no domináis el inglés, tendréis que activar vuestro modo de inferencia lingüística, ese que se encarga de rellenar con la imaginación las expresiones que no llegamos a comprender ;-)

Se dice que Wave estará disponible a finales de año. En este momento, lo más que podéis hacer es solicitar que os tengan en consideración cuando lo abran al público.

Enlaces interesantes:

Más enlaces interesantes (en inglés):

Publicado en: Variable not found.

martes, 26 de mayo de 2009

Desde hace unos meses estoy escribiendo las entradas de Variable Not Found desde Windows Live Writer, y la verdad es que estoy encantado con esta herramienta. Pero como nadie es perfecto, me he encontrado con ocasiones en las que tengo que acceder y retocar a mano el código fuente de la página para que el resultado sea el que pretendo.

Me suele ocurrir, por ejemplo, cuando inserto pequeñas porciones de código fuente en mitad de una frase, pues siempre me ha gustado rodear este tipo de textos por las correspondientes etiquetas <code> y </code>. Esto, además de ser un marcado semánticamente más correcto, me permite modificar la forma en que se muestra desde CSS, estableciéndole, por ejemplo, una tipografía de ancho fijo.

Para no tener que hacerlo más a mano, he creado un pequeño plugin para Live Writer que permite seleccionar un texto y envolverlo automáticamente por dichas etiquetas, y he pensado que quizás pueda serle útil a alguien más.

image La criatura se llama “CodeTag”, y la podéis descargar desde el enlace de más abajo. Para instalarla en vuestro Live Writer, simplemente debéis descomprimir el archivo y copiar CodeTag.dll a la carpeta Plugins dentro del directorio donde hayáis instalado la herramienta (en mi caso, la ruta completa es C:\Archivos de programa\Windows Live\Writer\Plugins).

Seguidamente, reiniciáis Live Writer y listo, veréis que en la barra lateral y el menú “insertar” ha aparecido la opción “Etiqueta <code>”, que os permitirá realizar el proceso descrito anteriormente.

En breve publicaré un post describiendo paso a paso cómo se crea un plugin para Live Writer, y veréis lo sencillo que resulta hacer estas pequeñas herramientas de productividad.

Publicado en: Variable not found

domingo, 24 de mayo de 2009

Yellow Screen of Death El código introducido en las vistas ASP.NET MVC pueden ser fuente de problemas en tiempo de ejecución debido a que, por defecto, no se compilan con el proyecto, sino cuando el sistema necesita mostrarlas por primera vez. Es decir, es perfectamente posible que errores de codificación en un archivo .ASPX no sean detectados hasta el momento de su ejecución, provocando un YSOD de los de toda la vida.

Un ejemplo. Imaginemos una vista tipada (en un archivo .ASPX) en la que hemos incluido el siguiente código, perfectamente válido…

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" 
Inherits
="System.Web.Mvc.ViewPage<Persona>" %>
[...]
<h2>Datos personales</h2>
Nombre:
<%= this.Model.Nombre %>

[...]
… perfectamente válido mientras la clase Persona tenga una propiedad que se llame Nombre. Si eliminamos esta propiedad o simplemente cambiamos su nombre, la refactorización automática del IDE no se realizará sobre el código de la vista, y se generará un error en ejecución en cuanto intentemos acceder a ella, es decir, estaremos llevando a tiempo de ejecución problemas propios del tiempo de compilación.

Afortunadamente, ASP.NET MVC framework incluye un mecanismo que nos permite forzar la compilación de las vistas y, así, detectar estos problemas de forma temprana, antes de poner el sitio en producción.

Para activarlo, lo primero que hay que hacer es abrir el archivo del proyecto (.csproj o .vbproj) con cualquier editor de textos, como el block de notas, localizar en las primeras líneas el comienzo de la sección PropertyGroup, e insertar justo después la declaración que habilita la compilación de las vistas:

<PropertyGroup>
<MvcBuildViews>true</MvcBuildViews>
[...]
</PropertyGroup>

Ojo a un detalle: es posible que en algún punto del archivo del proyecto exista una declaración que anule la anterior así: <MvcBuildViews>false</MvcBuildViews>. Debéis eliminar esa línea, pues de lo contrario no se activará esta característica.

Acto seguido, debemos asegurarnos de que exista el siguiente código cerca del final del archivo; en mi caso ya existía, pero si no es así debemos insertarlo, por ejemplo, justo antes del cierre del documento (en la línea anterior a </Projects>).

<Target Name="AfterBuild" Condition="'$(MvcBuildViews)'=='true'">
<AspNetCompiler VirtualPath="temp"
PhysicalPath
="$(ProjectDir)\..\$(ProjectName)" />
</Target>

Una vez guardado el archivo, recargamos la solución en Visual Studio, y listo. Eso sí, a partir de este momento tendremos que esperar un poco más durante la generación del proyecto, pero la seguridad que aporta bien vale la pena.

Fuente: Compile Views in ASP.NET MVC - Stack Overflow

Publicado en: Variable not found.

miércoles, 20 de mayo de 2009

jQueryEl gran K. Scott Allen comentaba hace unas semanas en OdeToCode un pequeño truco que puede resultar de utilidad cuando tengamos un código jQuery que no funcione correctamente.

Como comenta el autor, la mayoría de los problemas con jQuery se deben a un uso incorrecto de los potentes selectores, así que lo que propone es el uso de la consola javascript incluida en algunas herramientas como Firebug (para Firefox) o las propias herramientas de desarrollo incluidas en Internet Explorer 8 (geniales, por cierto).

Consola en FirebugEsta consola permite en ambos casos introducir instrucciones javascript de forma directa, que serán ejecutadas en el contexto de la página actual como si formaran parte del código de las mismas. Por tanto, en aquellas páginas en las que se incluido jQuery, será posible ejecutar llamadas y selecciones a lo largo del DOM y observar el resultado de forma interactiva.

En la imagen adjunta se ve cómo podemos ir realizando consultas con selectores e ir observando sus resultados con Firebug. Hay que tener en cuenta que la mayoría de operaciones de selección con jQuery retornan una colección con los elementos encontrados, por eso podemos utilizar length para obtener el número de coincidencias. Pero no sólo eso, podríamos utilizar expresiones como la siguiente para mostrar por consola el texto contenido en las etiquetas que cumplan el criterio:

$("div > a").each(function() { console.log($(this).text()) })

Y más aún, si lo que queremos es obtener una representación visual sobre la propia página, podemos alterar los elementos o destacarlos con cualquiera de los métodos de manipulación disponibles en jQuery, por ejemplo:

$("a:contains['sex']").css("background-color", "yellow")

La siguiente captura muestra el resultado de una consulta realizada con las herramientas de desarrollo incluidas en Internet Explorer 8: Consola en IE8


En fin, un pequeño truco para facilitarnos un poco la vida.


Fuente: What I Do When My jQuery Code Doesn’t Work

lunes, 18 de mayo de 2009
PensadorHa transcurrido un año desde que publiqué mi recopilación de citas célebres del mundo de la informática, como continuación de la serie iniciada originalmente por el amigo Timm Martin en DevTopics. Desde entonces he ido anotando todas las citas que me llamaban la atención, hasta que por fin he llegado al número mágico y puedo publicar el tercer post de la serie que tanto me divierte. Que aproveche.

Este es el tercer post de la serie compuesta por:
martes, 12 de mayo de 2009

yUML Tobin Harris ha puesto en marcha un curioso e interesante servicio, al que ha denominado yUML, cuyo propósito es facilitar la generación al vuelo de diagramas UML a partir de una concisa descripción expresada en un sencillo lenguaje específico.

Pero además de la generación automática del diagrama UML, que ya tiene mérito de por sí, llama la atención que esto puede realizarse desde una etiqueta (X)HTML <img> a la que se le envía la especificación como parámetro, generándose en tiempo real una imagen con el diagrama correspondiente. Por ello, está especialmente indicado para autores de blogs, wikis o contenidos web que deseen introducir muy fácilmente estos diagramas en sus creaciones.

En los siguientes ejemplos se muestran la especificación y la imagen generada de un diagrama de clases y uno de casos de uso, respectivamente. Como podréis comprobar, la especificación es bastante legible directamente.

Diagrama de clases

Especificación: [Company]<>-1>[Location], [Location]+->[Point]

image

Código fuente para su inserción en una página:

<img src="http://yuml.me/diagram/class/[Company]<>-1>[Location], [Location]+->[Point]" />

Estilo informalUna curiosidad es que si en la dirección insertamos la palabra “scruffy”, el sistema generará un diagrama como si hubiese sido dibujado a mano, en un estilo mucho más informal:
<img src="http://yuml.me/diagram/scruffy/class/[Company]<>-1>[Location], [Location]+->[Point]" />

Diagrama de casos de uso

Especificación: [User]-(Login), [User]-(Logout), (Login)<(Reminder), (Login)>(Captcha)

image

Código fuente para su inserción en una página:

<img src="http://yuml.me/diagram/usecase/[User]-(Login), [User]-(Logout), (Login)<(Reminder), (Login)>(Captcha)" />

Si prefieres simplemente descargar el diagrama para subirlo a tu web, opción bastante prudente hasta que se pueda confiar en la fiabilidad y disponibilidad del servicio, desde el mismo sitio web del producto podemos generar la imagen introduciendo la especificación textual; a partir de ahí podremos copiarla y pegarla sobre nuestra herramienta de edición, o salvarla en nuestro equipo para enviarla a su ubicación definitiva.

Si bien todavía yUML está en pruebas y es algo limitado (sólo soporta diagramas de clases y de casos de uso), me ha parecido muy interesante tanto la idea como la implementación del servicio.


Publicado en: Variable not found

lunes, 11 de mayo de 2009

Joe Cartano, perteneciente al Visual Web Development Team de Microsoft, ha publicado en el blog oficial (Visual Web Developer Team Blog) un post anunciando que ha actualizado la plantilla que permite crear directamente proyectos ASP.NET MVC con tests unitarios NUnit.

Así, una vez instalado, al crear un proyecto de tipo ASP.NET MVC nos aparecerá un cuadro de diálogo como el siguiente, en el que se nos brindará la oportunidad de crear en la solución un proyecto de pruebas unitarias utilizando NUnit:

Crear proyecto de pruebas unitarias

Para los poseedores de algunas de las versiones profesionales de Visual Studio 2008, esta ventana no es nueva, puesto que por defecto ya se incluyen los proyectos de tests propios de Microsoft, pero sí lo es para la versión express del IDE, que no incluye el framework de pruebas y en el que había que crear el proyecto a mano.

El proyecto, que se crea automáticamente, realiza 27 tests sobre la plantilla original de proyectos ASP.NET MVC, y pueden servir como base para seguir creando nuestro propio conjunto de pruebas:

¡La plantilla ASP.NET MVC valida!

La plantilla se instala descargando el archivo .zip cuyo enlace encontraréis en el post original y ejecutando, si es necesario con un usuario con privilegios de administrador, el script installNUnit.cmd disponible en el raíz del directorio donde lo hayáis descomprimido.

Para que todo vaya bien, debéis contar con NUnit previamente instalado en vuestro equipo. Pero ojo, las plantillas están preparadas para la versión 2.4.8.0 y la versión actual es la 2.5.0.9122, por lo que puede que no os funcionen bien si estáis a la última.

En este caso, es necesario tocar a mano los archivos de plantillas. Podéis seguir estos pasos:

  1. Descargad el archivo de plantillas desde el blog del Visual Web Developer Team.
  2. Descomprimidlo sobre cualquier carpeta.
  3. En los subdirectorios CSharp y Visual Basic encontraréis un archivo llamado MvcApplication.NUnit.Tests.zip. Son las plantillas para cada uno de esos lenguajes.
  4. El interior de dicho zip está el archivo de proyecto (MvcApplication.NUnit.Test.vbproj en el caso de Visual Basic, MvcApplication.NUnit.Test.csproj en C#). Descomprimid sólo estos archivos y buscar en ambos la cadena “2.4.8.0” y sustituirla por la versión de NUnit que tengáis instalada (en mi caso, “2.5.0.9122”). Al acabar, actualizad de nuevo el .zip con el archivo que acabáis de editar.
  5. Ejecutad el script de instalación installNUnit.cmd.

Publicado en: Variable not found.

domingo, 10 de mayo de 2009

¡Ya van tres años! El pasado 8 de mayo, además de conmemorarse el nacimiento de la Coca-Cola, se cumplían tres años desde que publiqué el primer post en este blog. Y me sigue pareciendo que fue ayer, cómo pasa el tiempo…

Como en otras ocasiones, es un bonito momento para recapitular y comentar lo que ha ido ocurriendo por aquí desde entonces.

Durante la celebración del segundo cumpleaños hice una distinción de dos periodos en la historia de Variable not found, que os resumo:

  • Año 1: la travesía del desierto (mayo 2006 - mayo 2007), periodo caracterizado principalmente por la ausencia prácticamente total de visitantes al blog, por aquellos tiempos disponible a través de la dirección jmaguilar.blogspot.com. Y no exagero, observad el registro de visitas de los primeros meses, y tened en cuenta que yo mismo también visitaba la página:

    image 
    Aprovecho para introducir un mensaje de aliento para los que estéis en esta situación ahora, empezando un blog: no os desaniméis, ni aun viendo este desastre. Con un poco de esfuerzo todo puede cambiar. El desierto también tiene su fin.
     
  • Año 2: el despegue (mayo 2007 - mayo 2008), durante el cual se produjeron múltiples novedades, como la adquisición del dominio variablenotfound.com, la difusión de feeds a través de, el por aquél entonces magnífico, Feedburner, la creación del diseño actual, y la inclusión en agregadores y comunidades como Planeta Código, Planet WebDev y Geeks.ms.

    Este segundo año lo acabamos con un incremento del 2.500% en el número de visitas (la verdad, tampoco lo tenía muy difícil partiendo desde cero ;-)), y lo que me parecía más increíble, 380 suscriptores.

Y siguiendo esta línea, el año que hemos acabamos ahora podríamos describirlo como:

Año 3: la consolidación (mayo 2008 – mayo 2009)

Aunque ha sido un año sin grandes cambios, Variable not found ha seguido creciendo. Obviamente no en las proporciones anteriores, pero sí ha mostrado un aumento considerable tanto de visitantes como de suscriptores.

Respecto a las visitas a la web, hay que destacar los siguientes datos:

  • las visitas, en general, han aumentado cerca de un 40%, así como el número de usuarios únicos absolutos. Hay que tener en cuenta que durante este último año no se han producido meneos espectaculares como el año anterior, no han pasado de meneillos, por lo que la subida de visitas en global tiene bastante mérito.
  • También se han incrementado ligeramente el número de páginas vistas por visita, y el tiempo medio en la página.
  • El tráfico dirigido desde buscadores se han incrementado en un 75% desde el periodo anterior. Esto podría entenderse como que el blog está logrando un posicionamiento importante en estos motores, probablemente debido a la antigüedad, a las referencias externas y a los propios contenidos.
  • Curiosamente, ha disminuido el número de visitantes nuevos en proporción a los que repiten, lo que implica que aumenta el número de usuarios que han visitado el blog más de una vez.
  • Los comentarios, como manda la tradición, siguen siendo escasos: la media no llega a dos comentarios por cada post publicado. Nada que objetar en esto, soy el primero al que le cuesta la misma vida comentar algo en blogs ajenos que sigo con asiduidad. Es cierto que me gustaría obtener más feedback y opiniones de los lectores, pero entiendo que no sea así.

imageEn cuanto a las suscripciones, siguen siendo uno de los datos que más me están sorprendiendo, y muy gratamente. En estos momentos, a pesar de los cambios de humor de Feedburner, hemos llegado ya a alcanzar los 650 suscriptores.

Y sinceramente, este es uno de los datos que más valoro. La temática tan específica del blog hace que los destinatarios de los contenidos, principalmente desarrolladores, sean una minoría, y conseguir que un número tan alto de personas decidan voluntariamente añadirte de alguna forma a sus fuentes favoritas es todo un honor y, sin duda, motivo de orgullo.

¿Dormido o durmiendo?En el aspecto económico, sigo arrasando con los ingresos por AdSense. El mes pasado, junto con el cheque mensual me enviaron una caja de bombones, con una tarjeta de felicitación firmada por el mismísimo Sergey Brin

… vaya, parece que me había quedado dormido otra vez ;-D. Pues no, de momento sigo recaudando centimillos a un ritmo lamentable, pero bueno, ¿quién dijo que esto de bloguear se hacía por dinero?

Lo que considero mi recompensa es lo que estoy aprendiendo. Cada tema tratado, ejemplo, referencia, cada comentario, cada mensaje privado recibido, me aportan una nueva oportunidad para ampliar conocimientos. Si esto lo unimos al placer de comunicar y de compartir los descubrimientos, ya tenemos un buen conjunto de motivaciones que animan a seguir trabajando en el blog.

Pero aún falta algo, sin duda lo más importante: vosotros. Los que estáis ahí leyendo, enviando comentarios o mensajes, añadiendo valor a las entradas, los que le dais sentido a todo esto. Repito lo que ya comenté en el post en el que celebraba el primer aniversario:

Os puedo asegurar que he disfrutado escribiendo cada post, pero las alegrías han venido con cada comentario, con cada visita, con cada referencia.

Muchas gracias a todos, y espero que sigáis por aquí, ayudándome a buscar la variable.

Publicado en: Variable not found

domingo, 3 de mayo de 2009

Existen numerosas aplicaciones que permiten analizar nuestros desarrollos con el objetivo final de incrementar la calidad de los mismos. FxCop, por ejemplo, es capaz de analizar los ensamblados y avisarnos cuando se encuentra con incumplimientos de las pautas de diseño para desarrolladores de librerías para .Net Framework (Design Guidelines for Class Library Developers). También hace tiempo comenté por aquí la disponibilidad de  Microsoft Source Analysis for C#, una herramienta que se centra en el código fuente y el cumplimiento de reglas de codificación.

Ahora, gracias al ofrecimiento de Patrick Smacchia, lead developer del producto, he podido probar NDepend, una herramienta de análisis de código de la que había oído hablar y que va mucho más allá que las citadas anteriormente.

La principal misión de NDepend es ayudarnos a incrementar la calidad de nuestros sistemas desarrollados con .NET mediante el análisis de sus ensamblados y código fuente desde distintas perspectivas, como la complejidad de sus módulos, el tamaño de los mismos, las interdependencias entre ellos, etc. Es decir, a diferencia de otros analizadores, su objetivo no es avisarnos de aspectos como la utilización de convenciones de codificación (aunque también puede hacerlo), o el uso de buenas prácticas de diseño, sino ayudarnos a determinar, por ejemplo, cuándo ponemos en peligro la mantenibilidad y evolución de un sistema debido a la complejidad  o al fuerte acoplamiento de algunos de sus componentes, por citar sólo algunos criterios.

El entorno gráfico

VisualNDepend es el entorno gráfico de la herramienta, y nos ofrece un entorno de trabajo muy potente e intuitivo, aunque durante los primeros minutos pueda resultar algo complejo debido a la cantidad de información mostrada.

Iniciar el análisis de un sistema es muy sencillo; una vez seleccionado el ensamblado, conjunto de ensamblados o proyecto a estudiar, el sistema realiza el análisis, del cual se obtiene un informe bastante completo, basado en web, sobre el mismo (podéis ver un ejemplo de informe en la página del producto). En él se recogen:

  • métricas de la aplicación, entre las que encontramos el número de líneas de código, comentarios, número de clases, métodos, etc., así como datos estadísticos relativos a la complejidad, extensión y estructura del código.
  • métricas por ensamblado, donde se refleja, por cada uno de los ensamblados que componen la solución, datos sobre su tamaño, grados de cohesión, acoplamiento, y otros aspectos relativos a su complejidad e interdependencia.
  • Vista de estructura vista de estructura (captura de la derecha) que muestra la distribución de componentes, la granularidad y su complejidad relativa según el tamaño de los bloques visualizados.
  • diagrama de abstracción e inestabilidad, que posiciona cada ensamblado en función del número de clases abstractas e interfaces que presenta y su dependencia del resto de elementos.
  • relación entre ensamblados, que detalla las interdependencias entre ensamblados del proyecto, los posibles ciclos, así como un posible orden de generación.
  • consultas y restricciones CQL, que realiza una serie de consultas predefinidas sobre los ensamblados y el código que nos ayuda a detectar una infinidad de problemas en nuestros desarrollos, desde aspectos relativamente simples como el exceso de métodos en clases o el incumplimiento de ciertas convenciones de nombrado, hasta problemas en la cobertura de los tests generados con NCover o Visual Studio Team System.

Pero lo mejor del entorno gráfico no es poder generar un análisis en formato web para poder consultarlo más adelante, de hecho esto puede conseguir también con la aplicación de consola que incluye NDepend. Lo mejor son las fantásticas herramientas interactivas que nos permiten navegar a través de nuestras aplicaciones, cambiar de vista, ampliar información sobre cualquier elemento, y realizar consultas en tiempo real, siempre ofreciendo unos resultados muy claros y visuales, como:

Diagrama de dependencias

  • diagrama de dependencias entre todo tipo de elementos, como clases, espacios de nombres, o ensamblados. Resulta muy útil, además, configurar el tamaño de los bloques, el grosor del borde y el de las flechas de unión para que sean proporcionales a la complejidad, tamaño y una larga lista de criterios.
  • Tabla de dependencias matriz de dependencias, que muestra de forma visual las relaciones de utilización entre espacios de nombres, tipos, métodos o propiedades, con posibilidad de ir ampliando información.
  • comparación entre ensamblados, mostrándonos los cambios producidos entre, por ejemplo, dos versiones de una misma librería o aplicación.
  • navegación avanzada por el código a través del uso del menú contextual que facilita la rápida localización de referencias, directas e indirectas, hacia y desde un método, propiedad o tipo existente.
  • enlace con Reflector, una herramienta indispensable, con la que se integra perfectamente gracias a su plugin.

 

CQL (Code Query Language)

Sin duda, una de las características más interesante que tiene NDepend es el soporte del lenguaje de consulta CQL (Code Query Language), que nos ofrece la posibilidad de tratar nuestro código y ensamblados como si fuesen una gigantesca base de datos sobre la que podemos realizar consultas de forma muy natural.  Las posibilidades que esto ofrece son tan amplias que prácticamente todas las funcionalidades de la aplicación están basadas en órdenes CQL prediseñadas que acompañan al producto, aunque podemos crear todas las consultas personalizadas que necesitemos, como por ejemplo:

/* Obtiene los métodos que escriben una propiedad */
SELECT METHODS WHERE IsDirectlyWritingField "Model.Cliente.Nombre"

/* Obtiene métodos que acceden incorrectamente a los
datos desde la capa de interfaz
*/
SELECT METHODS FROM NAMESPACES "Interfaz" WHERE IsDirectlyUsing "MySql.Data"

/* Obtiene los 10 métodos con más líneas de código */
SELECT TOP 10 METHODS ORDER BY NbLinesOfCode DESC

/* Obtiene los métodos considerados "peligrosos" según su complejidad ciclomática */
SELECT METHODS WHERE CyclomaticComplexity > 20


Editor de CQL con intellisenseAdemás de consultas CQL cuyo resultado podremos observar de forma directa tanto visualmente como en forma de listado, es posible incluir restricciones (WARN) que hará que el sistema nos alerte durante el análisis de un proyecto que cumpla las condiciones especificadas. Por ejemplo, la siguiente restricción nos avisará cuando exista un cierto tufillo a complejidad excesiva en un método (que haya sido definido con más de 10 parámetros):

WARN IF Count > 0 IN  SELECT METHODS WHERE NbParameters > 10

Estas consultas pueden ser añadidas (en la versión Pro) e integradas en los análisis, así como modificar las existentes, de forma que el producto puede ser personalizado a nuestras convenciones o necesidades específicas.

Otra posibilidad es incluir dentro del código de un ensamblado las restricciones que deseamos que se cumplan, expresándolas en lenguaje CQL embebido en un atributo de tipo CQLConstraint aplicado a sus elementos:

Afortunadamente existe mucha información disponible en la web sobre el lenguaje CQL, y consultas que nos muestran cómo sacarle el máximo partido.

Instalación e integraciones

NDepend se distribuye en formato .zip, y que puede ejecutarse directamente una vez descomprimido el contenido sobre una carpeta. La licencia, una vez obtenida, es un archivo XML firmado digitalmente que debemos colocar en el directorio de la aplicación, y listo.

Integración con VS y Reflector

Ya en ejecución, desde el menú “options” es posible configurarlo para que se integre con Visual Studio 2005, 2008 y como he comentado anteriormente, con el magnífico Reflector, con el que hace una excelente pareja.

Asimismo, es posible utilizarlo en sistemas como MSBuild y NAnt, facilitando así su uso en entornos de integración continua y montaje automatizado, para los que ofrece librerías

Finalmente, he de aclarar que NDepend es una aplicación comercial, aunque dispone de una versión limitada gratuita utilizable por universidades, desarrolladores open source e incluso, durante un tiempo determinado, de prueba en proyectos comerciales.

En cualquier caso, no dudéis en probarlo: os dará un control sobre vuestro código difícil de conseguir con otras herramientas.


Publicado en: www.variablenotfound.com