“Leadership is the art of trading imaginary things in the future for real things today”.
Dilbert es genial.
“Leadership is the art of trading imaginary things in the future for real things today”.
Dilbert es genial.
Home Sheep Home es un muy buen juego en el que tenemos que combinar las habilidades y propiedades de tres ovejas para ayudarlas a atravesar la pantalla.
Me hizo recordar bastante al Lost Vikings (que por cierto se pueden bajar en Abandonia).
Visto en Juegos Microsiervos.
Gran Proyecto (con mayúscula), muy “visible” para la gerencia de Megaempresa, vital para la subsistencia de NoTanGranPeroGranConsultoraOSoftwareFactory, y por tanto muy “visible” también para la gerencia de ésta última.
Desde nuestro punto de vista (de sistemas) no es más que otra aplicación de formularios que le pegan una y otra vez a una base de datos, más un sinfín de reportes que vienen a sacar lo que los formularios pusieron.
Otra gran, torpe, enmarañada, traicionera, e incansable generadora de un aburrido caudal de incidentes de fácil solución técnica, y que serían de fácil solución a secas si no fuera por el hecho de que más o menos la mitad de ellos contradice lo que indica la otra mitad.
Así que una horda de programadores armada con papel secante intenta contener ese río, resuelve uno y otro y alterna entre “A” y “no A” en un proceso que arroja tanta agua como recoge.
Hay otro caudal que lo alimenta, un caudal de programadores, analistas, managers y demás sacos de carne “recursos” que van pasando, pasando y pasando a ritmo creciente. Si no fuera por este otro caudal que aporta toda la energía desperdiciada en aquél ida y vuelta inútil el proceso descrito sería el santo grial del movimiento continuo.
Los gerentes, como esto es importante, están en contacto con “el equipo”, los conocen, interactúan con ellos más que con el resto. Es a esto a lo que se le llama “visibilidad” y que consiste en que, de vez en cuando, se abre un espacio en esos cielos y asciende alguno, (probablemente aquél con más habilidad para el alpinismo que para la programación) dejando al proyecto, que es realmente importante, en manos del resto que ahí queda, papel secante en mano (mientras el alpinista saluda desde lo alto).
Así es que, gracias a la “visibilidad” del proyecto los otros reman, reman y reman hasta que, hartos y sin esperanza, abandonan en busca de algún horizonte en donde sus entrenados brazos sean mejor recibidos. Aquellos en los que el entrenamiento no hace efecto y que por lo tanto carecen de otras expectativas (que los hay), esperan pacientemente su turno para el ascenso.
Unos y otros son reemplazados velozmente, así que eso que llamamos “equipo” (y que no es más que un fotograma de una película interminable) apenas puede acumular una muy vaga idea del negocio que el sistema viene a sostener, ya que el escaso conocimiento penosamente adquirido por prueba y error se va tan rápido como se acumula. Del negocio se sabe que es grande y millonario y que no es una verdulería ni un almacén de ramos generales, pero eso no alcanza para dividir las aguas y tomar la mitad de correcciones que corresponde para parar la rueda.
En fin… cualquiera que trabaje en sistemas un tiempo es expuesto a una de las más generosas fuentes de fina ironía que esta vida puede dar: el trabajo y, sobre todo, el trabajo en sistemas y, sobre todo, el trabajo “corporativo”. Si este tiempo es más o menos largo el sistémico, si sobrevive y lo sigue siendo (o mejor dicho, para sobrevivir y seguir siéndolo) habrá desarrollado un fino sentido de la ironía y del humor, o un cinismo a toda prueba, o todo eso junto. Por suerte algunos explotan genialmente esos efectos colaterales esas habilidades, y así tenemos a Dilbert (adivinen de dónde sacó Scott Adams la inspiración para sus personajes), o a Sinergia Sin Control, o al mayor repositorio de esfuerzo sin sentido y desperdicio de inteligencia jamás creado, TDWTF.
Porque lo más irónico de todo es que funciona y que buena parte de nosotros hemos vivido o viviremos de ello, y que es lo que termina financiando los juguetes (lenguajes, metodologías, herramientas, patrones, arquitectura) con los que nos entretenemos mientras, distraídamente, hacemos girar la rueda. En fin…
…que al mundo nada le importa
yira…
yira…
Una de las vetas más productivas que encontramos al utilizar los Text Templates en Visual Studio 2010 tiene que ver con la posibilidad de acceder al modelo de objetos de Visual Studio (EnvDte).
Este modelo de objetos nos permite analizar e incluso modificar no sólo la estructura de archivos del proyecto que estamos construyendo (archivos y proyectos incluidos, referencias, propiedades…) sino también del código (namespaces, clases, propiedades, métodos, variables…).
Cierto es que es algo que ya podíamos hacer programando un plug-in o un diseñador, pero estas opciones conllevan cierta complejidad. Veremos, a través de este ejemplo, que el uso de T4 para las tareas que antes requerían ese tipo de soluciones simplifica mucho las cosas, si bien el resultado es un poco menos… elegante (que un diseñador, por ejemplo).
El ejemplo consiste en crear una plantilla T4 que recorra los archivos javascript de un proyecto y cree y agregue versiones optimizadas de ellos utilizando la librería Microsoft Ajax Minifier. Empecemos.
Como ésta es una plantilla que podríamos reutilizar en varios proyectos, vamos a crear una preprocesada (Preprocessed Text Template) y ubicarla en una librería separada que luego podamos distribuir.
Así que comenzamos creando una solución de prueba (“T4Sample”), agregando un proyecto de tipo Class Library (“JavascriptCruncher”) y un proyecto de tipo Web Application (“SampleWebApplication”). En el proyecto de librería insertamos un nuevo ítem de tipo “Preprocessed Text Template” (“JavascriptCruncherTemplate”) y una clase en la que ubicaremos los métodos auxiliares que vayamos necesitando (“EnvDteHelper”). La solución debería quedar así:
El disparador de todo esto ha sido un excelente post de Oleg Sych, al que le vamos a pedir prestado el código para obtener la referencia al modelo de objetos de Visual Studio. Ubicaremos este código en nuestra clase auxiliar, como un método estático:
using System; using System.Collections.Generic; using System.IO; using EnvDTE; using Microsoft.VisualStudio.TextTemplating; namespace JavascriptCruncher { public class EnvDteHelper { public static Project GetProject(ITextTemplatingEngineHost host) { IServiceProvider hostServiceProvider = (IServiceProvider)host; if (hostServiceProvider == null) throw new Exception("Host property returned unexpected value (null)."); DTE dte = (DTE)hostServiceProvider.GetService(typeof(DTE)); if (dte == null) throw new Exception("Unable to retrieve EnvDTE.DTE"); Array activeSolutionProjects = (Array)dte.ActiveSolutionProjects; if (activeSolutionProjects == null) throw new Exception("DTE.ActiveSolutionProjects returned null."); Project dteProject = (Project)activeSolutionProjects.GetValue(0); if (dteProject == null) throw new Exception("DTE.ActiveSolutionProjects returned null."); return dteProject; } } }
Para que lo anterior funcione necesitamos referencias a las librerías “EnvDte” y “Microsoft.VisualStudio.TextTemplating.Interfaces.10.0”. Esta última (creo) no se incluye en la distribución del VS 2010. Si no la encuentran, pueden obtenerla bajándose el Visual Studio 2010 SDK.
En el paso anterior obtuvimos una referencia al proyecto en donde corre el template. El próximo paso es obtener una lista de todos los objetos ProjectItem (una interfaz que representa cada archivo y carpeta en el proyecto) que corresponden a archivos javascript.
El proyecto está representado como una jerarquía de ProjectItems que debemos recorrer recursivamente. Necesitamos todos los archivos “.js”, pero excluyendo aquellos “.min.js”, entendiendo que éstos ya están optimizados (“minificados” podríamos decir maltraduciendo “minified”).
Agregamos entonces las siguientes funciones a nuestra clase EnvDteHelper.
public static List<projectitem> GetJsProjectItems(Project project) { List<projectitem> jsProjectItems = new List<projectitem>(); foreach (ProjectItem projectItem in project.ProjectItems) { GetJsProjectItems(projectItem, jsProjectItems); if (projectItem.Name.EndsWith(".js") && !projectItem.Name.EndsWith(".min.js")) jsProjectItems.Add(projectItem); } return jsProjectItems; } private static void GetJsProjectItems(ProjectItem parentProjectItem, List<projectitem> jsProjectItems) { foreach (ProjectItem projectItem in parentProjectItem.ProjectItems) { GetJsProjectItems(projectItem, jsProjectItems); if (projectItem.Name.EndsWith(".js") && !projectItem.Name.EndsWith(".min.js")) jsProjectItems.Add(projectItem); } }
Este es el momento (si no lo han hecho ya) de bajar e instalar el Microsoft Ajax Minifier y agregar una referencia a la librería ajaxmin.dll en el proyecto (JavascriptCruncher).
Ésta librería es muy simple. La clase ScriptCruncher contiene el método “Crunch”, que recibe el código javascript a optimizar (minificar) y una instancia de la clase CodeSettings con las opciones deseadas y devuelve el código minificado como una cadena.
Combinando lo visto en el punto anterior con esta librería, podemos comenzar a escribir el template propiamente dicho. Sería algo así (lo siguiente no es el template completo, sólo un ejemplo del avance hasta ahora):
EnvDTE.Project project = EnvDteHelper.GetProject(this.Host); List<envdte.projectitem> jsProjectItems = EnvDteHelper.GetJsProjectItems(project); ScriptCruncher cruncher = new ScriptCruncher(); CodeSettings crunchSettings = new CodeSettings(); crunchSettings.CollapseToLiteral = true; crunchSettings.LocalRenaming = LocalRenaming.CrunchAll; crunchSettings.StripDebugStatements=true; foreach( EnvDTE.ProjectItem item in jsProjectItems) { string itemFileName = item.FileNames[0]; string jsCode = File.ReadAllText(itemFileName); string jsMinified = cruncher.Crunch(jsCode, crunchSettings); this.WriteLine(itemFileName); this.WriteLine(jsMinified); this.WriteLine("--------------------"); }
El ejemplo anterior solamente muestra el código minificado. Pero tenemos que…
Vamos a recurrir otra vez al bueno de Oleg Sych para ver cómo se lleva a cabo esta tarea. Mirando un poco sobre el código de su ejemplo llegamos a que agregar un archivo es tan simple como utilizar el método AddFromFile de la colección ProjectItems del objeto ProjectItem correspondiente al archivo javascript con el código original.
Como se ve, le pasamos el ProjectItem del archivo javascript original -que utilizará como referencia- y el código minificado. Lo único que hace es cambiar la extensión, grabar (o sobreescribir) el archivo minificado existente y agregarlo al proyecto (si es que no estaba ya agregado). Ubicamos este nuevo helper junto a los demás en EnvDteHelper
public static void SaveMinifiedCode(ProjectItem originalJsItem, string minifiedCode) { string outputFileName = Path.ChangeExtension(originalJsItem.FileNames[0], ".min.js"); File.WriteAllText(outputFileName, minifiedCode); ProjectItem parentProjectItem = originalJsItem.Properties.Parent; parentProjectItem.ProjectItems.AddFromFile(outputFileName); }
Hasta ahora venimos trabajando sobre nuestra clase EnvDteHelper. Suelo recomendar esto ya que, por ahora, el soporte de Intellisense en las plantillas es bastante limitado. Es mucho más fácil, entonces, trabajar dentro de lo posible en estos helpers, que son clases comunes y silvestres, para luego juntar todo en el template en un último paso.
Una vez terminado el trabajo, la plantilla (“JavascriptCruncherTemplate.tt”) debe quedar así:
<#@ template language="C#" hostSpecific="true" #> <#@ import namespace="System.Collections.Generic" #> <#@ import namespace="Microsoft.Ajax.Utilities" #> <#@ import namespace="System.IO" #> <# EnvDTE.Project project = EnvDteHelper.GetProject(this.Host); List<EnvDTE.ProjectItem> jsProjectItems = EnvDteHelper.GetJsProjectItems(project); ScriptCruncher cruncher = new ScriptCruncher(); CodeSettings crunchSettings = new CodeSettings(); crunchSettings.CollapseToLiteral = true; crunchSettings.LocalRenaming = LocalRenaming.CrunchAll; crunchSettings.StripDebugStatements=true; foreach( EnvDTE.ProjectItem item in jsProjectItems) { string itemFileName = item.FileNames[0]; string jsCode = File.ReadAllText(itemFileName); string jsMinified = cruncher.Crunch(jsCode, crunchSettings); EnvDteHelper.SaveMinifiedCode(item, jsMinified); this.WriteLine( "Processed: {0}", itemFileName); } this.WriteLine("done!"); #>
Para destacar: noten, en el tag inicial de la plantilla, que se especifica la opción “hostSpecific=true”. Esto es importante ya que le indica al generador que la clase resultante debe tener la propiedad Host. Si no lo hacemos obtendremos un error de compilación luego (¡me costó un rato darme cuenta de eso!). Vean también la declaración de los “import” (el equivalente a “using” en los templates).
El resto del código es simple dadas las herramientas que nos construimos:
Nuestra librería JavascriptCruncher está lista para ser utilizada en un proyecto de prueba. ¿Cómo la referenciamos?
Lo que debemos hacer es crear una plantilla en cada proyecto en el que querramos utilizar nuestro JavascriptCruncherTemplate que haga referencia a éste.
Hice un proyecto de prueba muy tonto, que tiene dos páginas iguales, una en la carpeta principal y otro en una subcarpeta (para verificar que el template genere ambos archivos). También agregué, en la carpeta principal un elemento de tipo “Text Template” (JavascriptCruncher.tt) que hará referencia al template precompilado que acabamos de crear. La estructura, en resumen, queda así:
JavascriptCruncher.tt referencia al template con este código:
<#@ template debug="false" hostspecific="true" language="C#" #> <#@ output extension=".log" #> <#@ assembly name="JavascriptCruncher" #> <#@ import namespace="JavascriptCruncher" #> <# JavascriptCruncherTemplate t = new JavascriptCruncherTemplate(); t.Host = this.Host; this.Write(t.TransformText()); #>
Noten que simplemente crea una instancia de JavascriptCruncherTemplate, establece la propiedad host (recuerden nuevamente que es importante la propiedad hostspecific="true" de la primera línea) e invoca al método TransformText.
La línea en JavascriptCruncher.tt de nuestro proyecto de prueba que hace referencia a la librería de templates que acabamos de crear es la que dice:
<#@ assembly name="JavascriptCruncher" #>
Puedo hacerlo de esta manera -sin indicar el path a la dll- ya que previamente incluí a JavascriptCruncher.dll y ajaxmin.dll en la GAC.
Hay otras opciones, como indica Oleg Sych en Understanding T4: <#@ assembly #> directive, pero la realidad es que por uno u otro motivo no pude hacer funcionar ninguna de ellas limpiamente, así que yo recomiendo ésta de agregar la librería a la GAC, ustedes experimenten por su cuenta y después me dicen.
Para esto primero hay que firmar JavascriptCruncher.dll, lo que es muy sencillo. En propiedades del proyecto vamos a la solapa “Signing”. Indicamos que firme el ensamblado y elegimos un nombre de archivo, más una password (opcional):
Tenemos que compilar y luego utilizar gacutil para registrar las dos librerías (la otra es ajaxmin.dll que es referenciada por la nuestra, y que tampoco está en la gac). En la línea de comandos de Visual Studio 2010 (“Menú de Inicio / Todos los programas / Microsoft Visual Studio 2010 / Visual Studio Tools / Visual Studio Command Prompt (2010)”):
gacutil -i "[path completo]\JavascriptCruncher.dll"
y no nos olvidemos de ajaxmin.dll, que también deberá estar en la GAC:
gacutil -i "[path completo]\ajaxmin.dll"
Si corremos el template (grabándolo o utilizando la opción “Run Custom Tool” del menú contextual que aparece al hacer click con el botón de la derecha sobre el template) veremos cómo se agregan los archivos javascript minificados:
Pueden descargarse el código fuente de este ejemplo desde el repositorio de desdesarrollo en google code (no se olviden de registrar las librerías en la GAC).
Son 100 excelentes publicidades gráficas que apelan a imágenes chocantes para llamar la atención sobre el hambre, el cambio climático, la prostitución, abuso infantil, abuso de drogras y tabaco, entre otros graves flagelos.
Algunas de mis preferidas:
Éstas y el resto de las imágenes en resolución original, en la entrada de Le Blog de Bango.
(Gracias a @Cerebrado).
Creo que hace ya un par de semanas que ha salido Visual Studio 2010 RC. En el trabajo comenzamos a prepararnos hace ya un tiempo, utilizando la beta, y recién ayer actualizamos a esta última versión.
Mi percepción es que está sobre todo más estable. Han habido más correcciones que cambios, lo que es esperable dado el grado de madurez del producto. Por lo que hizo a mi trabajo en estas semanas (.Net MVC, mucho $(javascript), templates) noté muchas mejoras en el intellisense y la depuración en javascript -que hasta ahora era imposible de utilizar por lo limitado- aunque sigue sin ser la gran cosa comparado con FireBug, mi caballito de batalla.
Pero volvamos al tema del título. Las plantillas T4 para generación de código. De las novedades del 2010, ésta es la que más impacto real (y positivo) ha tenido en los proyectos, automatizando infinidad de pequeñas tareas tediosas y repetitivas.
Dije “pequeñas” porque, para las “grandes” tareas automáticas y repetitivas (generación de entidades, table gateways, row gateways, manejo de recursos de texto, etc.) teníamos ya infinidad de opciones, desde herramientas propias de .Net (como los recursos incrustados o los archivos .resx con sus clases asociadas), frameworks (Entity Framework, NHibernate), diseñadores (como los clásicos diseñadores de Datasets), plugins (no soy de utilizarlos, así que ni ejemplos puedo dar), hasta otros sistemas de generación automática de código, muy similares a T4 (CodeSmith).
Pero el tema con las herramientas mencionadas es que requieren mantenimiento, instalación, depuración, y una inevitable y nada despreciable curva de aprendizaje… pero sobre todo, que son pesadas y abarcativas (para utilizarlas razonablemente tenemos que seguir su paradigma, adaptando nuestro esquema desarrollo a éste en mayor o menor medida). Son muy buenas herramientas, pero nadie va a meterse con CodeSmith (sólo por dar un ejemplo), si no lo está ya utilizando para otra cosa, sólo para actualizar una lista de constantes o una enumeración de ciertos recursos.
Las ventajas de T4 en el Visual Studio 2010 tienen que ver justamente con ésto: están integradas al entorno de desarrollo y su sintaxis es muy intuitiva (a cualquier desarrollador le alcanzará con saber que “es como hacer una página .aspx” para empezar), perfectas para automatizar esas pequeñas, molestas, y usualmente numerosas tareas de copiar, pegar y modificar.
No voy a seguir con mucho detalle, ya que hay mucho (y mejor) escrito sobre el tema. Pero sí los voy a dejar con algunas recomendaciones de punto de partida:
T4: Text Template Transformation Toolkit: este post de Oleg Sych es un muy buen lugar para arrancar velozmente, y tiene además su propia (y extensa) lista de recomendaciones.
La documentación en MSDN es enrevesada y difícil de seguir, como toda la de Microsoft, pero está bueno pegarle una leída, no para empezar sino para conocer más en detalle.
Punto aparte y mención especial para este otro post de Oleg Sych, en el que hace referencia desde el template al modelo de objetos de Visual Studio (EnvDTE) que abre la puerta a posibilidades realmente interesantes.
Mmmm… no parece la gran cosa. Pero eso es porque no lo han visto en su página original.
La verdad que un video así los hace merecedores de una compra al por mayor.
Visto en FayerWayer.
Finalizamos el post anterior de esta serie con la infraestructura necesaria para atrapar en javascript los errores y excepciones que puedan producirse del lado del servidor y “canalizarlos” hacia tipos conocidos con una codificación similar a la que utilizamos en c#.
Recordemos el ejemplo final. Teníamos, en javascript, una excepción base:
ExceptionBase = function (type, message) { this.Type = type; this.Message = message; };
De la que luego derivamos dos excepciones más específicas, una para excepciones de negocio (la que arrojaría nuestro código en el caso de que una operación supere cierto monto permitido, por ejemplo):
ProgramException = function (message, netException) { this.Reasons = []; this.NetException = netException; if (typeof message != "undefined" && message != null) this.Message = message; }; ProgramException.prototype = new ExceptionBase("ProgramException", "Operation Error");
y la otra para errores “fatales”, es decir todos aquellos que indican que la aplicación ha arribado a un estado no contemplado y que por lo tanto no puede seguir utilizándose (que ha cascado, vamos):
FatalException = function (message, netException) { this.NetException = netException; if (typeof message != "undefined" && message != null) this.Message = message; } FatalException.prototype = new ExceptionBase("FatalException", "Unexpected Error");
Para ver cómo utilizamos estas clases veamos nuestro ejemplo de llamada $.ajax, donde se establece la diferencia entre errores de negocio y fatales, lanzándose una excepción u otra dependiendo el caso. Más concretamente la sección “success” (lo siguiente es sólo el fragmento correspondiente a “success” dentro de la llamada a $.ajax):
//... (etc) .... success: function (response, status, xhr) { //Exception handling. var responseStatus = xhr.getResponseHeader("RESPONSE_STATUS"); var ex = null; if (responseStatus == "ApplicationException") { var netEx = JSON.parse(response); ex = new ProgramException(netEx.Message, netEx); ex.Reasons = netEx.Reasons; } else if (responseStatus == "UnexpectedException") { var netEx = JSON.parse(response); ex = new FatalException(netEx.Message, netEx); } if(ex!=null) { if (async) ShowException(ex) else throw ex; } //...(continúa)...
Esta infraestructura de excepción base y derivadas sería una complejidad decorativa y sin sentido si no tomamos, en algún lugar del código, una decisión en base a ese tipo que tanto esfuerzo nos lleva determinar.
La diferencia entre una excepción y otra, desde el punto de vista del front-end, radica en cómo se le presenta al usuario. En nuestro ejemplo, esa tarea le corresponde a la función ShowException:
ShowException = function (exception) { //non-fatal exceptions if (exception.Type == "ProgramException") { var $messageBox = GetMessageBox(); $messageBox.find("#MessageBoxReasons").html(exception.Reasons.join( "<p/>" )); var buttons = {}; $messageBox.dialog({ autoOpen: true, modal: true, buttons: { "Ok": function () { $messageBox.dialog("destroy"); } }, closeOnEscape: true, title: exception.Message, close: function () { $messageBox.dialog("destroy"); } }); } //fatal exceptions. else if (exception.Type == "FatalException") { var displayHtml = "<h1>" + exception.Message + "</h1>"; var doc = window.top.document; doc.open(); doc.write(displayHtml); doc.close(); } }
Esta función simplemente evalúa el tipo de excepción que recibe y toma las decisiones necesarias. En este ejemplo, muy simple y esquemático, tenemos dos secciones: la primera parte del if crea una ventana utilizando $.dialog para mostrar prolijamente las excepciones de negocio, y la segunda parte (luego del else) “rompe” la pantalla, limpiando todo el html y dejando solamente el mensaje de error.
La función auxiliar “GetMessageBox” simplemente construye el html necesario para mostrar el cuadro de diálogo:
GetMessageBox = function () { var top$ = window.top.$; var $messageBox = top$("#MessageBoxContainer"); if ($messageBox.length > 0) return $messageBox; var $messageBoxContainer = top$("<div />").appendTo(top$("body")); $messageBoxContainer.attr("title", ""); $messageBoxContainer.css("display", "none"); var $innerTable = top$("<table/>").appendTo($messageBoxContainer); $innerTable.css("width", "100%"); var $innerTableFirstRow = top$("<tr/>").appendTo($messageBoxContainer); var $innerTableFirstRowFirstCell = top$("<td/>").appendTo($innerTableFirstRow); var $messageBoxImage = top$("<img/>").appendTo($innerTableFirstRowFirstCell); $messageBoxImage.attr("alt", ""); $messageBoxImage.attr("src", ""); //TODO: set src. var $innerTableFirstRowSecondCell = top$("<td/>").appendTo($innerTableFirstRow); $innerTableFirstRowSecondCell.addClass("messageBoxMessage"); var $messageBoxMessage = top$("<div/>").appendTo($innerTableFirstRowSecondCell); $messageBoxMessage.attr("id", "MessageBoxMessage"); var $innerTableSecondRow = top$("<tr/>").appendTo($innerTable); var $innerTableSecondRowFirstCell = top$("<td/>").appendTo($innerTableSecondRow); $innerTableSecondRowFirstCell.attr("colspan", "2"); $innerTableSecondRowFirstCell.addClass("messageBoxDescription"); var $messageBoxDescription = top$("<div/>").appendTo($innerTableSecondRowFirstCell); $messageBoxDescription.attr("id", "MessageBoxDescription"); var $innerTableThirdRow = top$("<tr/>").appendTo($innerTable); var $innerTableThirdRowFirstCell = top$("<td/>").appendTo($innerTableThirdRow); $innerTableThirdRowFirstCell.attr("colspan", "2"); $innerTableThirdRowFirstCell.addClass("messageBoxReasons"); var $messageBoxReasons = top$("<div/>").appendTo($innerTableThirdRowFirstCell); $messageBoxReasons.attr("id", "MessageBoxReasons"); return $messageBoxContainer; };
Notarán que en la sección en la que se muestra el error fatal no se utiliza jQuery. En estas situaciones tenemos que evitar cualquier referencia externa al código que se está ejecutando, ya que sólo sabemos que ha ocurrido un error inesperado y no conocemos el estado de los demás componentes de la aplicación (como por ejemplo el plugin de jQuery. El error podría deberse a que el cliente no pudo descargar el script de jQuery). Aquí tenemos que esforzarnos en codificar, siempre dentro de lo posible, código que funcione en circunstancias extremas… y la mejor manera es que sea extremadamente simple.
Hasta aquí hemos abarcado las situaciones más comunes, y sólo en el marco de llamadas $.ajax al servidor:
Parece una estructura demasiado compleja para, finalmente, hacer un “if” y determinar si rompemos la pantalla o mostramos un cuadro de diálogo, cubriendo apenas los dos puntos de arriba. Es un buen momento para repasar sus ventajas:
Por otro lado hay mucho para explorar de aquí en más. Recordemos que ni siquiera cubrimos los casos mínimos para una aplicación “aceptable”. Veremos que al contemplar más situaciones (errores y excepciones de lado del cliente, en la comunicación, etc.) y al integrar más funcionalidad (por ejemplo mostrando un mensaje de error especial en ambientes de desarrollo o testing), en el manejo de “pequeños detalles” y casos especiales, será cuando este esquema muestre sus verdaderas ventajas.