Koalite
Automatizar la compilación con MSBuild
Mayo 20th, 2013
Cuando en mi anterior post explicaba las ventajas de usar un servidor de integración continua, decía que el primer paso para mejorar el proceso de integración es automatizar el proceso de compilación. En este post voy a explicar con un ejemplo sencillo cómo podemos automatizar este proceso usando MSBuild.
Para este ejemplo vamos a tener una solución de Visual Studio con dos proyectos: nuestra aplicación y un proyecto de tests, y nuestro script se va a encargar de realizar los siguientes pasos:
- Limpiar los resultados de ejecuciones anteriores.
- Compilar la solución.
- Ejecutar los tests.
- Generar un paquete NuGet con el resultado.
El objetivo será que alguien que se baje el proyecto desde el control de código fuente pueda realizar todos estos pasos ejecutando un único comando, sin necesidad de instalar manualmente ningún componente adicional en la máquina (más allá el SDK de .NET). De esta forma conseguiremos que la compilación siempre se realice en un entorno controlado, con versiones adecuadas de cada dependencia y sin posibilidad de introducir errores en el proceso por saltarnos algún paso manual.
Estructura de Carpetas
Cuando diseñamos un script de estas características, es importante definir una estructura de carpetas razonable. Esto, además de ayudarnos a crear el script, nos permitirá adaptarlo fácilmente a nuevos proyectos siempre que mantengamos la misma estructura de carpetas.
La estructura de carpetas que vamos a utilizar es la siguiente:
La carpeta src será la carpeta donde se encontrará la solución de Visual Studio y los paquetes NuGet utilizados. Toda la gestión de paquetes NuGet la haremos desde Visual Studio, donde además de instalarlos deberemos hablitar la opción de Restaurar Paquetes NuGet (Enable NuGet Package Restore). Podríamos hacerlo desde el propio script de compilación usando NuGet por línea de comandos, pero resulta más cómodo hacerlo desde Visual Studio.
En la carpeta build generemos los archivos compilados (.exes y .dlls) y la usaremos como carpeta de trabajo durante la compilación.
La carpeta results contendrá en resultado final del proceso. En nuestro caso será un paquete NuGet y un fichero xml con los resultados de la ejecución de los tests.
En la carpeta raíz del proyecto tendremos el script de compilación propiamente dicho (sample.build) y un fichero build.cmd para lanzar el proceso cómodamente.
MSBuild: Targets y Tasks
MSBuild utiliza archivos XML para configurar el proceso de compilación. Estos ficheros se organizan alrededor del concepto de Targets. Un Target representa un proceso que se puede invocar de forma independiente y que, a su vez, puede depender de otros procesos.
En nuestro ejemplo, cada una de las fases de la compilación (limpiar, compilar, empaquetar, etc.) será un Target. Además crearemos un Target que dependa de todos los demás de manera que al invocarlo se ejecute el proceso completo.
La estructura del fichero incluyendo sólo los Targets es la siguiente:
... ... ... ...
Para invocar un Target concreto por línea de comandos, debemos usar el parámetro /t. Si no indicamos nada, se ejecutará el Target por defecto (en este caso Default). Esto nos permite probar el script paso a paso:
msbuild.exe sample.build ← Esto ejecuta el target por defecto msbuild.exe sample.bulid /t:RunTests ← Esto sólo ejecutaría el target RunTests
Para definir las acciones a realizar en cada Target se utilizan las Tasks. Las Tasks son operaciones que nos ofrece MSBuild para manejar archivos y directorios, compilar proyectos, ejecutar comandos externos, etc. Además de la que incluye MSBuild puedes crear tus propias Tasks o usar alguna de las librerías existentes, como MSBuild Extension Pack o MSBuild Community Tasks).
Al crear el script podemos definir propiedades y grupos de ficheros usando los elementos Property e ItemGroup. Las propiedades funcionan de manera similar a las variables, y podemos utilizarlas para almacenar la ruta hasta ficheros, parámetros de compilación, etc. Los grupos de ficheros sirven, como era de esperar, para indicar un conjunto de ficheros que luego podremos referenciar en las Tasks.
Aunque no voy a entrar en detalles para no alargar mucho el post, veamos cómo se implementa uno de los pasos: la ejecución de los tests:
$(MSBuildProjectDirectory)\build\ $(MSBuildProjectDirectory)\results\ $(MSBuildProjectDirectory)\src\packages\NUnit.Runners.2.6.2\tools\nunit-console.exe
En el PropertyGroup declaramos una serie de propieades con la ruta hasta el ejecutable de NUnit-Console.exe que se encargará de lanzar los tests, la carpeta donde se encuentran los ficheros compilados y la carpeta donde se almacenan el resultado de los tests.
El Target RunTests utiliza la Task Exec para ejecutar NUnit-Console.exe pasándole como parámetros el assembly con los tests e indicando como carpeta de trabajo la carpeta de resultados. Como podéis ver, podemos usar string interpolation con las propiedades que hemos definido antes para construir los argumentos de la Task.
En el script de compilación completo podéis ver cómo se implementan el resto de Targets.
Resumen
Espero que este post haya servidor para desmitificar un poco el uso de MSBuild, podéis encontrar el código completo del proyecto en mi cuenta de github para jugar con él y ver cómo funciona.
Es importante tener una buena estructura de carpetas que podamos reutilizar de proyecto a proyecto, porque eso nos ayudará también a reutilizar el propio script de compilación. Lo normal es acabar con un script bastante genérico que luego se pueda adaptar a otros proyectos mediante la definición de propiedades y nuevos targets.
MSBuild es una herramienta muy potente y con él podemos automatizar muchas más cosas de las que se ven en este ejemplo. El mayor inconveniente que tiene (para mi gusto) es el uso de XML, que hace que resulte todo un poco más lioso, pero una vez que te acostumbras merece la pena.
Posts relacionados:
» Leer más, comentarios, etc...
Buayacorp
HTML5 Template Tag: la novedad
Mayo 17th, 2013
Para solventar el uso de templates, personalmente uso y me encanta Mustache, ya que tiene El 7 de mayo, se publicó el borrador de la etiqueta template de HTML5, Y se puede hacer algo como esto:
id="template-row">class=""> </template>
var t = document.querySelector("#tabla tbody"), row = document.getElementById("template-row"); var td = row.getElementsByTagName("td"); td[0].textContent = "1"; td[1].textContent = "Nombres"; td[2].textContent = "Apellidos"; t.appendChild(row.content.cloneNode(true));
Para tener en cuenta:
- El código dentro de
templateno es mostrado por el navegador. - El código dentro de
templateno es parte del documento, es decir quedocument.getElementById('#template-row');no tiene hijos. - Los templates son inactivos hasta que son usados, es decir, que las imágenes no se cargan, archivos multimedia no son reproducidos y scripts no son ejecutados.
Visto en Frontend.pe.
» Leer más, comentarios, etc...
El blog de pico.dev
Copia de seguridad con rsync
Mayo 17th, 2013

Una vez que tener copias de seguridad nos parece importante y tenemos respondida la pregunta de por qué, se nos plantean algunas otras preguntas:
- ¿De que hacer copias de seguridad?
- ¿Cada cuanto hacer copias de seguridad?
- ¿Que características han de tener las copias de seguridad?
- ¿En que medio guardar las copias?
- ¿Como hacer las copias de seguridad?
- Y tan importantes como las anteriores, ¿como recuperar los datos?
Yo hasta ahora no usaba ninguna herramienta usaba el simple y manual método de copiar y pegar archivos del disco duro del ordenador a un disco duro externo. Cuando no tenía disco duro externo las copias de seguridad las hacía en CD y DVD pero hacerlas en un medio óptico como los anteriores no los recomiendo porque son lentos de realizar, en estos días no tienen capacidad suficiente para guardar todo y por tanto deberemos tener varios volúmenes y finalmente lo más importante porque son unos medios poco fiables. Dado el precio y capacidad que tienen hoy en día los discos duros USB externos además de su velocidad de transferencia es más recomendable esta opción y es la que a día de hoy uso. Pero aún con un disco duro externo el método de copiar y pegar tarda un buen tiempo y sigue siendo lento, con 70 GiB de archivos personales que no quiero perder, pero también el hecho de hacerlo manualmente es propenso a que algún día se me olvide hacer la copia de seguridad de todo y por la ley de Murphy ese día fallará el ordenador. Con lo que con esta situación he empezado a buscar alternativas a mi método manual de copiar y pegar.
Empece probando Déjà Dup pero no me ha convencido porque las copias de seguridad las guarda en un formato comprimido (y cifrado si se quiere) de modo que para recuperar los archivos se ha de utilizar la misma herramienta. Para mi una de las características de la copia de seguridad es que siga pudiendo tener accesibles los archivos independientemente de la herramienta. Viendo las posibilidades de los programas de copia de seguridad disponibles en Linux por la que me he decantado ha sido rsync. Es un método simple que mantiene sincronizados el contenido de dos carpetas, copiando los nuevos archivos, borrando los eliminados y solo actualizando los necesarios además de poder hacerse las copias entre dos máquinas diferentes a través de la red. Mediante rsync para hacer una copia de seguridad ahora tardo mucho menos ya que solo se copian los archivos que hayan cambiado además de estar automatizado con un script con lo que resulta más fácil y por ello se pueden hacer más a menudo.
Con el siguiente comando de rsync se puede hacer una copia de seguridad de una carpeta a otra:
La opción -a es una meta opción de utilidad que engloba otras con las más comunes para hacer copias de seguridad, -P hace que se muestre el progreso, --delete borra los archivos en la carpeta de destino que se hayan borrado en la carpeta origen.
Otro ejemplo para copiar varias carpetas de la carpeta home de un usuario es, con la opción --files-from, hay que indicar también la opción -r para que las carpetas se copien de forma recursiva:
Este simple comando ahorra mucho tiempo ya que con él no hay que copiar los XXX GiB cada vez que hay que hacer un copia de seguridad, solo se copiará lo que haya cambiado entre la copia de seguridad y el origen, que muy probablemente es una cantidad mucho menor que esos XXX GiB. Con lo simple que es se me hace extraño que haya pasado tanto tiempo haciendo las copias de seguridad manualmente.
Si quieres algunas otros ejemplos de uso de rsync como por ejemplo el como programar las copias de seguridad con cron o quieres conocer otras herramientas en la wiki de Arch Linux tienes unas cuantas más, algunas con interfaz gráfica.
Referencia:
https://wiki.archlinux.org/index.php/Backup_Programs
https://wiki.archlinux.org/index.php/Rsync
» Leer más, comentarios, etc...
Buayacorp
Nginx + PHP FPM + PHP APC + Memcached + Batcache
Mayo 14th, 2013
Finalmente dedique un poco de tiempo para reducir el consumo de CPU que estaban causando ciertos spammers, especialmente hace un par de dias. Por el momento se ve bastante bien.
» Leer más, comentarios, etc...
Buayacorp
A Facebook update in real life
Mayo 14th, 2013
» Leer más, comentarios, etc...
Variable not found
Unity 3.0, con soporte oficial para ASP.NET MVC
Mayo 14th, 2013
Hasta ahora, los desarrolladores que queríamos usar Unity con ASP.NET MVC teníamos que recurrir a triquiñuelas o componentes no oficiales, como Unity.MVC3. No es que fuera especialmente incómodo ni que tuviera contraindicaciones, pero la verdad es que no dejaba de resultar curioso que no existieran adaptadores específicos “oficiales” para tecnologías tan difundidas como ASP.NET MVC, o WebAPI.
Pues bien, desde hace unas semanas tenemos disponible la versión 3.0 de Unity, que ha venido acompañando también a la reluciente versión 6 de la Enterprise Library. Como sabemos, esta creación del equipo de Patterns & Practices de Microsoft contiene un conjunto de componentes reutilizables llamados “application blocks” que resuelven problemáticas comunes en el desarrollo de sistemas, como logging, tracing, acceso a datos, gestión de excepciones y otras, incluyendo por supuesto inyección de dependencias e inversión de control.
En este post vamos a ver cómo podemos usar fácilmente esta nueva versión de Unity en nuestras aplicaciones ASP.NET MVC y algunas de las novedades que ofrece para este tipo de sistemas.
1. Instalación
Como es habitual, la instalación del componente la realizaremos usando Nuget:PM> Install-Package unity.mvc
Esta acción instalará varios paquetes dependientes y creará un par de archivos en la carpeta App_Start: UnityConfig.cs y UnityMvcActivator.cs.En el primero de ellos encontramos la configuración de contenedor, es decir, es el punto en el que registraremos nuestras clases e interfaces para posteriormente usarlas en la resolución de dependencias. En UnityMvcActivator.cs, en cambio, se encuentra el código de inicialización específico de la integración para ASP.NET MVC. Más adelante veremos para qué son útiles y qué tenemos que tocar en ambos archivos.
Por comodidad, no tendremos que incluir en el global.asax.cs código para inicializar el contenedor. El mismo paquete Unity.Mvc incluye automáticamente una llamada a
UnityWebActivator.Start() durante el proceso de inicialización utilizando el componente WebActivatorEx, una útil herramienta que permite introducir módulos de forma dinámica, durante el arranque.2. Registro de componentes
El registro de interfaces y clases la realizaremos como siempre, sobre el contenedor Unity. Para ello, sólo tenemos que dirigirnos hacia el archivo UnityConfig.cs e implementarlo de la misma forma que siempre:public static void RegisterTypes(IUnityContainer container)
{
// NOTE: To load from web.config uncomment the line below. [...]
// container.LoadConfiguration();
// TODO: Register your types here
// container.RegisterType<IProductRepository, ProductRepository>();
container.RegisterType<IProductServices, ProductServices>();
}
Por supuesto, podríamos incluir tantos pares interfaz-clase como sean necesarios para nuestros propósitos.3. Instancias per request
Ya he comentado muchísimas veces por aquí la importancia de hacer que los componentes que implementenIDisposable sean liberados al finalizar la misma. De no ser así, estaríamos creando una bomba de relojería: si no se liberan componentes, en cada petición podemos estar perdiendo memoria o recursos, y eso, a la larga, ya os digo que no es una historia con final feliz ;-)La nueva versión de Unity y su componente de integración con MVC ha simplificado bastante la gestión del ciclo de vida de estas instancias y ha eliminado algunas de las cosas feas que teníamos que hacer antes, como la creación de instancias usando
HierarchicalLifetimeManagers para vincularlas a un contenedor padre que era liberado al finalizar la petición. Ahora podemos ir bastante más al grano a la hora de registrar las clases, usando directamente un PerRequestLifetimeManager:public static void RegisterTypes(IUnityContainer container)
{
container.RegisterType<IProductServices, ProductServices>(
new PerRequestLifetimeManager());
}
Sólo con esto ya conseguimos que las instancias sean únicas por request, es decir, que si desde varios puntos del proceso de la petición se solicita un objeto de un tipo registrado, la instancia será única y compartida para todos ellos.Pero aún falta un detalle para que las instancias sean liberadas correctamente al finalizar la petición. La pista la encontramos en el archivo UnityMvcActivator.cs que Nuget ha dejado en la carpeta App_Start:
public static void Start()
{
var container = UnityConfig.GetConfiguredContainer();
FilterProviders.Providers.Remove(
FilterProviders.Providers
.OfType<FilterAttributeFilterProvider>()
.First());
FilterProviders.Providers.Add(
new UnityFilterAttributeFilterProvider(container));
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
// TODO: Uncomment if you want to use PerRequestLifetimeManager
// Microsoft.Web.Infrastructure.DynamicModuleHelper.DynamicModuleUtility
// .RegisterModule(typeof(UnityPerRequestHttpModule));
}
Simplemente descomentamos la llamada a RegisterModule() del final y nuestras instancias serán liberadas al finalizar la petición de forma automática :-)Seguro que habréis observado también que al comienzo de este método se elimina el proveedor de filtros por defecto y se establece el de Unity. Efectivamente, ¡tenemos de serie inyección de dependencias en filtros! En breve escribiré un post al respecto para que veamos cómo se implementan.
4. Sorpresa final: ¡convenciones!
Pues sí, esta es una de las grandes novedades que he podido descubrir hasta el momento de esta nueva versión. De la misma forma que ya se podía hacer con otros contenedores IoC, ahora Unity soporta el registro de componentes basado en convenciones, de forma que podemos ahorrarnos bastante trabajo a la hora de mantenerlo.¿Un ejemplo rápido de su utilidad? Pues por ejemplo que ya no tendremos que asociar mil interfaces del tipo
IMyComponent a la clase MyComponent: simplemente indicaremos a Unity que debe usar la convención de nombrado para interfaces y clases con nombres equivalentes :-)Así, en una única línea en el método
RegisterTypes() de UnityConfig.cs podemos decirle que todas las clases pertenecientes al ensamblado de la aplicación MVC deben asociarse a interfaces cuyo nombre corresponda con ellas (es decir, sea igual pero comenzando por “I”), y siempre con tiempo de vida por petición:public static void RegisterTypes(IUnityContainer container)
{
container.RegisterTypes(
AllClasses.FromAssemblies(typeof(MvcApplication).Assembly),
WithMappings.FromMatchingInterface,
WithName.Default,
WithLifetime.Custom<PerRequestLifetimeManager>
);
}
Por supuesto, las convenciones son totalmente configurables, por lo que con mucha probabilidad podremos adaptarlas a nuestras necesidades. Otro día escribiré también algo más sobre esto ;)Enlaces:
Publicado en Variable not found.
» Leer más, comentarios, etc...
Variable not found
Enlaces interesantes 117
Mayo 13th, 2013
Estos son los enlaces que he ido recopilando durante la semana pasada. Espero que os resulten interesantes :-)
.Net
- Getting Started With ScriptCS
Scott Smith - LINQ Tutorial for Beginners
D. Ban - Advanced programming with C#
Florian Rappl
Asp.net
- Include JavaScript Files with Automatic Versioning
Hernán Hegykozi - Validation Components in ASP.NET MVC
Kalyan Bandarupalli - Bootstrap HTML Helpers
Gordon Matt - ASP.NET MVC Facebook Birthday App
Rick Anderson - ASP.NET SignalR Hubs API Guide - JavaScript Client
Tom Dykstra - ASP.NET SignalR Hubs API Guide
Tom Dykstra - Comenzando con ASP.NET Web API y Odata , Parte 2
Gonzalo Pérez - The future is now – OWIN and multi-hosting ASP.NET web applications
Filip W. - One ASP.NET: Nancy.Templates for Visual Studio
Scott Hanselman - Building a Real-Time Photo Gallery with Xamarin, SignalR, Azure, and WebAPI
Gregs Hackles - ASP.NET Globalization and Localization
Marla Sukesh - CheckBoxList(For) - A missing MVC extension
Mikhail Tsennykh - A small but crucial point about App_Start folder
Imran Balochs
Azure / Cloud
Conceptos/Patrones/Buenas prácticas
- Cómo funcionan las cookies y por qué es importante saberlo
José Manuel Alarcón - Building for Web scale is a different skill
Jess Putz - Unit of Work Design Pattern
Shivprasad koirala
Data access
- EF 6: Command rewriters - I
Unai Zorrilla - EF 6: Interceptors, segunda iteración
Unai Zorrilla - What’s New in Entity Framework 5.0?
Kosta Hristov
Html/Css/Javascript
- How to add JavaScript and CSS files to a web page during runtime
Sisirp - Comparing JavaScript OOP to .NET
Jon Woo - Use ECMAScript 6 Today
sayanee Basu - jQuery $(‘body’).on() vs $(‘document’).on()
Sam Deering - Making accessible icon buttons
Nicholas C. Zakas - AngularJS Abstractions: Organizing Modules
K. Scott Allen
Visual Studio/Complementos/Herramientas
Eventos- Webcast: Novedades en Entity Framework 6
Jue 30 mayo | Diego Vega & Unai Zorrilla - Tenerife: C# para dominarlos a todos, Xamarin es el anillo de poder
Vie 24 mayo | José Miguel Torres & Juan M. Gómez - Móstoles: Mobility Day - Workshop Juegos con WaveEngine
Mié 22 mayo | Javier Cantón
Otros
- Tony Hoare Doctor Honoris Causa por la Universidad Complutense
David de Frutos - Open Source en un mundo Microsoft
Juan María Hernández
Publicado en Variable not found
» Leer más, comentarios, etc...
Koalite
Servidor de Integración Continua, una buena inversión
Mayo 13th, 2013
Siempre he pensado que las herramientas que usamos en el desarrollo de software deben estar seleccionadas y adaptadas en función del tipo de desarrollo que realizamos, el equipo, la metodología, etc.
Sin embargo, hay tres herramientas básicas que uso en todos los proyectos: un sistema de control de versiones (desde git hasta subversion), un gestor de bugs/tareas (aunque sea una hoja Excel) y un servidor de integración continua.
Las dos primeras herramientas de la lista están muy extendidas y no conozco ningún equipo de desarrollo que trabaje sin ellas, pero lo del servidor de integración continua es menos conocido y no se usa tanto.
¿Qué es la integración continua?
La integración continua es una practica ligada a la programación extrema que consiste en automatizar el proceso de integración de código para poder realizarlo de la manera más continuada posible.
Cuando llega el momento de liberar una nueva versión de una aplicación hay que realizar un proceso de integración de todos los componentes que forman parte de la aplicación. Esto implica realizar varios pasos, como obtener el código fuente, compilarlo, ejecutar los tests, etc.
En muchos proyectos este es un proceso completamente manual, en el mejor de los casos documentado en forma de checklist en alguna parte, pero a fin de cuentas, siempre que haya pasos manuales, antes o después habrá algún error y pondremos en producción una aplicación con el número de versión incorrecto, que no ha pasado las pruebas automatizadas o que no estará correctamente etiquetada en el control de código fuente.
Si queremos mejorar este proceso, el primer paso es automatizarlo. Para ello contamos con diferentes herramientas, generalmente basadas en algún tipo de script de compilación. En .NET se suele utilizar MSBuild, en Javascript hay opciones como Grunt, en Ruby existe Rake… Al final todas se basan en lo mismo, poder definir de una manera más o menos cómoda las tareas a ejecutar para poder lanzarlas en un único paso.
Un ejemplo de script de compilación para una aplicación de escritorio podría contar con los siguientes pasos:
- Obtener la última versión del código fuente.
- Actualizar la información de versión en el fichero AssemblyInfo.cs basándose en la convención que se haya establecido (número de compilación, fecha, etc.).
- Ejecutar una herramienta de análisis estático como FxCop o SourceMonitor para detectar defectos en el código.
- Compilar los proyectos.
- Ejecutar las suites de tests automatizados.
- Generar la documentación (manuales, guías de instalación, etc.).
- Crear los paquetes de instalación.
- Etiquetar en el control de código fuente la versión del código usada para generar estos instaladores, de forma que luego podamos trazar exactamente qué codigo fuente se uso para generar cada versión de la aplicación.
Preparar este script puede llevar un tiempo y no es una tarea fácil la primera vez que lo haces, pero una vez que has aprendido es bastante sencillo adaptarlo para nuevos proyectos, por lo que la inversión de tiempo se recupera rápidamente.
¿Qué aporta un servidor de integración continua a este proceso?
Una vez que contamos con este script, cada vez que queramos realizar una integración completa (potencialmente liberable) del código, podremos hacerlo en un sólo paso ejecutándolo desde cualquier equipo; entonces, ¿para qué hace falta un servidor de integración continua?
El servidor de integración continua se encarga de ejecutar este script automáticamente según la programación que establezcamos, por ejemplo cada vez que se se realiza un commit/checkin/push al control de código fuente.
Además, podemos establecer distintas configuraciones para un mismo proyecto. Por ejemplo, podemos crear una configuración que realice una compilación “rápida”, ejecutando sólo los test unitarios cada vez que realizamos un commit, y programar una compilación “completa”, que ejecuta también los tests de integración y genera los instaladores todas las noches (las típicas nightly builds).
Realizar el proceso de integración de continuamente en un servidor tiene varias ventajas:
Por una parte, evita discrepancias entre las versiones utilizadas para generar los entregables. Al realizar la compilación en el servidor se garantiza que siempre se usa la misma versión del compilador, SDKs, librerías de terceros, etc. Eso es difícil de conseguir si la compilación se realiza cada vez en equipo distintos donde podría haber distintas versiones de cosas instaladas globalmente (assemblies en el GAC, módulos node instalados con npm install -g, etc.), provocando cambios en el comportamiento final de la aplicación dependiendo del equipo en que se ha compilado. Este tipo de errores son complicados de diagnosticar y pueden hacerte perder mucho tiempo.
Además, permite detectar rápidamente errores introducidos por cambios en el código. Aunque se supone que antes de hacer un commit cada desarrollador debería asegurarse de que todo funciona y ejecutar los tests en su equipo, a veces las suites de tests tardan más tiempo del que nos gustaría en ejecutarse (especialmente si tenemos tests de integración) o directamente al desarrollador se le olvida. Al hacer que el código se compile cada vez que se hace el commit en el servidor de integración continua, los errores se detectan en cuestión de minutos en lugar de hacerlo varios días después cuando alguien intenta liberar una nueva versión.
Otra ventaja de usar un servidor de integración continua es que podemos mantener un histórico de los resultados de las compilaciones. Dependiendo de los análisis que estemos realizando sobre nuestro código, esto nos permite conocer como ha evolucionado la cobertura de código de los tests, la complejidad ciclomática media y todas esas métricas que tanto nos gustan (otra cosa es que sirvan para algo, pero eso lo dejamos para otro día).
En teoría, si este proceso se lleva al extremo cada vez que se hace un commit al control de código fuente el servidor de integración continua sería capaz de detectar si todo es correcto y en ese caso hacer un despliegue automático. Eso es lo que se conoce como entrega continua (continuous delivery o continuous deployment) y hay proyectos que son capaces de hacerlo, aunque no siempre es viable (ni recomendable).
Conclusiones
Montar un servidor de integración continua puede resultar un poco complicado al principio. Hay muchas opciones disponibles, algunas más sencillas y otras más complejas, pero donde realmente reside la complicación es en estructurar tu proyecto para que todas las tareas sean fácilmente automatizables.
Aunque pueda parecer que este tipo de técnicas sólo son aplicables a equipos muy grandes que hacen aplicaciones muy complejas, no es así. Yo lo he utilizado en proyecto con un único desarrollador en los que lo único que se hacía era compilar el código y generar un instalador, y aun así merece la pena. Tener la seguridad de que lo que has compilado es lo que se debía compilar, que la versión está correctamente asignada y que el instalador lo has generado con los binarios correctos (y no con otros que compilaste dos horas atrás), te permite vivir mucho más tranquilo.
Es verdad que el coste inicial es alto, pero lo bueno es que las técnicas necesarias son muy fáciles de reutilizar de un proyecto a otro, por lo que una vez que has automatizado la integración del primer proyecto, si sigues una estructura común en todos tus proyectos, el resto es cuestión de ajustar un par de rutas en los scripts de compilación y los tienes listos en minutos (literalmente).
No hay posts relacionados.
» Leer más, comentarios, etc...
Navegapolis
¿Influye la edad en la calidad como programador?
Mayo 12th, 2013
Se dice que son necesarias 10.000 horas de trabajo de programación para alcanzar el nivel de experto, pero una vez llegado a él, ¿cuál es la evolución? ¿Se mantiene? ¿Continúa mejorando? ¿Retrocede?
Aunque hay diversas opiniones, posiblemente la más habitual considera que la edad desgasta la capacidad para adoptar y absorber nuevos conocimientos, por lo que al avanzar va transvasando a puestos comerciales, de gestión, de asesoría, o incluso a otras actividades.
Como la demanda de software continúa creciendo, y en el medio plazo no es probable un cambio de tendencia, se empieza a cuestionar si la preferencia de candidatos jóvenes, o la percepción del "demasiado mayor" tiene sentido o puede ser un comportamiento inducido por una falsa lógica.
Patrick Morrison y Emerson Murphy-Hill del departamento de Ingeniería del Software de la Universidad de Carolina del Norte, han realizado un estudio pionero en esta línea, basándose en el análisis de los datos de la comunidad de programadores de stackoverflow.com (edad, reputación en la comunidad, tecnologías tratadas).
El objetivo de su primer estudio en esta línea es identificar si se aprecia algún tipo de correlación entre edad y nivel de programación, antes de profundizar con posibles correlaciones entre la "inteligencia fluida" o capacidad para identificar relaciones complejas y sus inferencias, y la "inteligencia cristalizada" que refleja la amplitud de conocimiento, comprensión juicio y sabiduría adquirida por la experiencia".
La conclusión a la que llegan los autores es que sí que existe una correlación entre la edad y la reputación de los programadores en stackoverflow.com, que parece indicar que se puede mantener un nivel de programación alto en los 50 y los 60, y que los usuarios más veteranos de stackoverflow se encuentran al día y adquieren sin problemas los nuevos conocimientos en las tecnologías que ha examinado este estudio.
Este es el documento del estudio: Is Programming Knowledge Related to Age? que presentarán los autores el 18 de mayo en el eventop "10th Working Conference on Mining Software Repositories" en San Francisco.
» Leer más, comentarios, etc...
Bitácora de Javier Gutiérrez Chamorro (Guti) » Programación
WikiquoteES
Mayo 12th, 2013
WikiquoteES lleva funcionando más de un año, pero acabo de darme cuenta, que no llegué a anunciarlo aquí. Es un sencillo bot que he creado, que sindica en RSS la frase del día de Wikiquote. Es decir, poco más que un parseador HTML escrito en PHP, que obtiene el contenido deseado. Menos de 100 lineas [...]
» Leer más, comentarios, etc...
El blog de pico.dev
Modificar la base de datos con Liquibase
Mayo 10th, 2013

Para llevar a cabo estas modificaciones tenemos varias opciones la primera que se nos puede ocurrir es crear un archivo con las sentencias SQL de los cambios y lanzarlo contra la base de datos. Sin embargo, esta opción aunque sencilla y rápida puede que no sea la mejor opción. La primera desventaja es que las sentencias puede que solo funcionen en uno o unos pocos sistemas de base de datos con lo que tal vez tengamos que desarrollar varios scripts para cada uno de los sistemas de base de datos a los que debamos dar soporte. La segunda desventaja es que si queremos revertir los cambios de un «refactor» deberemos crear las sentencias de «rollback» que puede dar bastante trabajo nuevamente si tenemos diferentes DBMS. Además de estas desventajas podríamos necesitar volver a cierto estado de la base de datos deberemos gestionarlo de alguna manera nosotros mismos probablemente de forma manual.
Una de las herramientas que nos puede hacer más sencillo estas modificaciones que se producen a la base de datos es Liquibase. Los scritps que actualizan la base de datos se definen en un XML (o json entre otros) de cambios que es independiente del sistema de base de datos que usemos aunque se pueden incluir sentencias SQL específicas para uno de ellos, también se pueden definir las sentencias rollback que permiten volver a un estado anterior. Soporta las principales bases de datos entre ellas:
- MySQL
- PostgreSQL
- Oracle
- MS-SQL
- SQLite
- Y otras entre ellas Sybase Enterprise, Sybase, DB2, Apache Derby, HSQL, H2, Informix, InterSystems, Firebird, SAPDB.
Si queremos lanzar una actualización de la base de datos a partir de un archivo changelog.xml con algunas modificaciones ejecutaremos lo siguiente:
Para obtener las SQLs que son lanzadas en la consola sin que sean ejecutadas contra la base de datos usaremos «updateSQL» en vez de «update» de esta manera podremos revisar las sentencias que ejecutará Liquibase. También puede sernos de utilidad obtener las diferencias entre dos versiones de la bases de datos o hacer rollback de los cambios, cosa que podemos hacer de diferentes formas.
Un archivo changelog.xml tiene el siguiente aspecto, en él vemos como se definen las tablas, campos y restricciones de integridad:
Por supuesto, estas tareas se pueden automatizar con la herramienta de construcción que prefiramos ya sea Ant, Maven, Gradle o mediante linea de comandos.
Referencia:
Liquibase
» Leer más, comentarios, etc...
Thanks Network
Disposing objects in SharePoint 2010
Mayo 10th, 2013
En SharePoint 2010 contamos con los siguientes objetos: SPFarm SPService SPWebApplication SPSite SPWeb SPList La mayoría de los cuales no soportan o requieren Dispose. SharePoint 2010 cuenta con una gran cantidad de productos, algunos de ellos fueron creados incluso antes de que … Sigue leyendo ![]()
» Leer más, comentarios, etc...
Cuaderno de software
Valladolid, 1 de Junio: Programania from the trenches
Mayo 9th, 2013
» Leer más, comentarios, etc...
Variable not found
Siete años ya, y esa variable que no aparece
Mayo 8th, 2013
Recuerdo aquel lunes 8 de mayo de 2006 como si fuera ayer. Venga, va, no lo recuerdo, pero buscando un poco por la red he visto que en Sevilla teníamos un día soleado con una temperatura máxima rondando los 27 grados que seguro que invitaba a salir a pasear. Según se puede comprobar en los periódicos del día (uno, otro, otro más…), el mundo estaba, como siempre, revuelto. Se celebraba el día de los Acacios, Gibrianos, y Metrones, entre otros nombres imposibles, y se cumplían 120 años desde el nacimiento de la coca-cola.
Y por algún motivo debí pensar que era un buen día para comenzar un blog. Fue uno de esos pequeños pasos que das sin otorgarle importancia alguna, pero que transcurrido el tiempo puedes comprobar que realmente fue decisivo para tu futuro. Lo único que tenía a mi favor eran las ganas de hacerlo, y la ventaja de tener poco que perder por intentarlo. Nota: no sé si os habréis fijado, pero las negrillas de este párrafo componen un mensaje subliminal para todos aquellos que aún no os habéis lanzado a crear vuestro propio blog ;)
Una vez tomada la decisión, sólo faltaba elegir por una la plataforma que me proporcionara alojamiento gratuito, pues obviamente no pensaba gastar ni un céntimo en esta aventura. Opté por Blogger porque en aquél momento todo lo que tenía que ver con Google molaba mucho, y me parecía una herramienta que ofrecía justo lo que andaba buscando: simplicidad de uso, y no complicarme mucho la vida en los aspectos infraestructurales ni de diseño.
Como curiosidad, el nombre original que elegí para el blog era “Variable not found 0:1”, como homenaje a uno de los famosos errores que mostraba nuestro añorado ZX Spectrum cuando hacíamos burradas con variables inexistentes. Más adelante eliminé el “0:1” final porque pensé que sólo unos cuantos frikis de mi generación podrían entenderlo, así que me centré en la parte textual del nombre, que me pareció bastante neutra y reconocible para los que nos dedicamos al desarrollo independientemente de nuestra trayectoria o tecnologías de cabecera.
Tras un breve post de presentación, me puse a escribir sobre lo que se me ocurría. Y no tardaron en verse los resultados, perfectamente reflejados en la siguiente gráfica de visitas:

¡Nada! Exceptuando un pico provocado por un post afortunado sobre Google Image Labeler, el número de visitas diarias durante los diez primeros meses rondaba entre cero y cinco de media. Vaya desastre, ¿no? Pues no; me divertía lo que hacía, y cada visita o comentario los celebraba casi con champán ;)
Ya cerca de cumplir el primer año de vida empecé a dedicar algo de tiempo a incluir el blog en directorios y agregadores, a hacer crossposting en sitios de primer nivel, a incluir feeds, y a obtener los primeros enlaces de otros autores a los que parecía interesar algunas de las cosas que escribía. Las visitas comenzaron a subir.
El segundo año fue el de mayor crecimiento de toda la historial del blog. Acabamos con una media de 10.000 visitas al mes, aunque algo artificial debido a picos brutales procedentes de avalanchas desde Menéame, Microsiervos, Genbeta y otros. Dado que la cosa se estaba poniendo ya seria, a finales de 2007 comencé a usar el dominio actual y sustituí la plantilla de Blogger por un diseño propio, bastante similar al actual (al que, por cierto, sé que se le notan ya los años ;-D).
El tercer y cuarto año podríamos considerarlos como de consolidación. Variable not found siguió creciendo a buen ritmo, y se incluyeron otras vías para estar en contacto con los seguidores, principalmente a través de Facebook y Twitter. Llegaron algunos patrocinadores y tuve la oportunidad de probar y acceder a información temprana sobre algunos productos, por cortesía de sus fabricantes. Superábamos las 120.000 páginas vistas por 70.000 visitantes únicos al año sólo en variablenotfound.com, y cerca de 1.000 suscriptores al RSS, unas cifras que no podía haber imaginado en mis mejores sueños.
El quinto año (2010-2011), continuamos creciendo de forma considerable en visitantes, suscriptores al feed, y seguidores a través de redes sociales, pero lo más importante que se produjo fue el salto al mundo físico. Hasta entonces, toda mi relación con la comunidad de desarrolladores había sido a través del blog pero, gracias a una invitación de Microsoft, en este año tuve ocasión de asistir a algunos eventos, participar en charlas y, en definitiva, poner cara a decenas de personas que conocía sólo virtualmente y a hacer buenos amigos entre gente a la que admiraba y seguía desde hacía mucho tiempo.
Continuamos creciendo también el sexto año con unas cifras bastante importantes. Se consultaban 200.000 páginas al al año y se superaban los 1.300 suscriptores al RSS. Pero, sin duda, lo mejor de este año fue la grata e inesperada sorpresa de ser nombrado Microsoft MVP, todo un lujazo que me dio la oportunidad de tratar con figuras de primer nivel o visitar el campus de Microsoft en Redmond, entre otras grandes satisfacciones, y una auténtica revolución a nivel profesional.
El séptimo año, el que ahora cerramos, ha sido también muy importante por varios motivos. Por un lado, llegó mi segundo nombramiento como Microsoft MVP, que ha sido de nuevo una alegría inmensa y me ha dado la oportunidad de seguir estrechando lazos con auténticos fenómenos en lo personal y profesional, de viajar de nuevo a Seattle, y pasármelo de fábula.
El segundo gran acontecimiento de este año, y que marca la diferencia respecto a etapas anteriores, ha sido la publicación de mi primer libro, Introducción a ASP.NET SignalR. La verdad es que es una experiencia que he disfrutado bastante porque me encanta esta tecnología; además, aprovechando la asistencia al MVP Summit en Seattle tuvimos la ocasión de la ocasión de presentárselo y entregárselo en persona a los principales desarrolladores de este framework, que lo acogieron con gran entusiasmo al ser el primero publicado sobre su criatura. Incluso dejé algún ejemplar firmado y con dedicatoria, como la gente importante ;-)
Bueno, ¿y qué tal fue la cosa en este séptimo año de vida del blog en cuanto a visitantes? Pues seguimos sin poder quejarnos… más bien todo lo contrario. En general sigue aumentando a muy buen ritmo el número de amigos de Variable not found a través de las distintas vías disponibles:
- Sólo en variablenotfound.com, alcanzamos las 250.000 páginas vistas al año, realizadas por 126.000 visitantes únicos. ¡Uau!
- Superando en mucho los 1500 suscriptores al RSS. Impresionante.
- 815 followers en Twitter. Brutal.
- Superando los 510 amigos en Facebook. Increíble.
Os comento también algunos datos destacables sobre estas visitas del último año. El porcentaje de lectores que acceder al blog usando Internet Explorer ha descendido un 3%, mientras que Firefox lo ha hecho más de un 10%; esto podría explicar prácticamente el 14% de incremento de visitantes que usan Google Chrome, que son ya más de la mitad.En cuanto al origen de estas visitas, el principal sigue siendo España, aunque este año se ha producido un fuerte incremento de los amigos procedentes del lado de allá del Atlántico, donde destacan las comunidades de México, Argentina, Colombia, Perú y Chile.
De verdad, no tengo palabras para agradeceros a todos que la comunidad de la variable continúe creciendo año tras año, y las alegrías que me sigue deparando. Personalmente, saber que estáis ahí es la mejor recompensa que puedo tener
Sólo espero seguir contando con vuestro apoyo y compañía un año más, y que continuéis ayudándome a encontrar esta escurridiza variable.
¡Nos vemos por aquí!
Publicado en Variable not found.
» Leer más, comentarios, etc...
Variable not found
Evento en Sevilla: Arquitectura con Javascript, ¡no más Google Copy Paste!
Mayo 7th, 2013
El próximo jueves día 9 de mayo, desde CartujaDotNet, el grupo de usuarios .NET de Sevilla, tenemos el gusto de invitaros a un evento presencial del gran Braulio Díez para tratar sobre un tema de gran actualidad: arquitectura de aplicaciones Javascript con Knockout y otros frameworks interesantes.
Será a las 19:30h, durará unas dos horas, y tendrá lugar en el Clouding Point/Centro demostrador TIC de Sevilla, que podéis encontrar en la siguiente dirección:
Parque Empresarial Nuevo Torneo
41015 Sevilla
Por supuesto, como de costumbre, la asistencia es gratuita, y lo único que tenéis que hacer es registraros en el sitio del evento en Eventbrite :-)
Descripción oficial del evento
¿Se acumula desmesuradamente código cliente en tus proyectos?, ¿has oído hablar de MVVM?, ¿quizás de Knockout?. Desde CartujaDotNet (@cartujadotnet), grupo de usuarios .NET de Sevilla, organizamos un evento sobre Arquitectura con Javascript.
Soluciones a problemas comunes: scripts/css, cache y tamaños, crear módulos y métodos “privados/ públicos”, gestión de dependencias, servicios de datos, unit testing, mocking de datos, binding declarativo y más!
Para ello se utilizarán en conjunto: Bundling, Amplify, RequireJS, Knockout y MockJSON.
Sobre Braulio Díez
Es un desarrollador de software especializado en Web y XAML. Lleva más de 15 años de experiencia trabajando en el sector en proyectos de ámbito internacional. Además, Braulio es MVP de Silverlight, escribe artículos técnicos relacionados con tecnologías .NET, es trainer y speaker.No os lo perdáis, que seguro que es interesantísimo. ¡Nos vemos por allí! :)
Publicado en: Variable not found.
» Leer más, comentarios, etc...
Variable not found
Habemus ganadores del sorteo Radarc
Mayo 7th, 2013
Pues la suerte ha hablado, y ya tenemos ganadores para el sorteo de licencias de Radarc que anunciábamos hace unos días. Al final el notario no pudo estar presente por razones de agenda ;), pero os aseguro que ha sido un sorteo total y absolutamente limpio y aleatorio.
Los afortunados, con los que ya me he puesto en contacto para indicarles cómo conseguir sus licencias, son:
- Carlos Corral
- Julio Avellaneda
- José Ignacio Pérez
- Jorge Gil
- Betto Geldres
Publicado en Variable not found.
» Leer más, comentarios, etc...
Variable not found
Enlaces interesantes 116
Mayo 6th, 2013
Estos son los enlaces que he ido recopilando durante la semana pasada, del 29 de abril al 3 de mayo de 2013.
Espero que os resulten interesantes :-)
.Net
- Geolocalizando direcciones IP con C#
Juan María Hernández - Zip and Unzip Files Programmatically in C#
Bipin Joshi - [C#]–Enumeraciones y corutinas
Eduard Tomás - Unity – Part 3: Aspect Oriented Programming
Ricardo Peres - Expression based Property Getters and Setters
Nick Polyak
Asp.net
- How to create SSL secure server (HTTPS) in local IIS ?
Bikash Prakash Dash - Async Controller In MVC 4
Prashant Khandelwal - Leverage Multiple Code Frameworks with One ASP.NET
Jeff Fritz - Classic ASP and MVC
Emmet M - Inheritance issue w/ Typescript (JavaScript) and MVC Bundling Ordering
Derik Whittaker - A View Engine for ASP.NET MVC Feature-Based Organized
Matt Honeycutt - ASP.NET Razor @Helpers Passing in Another @Helper Trick!
Ben Scharbach - Lifecycle of an ASP.NET Web API Message
Suprotim Agarwal - Zero friction parallel self hosting of ASP.NET Web API and static files using OWIN and Katana
Alexander Zeitler - ASP.NET Web API and greedy query string parameter binding
Filip W.
Azure / Cloud
- Desplegar aplicaciones web en Windows Azure WebSites que hagan uso de WIF
Ibón Landa - Announcing the release of Windows Azure SDK 2.0 for .NET
Scott Guthrie - Publishing to Azure Web Sites from any git/hg repo
David Ebbo - Windows Azure: Improvements to Virtual Networks, Virtual Machines, Cloud Services and a new Ruby SDK
Scott Guthrie
Conceptos/Patrones/Buenas prácticas
Data access
- Rompiendo el hielo con Code First Migrations
Sergio León - EF 6: Sorpresa, interceptors
Unai zorrilla - Entity Framework Code First Relations
Ricardo Peres
Html/Css/Javascript
- jQuery Migrate 1.2.0 Released
Dave Methvin - Automatic Figure Numbering with CSS Counters
Hugo Giraudel - La etiqueta base de HTML
Gisela Torres - AngularJS Abstractions: Modules
K. Scott Allen - Communication with Cross Domain IFrame - A Cross Browser Solution
Tadit Dash - Angular Abstractions: The Application
K. Scott Allen - Getting Into Ember: Part 4
Rey Bango - Principios para escribir JavaScript consistente e idiomático
Idiomatic.js team - HTML 5 Fullscreen API
Gisela Torres
Visual Studio/Complementos/Herramientas
- Announcing the Release of WebMatrix 3
Scott Guthrie - Scraping Dynamic WebSite data using Watin
Shah Vatsal
Windows 8/WinRT/WP
- [Tips and Tricks] Windows Phone 8. ¿Donde esta mi SplashScreen?
Javier Suárez Ruiz - Windows Phone 8. Distribución empresarial.
Javier Suárez Ruiz
Publicado en Variable not found
» Leer más, comentarios, etc...
Koalite
Open Source en un mundo Microsoft
Mayo 6th, 2013
Hace poco me entretenía con la enésima discusión en twitter sobre las maravillas y desastres de node.js y acabó saliendo un tema que hacía tiempo que no veía: el uso de librerías y plataformas de código abierto.
Sobre esto del código abierto hay posiciones muy extremas, tanto en un sentido como en otro (me atrevería a decir que hay mayor extremismo entre los defensores del código abierto), pero dejando a un lado fanatismos, creo que es posible realizar un análisis más pragmático del asunto.
OJO: no voy a entrar en discusiones ético-filosóficas sobre las bondades de uno u otro sistema. Tampoco voy a meterme en las diferencias entre código abierto, software libre y software gratis, porque aunque creo que son importantes, prefiero mantener un enfoque más “práctico” en este post.
Un mundo guiado por un único fabricante
Los que trabajamos con plataformas de Microsoft hemos estado muy acostumbrados a utilizar únicamente lo que nos proporcionaba Microsoft que, por otra parte, no era poco y cubría bastante bien la mayoría de situaciones a las que nos teníamos que enfrentar.
Si necesitabas hacer una aplicación web, tirabas de WebForms (o ahora de ASP.NET MVC). Si querías un ORM, Entity Framework. Para bases de datos, SQL Server, y si tenía que ser embebida, SQL CE. Esa era la forma de hacer las cosas y mucha gente no se la cuestionaba.
Recuerdo que hace unos 6 años empecé a introducir en mi trabajo del mundo real librerías “no Microsoft”. Primero log4net, luego Castle Windsor, después NHibernate, …
Al principio tuve que vencer cierta reticencia entre algunos compañeros de trabajo, pero el tiempo me ha dado la razón y hoy en día usamos muchas librerías “de terceros”, la mayoría de ellas open source, y debo decir que han mejorado considerablemente nuestros procesos de desarrollo (tanto a nivel de productividad como a nivel de calidad).
Aún hoy hay empresas en que plantear usar tecnologías no Microsoft dentro de un proyecto .NET se considera una mala idea y existen directores técnicos que directamente vetan el uso de estas tecnologías.
Es como el viejo dicho de “nunca despidieron a nadie por comprar IBM“, parece que si te ciñes al uso de tecnologías soportadas por Microsoft, nada puede salir mal, o al menos si sale mal, la culpa no será de tu elección tecnológica.
Sin embargo, gracias a la exposición constante que tenemos los desarrolladores a otras comunidades y a la mezcla de ideas procedentes de otros lenguajes, ya no es tan raro emplear herramientas de código abierto en proyectos de .NET.
De hecho, en los últimos años la propia Microsoft ha abrazado ese concepto con herramientas con Nuget y las últimas librerías desarrolladas por Microsoft están siguiendo este modelo de desarrollo (ahí tenemos Entity Framework aceptando pull requests de desarrolladores externos como Unai).
Una cuestión de confianza
En realidad, el problema que tiene mucha gente con el software de código abierto no tiene nada que ver con que el código sea abierto o cerrado o con que la licencia y el modelo de desarrollo sea más o menos libre, sino con quién está detrás del código.
Existe una sensación demasiado habitual de que un proyecto de código abierto que no está dirigido por una organización comercial es algo en lo que no se puede confiar, pero el tiempo nos ha enseñado que esto no es cierto.
Hay proyectos realizados por voluntarios que llevan mucho tiempo funcionando bien y tienen bases de usuarios tan grandes que los convierten en soluciones mucho más sólidas que sus equivalentes comerciales. Los más extendidos han conseguido además el apoyo de grandes empresas, lo que les da ese plus de confianza que parece necesario para usarlos en ciertos entornos (estoy pensando en casos como jQuery).
Al final, lo más importante para que una librería o plataforma tenga un buen soporte es que cuente con un número suficiente de usuarios. Si una tecnología alcanza el suficiente grado de adopción, siempre será más fácil encontrar alguien dispuesto a soportarla, ya sea de manera altruista respondiendo preguntas en Stack Overflow o de forma comercial cobrando por servicios de consultoría.
En ese sentido, a la hora de decidir si una librería determinada o una plataforma concreta son confiables, me parece más importante tener en cuenta la base de usuarios que quién está detrás de ella. De hecho, hay ocasiones en que tener una empresa detrás puede llegar a ser contraproducente porque puede que mantener esa tecnología viva vaya en contra de los objetivos económico/estratégicos de la empresa (preguntádselo a los que decidieron usar Linq2SQL para ver cómo en poco tiempo era descontinuado y reemplazado por Entity Framework).
También es cierto que existen empresas capaces de garantizar la supervivencia de un producto y el soporte del mismo durante un tiempo prefijado. Ahí están casos como Canonical (una empresa tan “comercial” como Microsoft) y sus releases de Ubuntu con soporte extendido o los ciclos de vida que ofrece Microsoft para productos como SQL Server.
Conclusiones
Este post no pretende ser un alegato a favor del movimiento Open Source y en contra de Microsoft. Nada más lejos de mi intención. Como he dicho al principio, creo que hay demasiado fanatismo en ambos bandos y se tiende a perder de vista el objetivo real: generar valor desarrollando software.
A la hora de elegir sobre qué plataforma desarrollar o qué librería utilizar hay muchos factores a tener en cuenta y, sin duda, el soporte que podamos obtener es uno de ellos, pero también lo es la facilidad para desarrollar sobre ella, el rendimiento que podamos conseguir y la disponibilidad de buenos desarrolladores.
Los típicos argumentos de “código cerrado malo porque no lo puedo tocar”, “código abierto malo porque nadie me lo va a mantener” suelen caerse por su propio peso. Todos conocemos ejemplos de productos comerciales que han sido descontinuados dejando de dar soporte a sus usuarios, y todos usamos un montón de código abierto que no sabríamos tocar ni en un millón de años.
Lo que cuenta es resolver un problema y buscar las mejores herramientas para ello, que a veces serán open source y a veces no. No dejes que eso sea lo único que te guíe a la hora de elegir las herramientas más adecuadas para tu próximo proyecto. El código (abierto o cerrado) importa, pero el contexto más.
No hay posts relacionados.
» Leer más, comentarios, etc...
El blog de pico.dev
Ejemplo de pruebas unitarias en javascript con Jasmine y Sinon
Mayo 3rd, 2013

Aprovechando el ejemplo que hice de una aplicación bastante completa de una lista de tareas que utilizaba Backbone, RequireJS, Mustache en el lado cliente y Tapestry y RESTEasy en el lado del servidor ahora le añadiré al código javascript el conjunto de pruebas unitarias con Jasmine y Sinon.
Jasmine es una herramienta para realizar pruebas basadas en BDD (behavior-driven development). BDD es parte de las metododologías TDD (test-driven development) haciendo énfasis en que la gente del dominio pueda trabajar con la gente técnica. Jasmine nos servirá para hacer las pruebas unitarias, Sinon nos permitirá usar spies, stubs y mocks en esas pruebas:
- Spy: Un spy es una función que se envía como parámetro al sujeto bajo prueba para que recolecte datos sobre lo que sucede dentro de esa función y objeto, se llama espía porque se envía tras la lineas enemigas para que recolecte información. La información que puede recolectar es el número de veces que se llama una función, con que parámetros y cuales, los valores de retorno o si lanzó excepciones.
- Stub: ofrecen un comportamiento parcial preprogramado del objeto real, se puede utilizar para que proporcione los datos que queramos al sujeto bajo prueba. Los stubs también pueden ejercer de espías.
- Mock: que también son espías (funciones falsas) y stubs (ofrecen comportmiento preprogramado) conocen las expectativas de como han de ser usados, requiriendo que se llamen con ciertos parámetros, un número de veces, en cierto orden, .... Conocen cual es el comportamiento esperado del sujeto bajo prueba y una vez hecha la prueba se puede comprobar.
En la parte del controlador de la vista nos centraremos en probar los métodos y eventos como onChangeCompletada en TareaView y addTarea, resetTareas, onClickLimpiar y onKeypressNuevaTarea en TareasView. Dado que la lista de tareas hace uso de un servicio REST utilizaremos Sinon para atrapar esas peticiones AJAX que se harían en la realidad.
En la vista probaremos que dado un modelo el resultado del html contiene los elementos que esperamos:
Las pruebas se lanzan abriendo la página html SpecRunner.html en el navegador. Esa página html tiene la dependencia sobre RequireJS y este carga en el navegador el resto de dependencias que necesiten los diferentes módulos incluido el módulo bajo prueba de la lista de tareas. Las pruebas de Jasmine se definen en otro módulo de RequireJS que tiene como dependencia la librería de Jasmine, Sinon y por supuesto el módulo tareas que probará. El resultado es el siguiente:
Backbone se presta muy bien a las pruebas unitarias. Una gran característica es que para realizarlas no es necesario que las vistas se asocien a un DOM real, funcionan en memoria, de modo que no hace falta insertar ni quitar contenido de la página mientras se van ejecutando los teses, no hay necesidad de limpiar el DOM para los siguiente teses y así funciona más rápido. Como usa el patrón MVC cada una de las partes del modelo, vista y controlador pueden probarse por separado. Este ejemplo no muestra todas la posibilidades de Jasmine y Sinon pero da una idea de como son las pruebas con ellas.
En mi repositorio de GitHub puedes encontrar el código fuente completo del ejemplo de pruebas unitarias en javascript con Jasmine y Sinon.
Y con esta es por el momento la última entrada sobre javascript que escriba de esta serie, no descarto escribir alguna más sobre javascript en el futuro porque alrededor de este lenguaje de programación están surgiendo muchas utilidades y librerías.
Referencia:
Introducción y ejemplo de RequireJS
Introducción y ejemplo de Mustache
Logging en Javascript con log4javascript
Capturar errores de Javascript
Optimizar módulos de RequireJS y archivos Javascript
Patrón de diseño MVC del lado cliente con Backbone.js
Introducción y ejemplo de Backbone.js
» Leer más, comentarios, etc...
Navegapolis
Satisfacción laboral de los equipos de programación ágiles y no ágiles
Mayo 2nd, 2013
"Un problema de los equipos de programación con importante impacto económico es la alta rotación de personal. La relación entre insatisfacción y rotación es una evidencia contrastada. El estudio (Comparative Analysis of Job Satisfaction in Agile and Non-Agile Software Development Teams) investiga si existe una relación entre la satisfacción laboral y el método de desarrollo empleado, y encuentra una correlación positiva moderada entre el nivel de satisfacción y el uso de metodologías ágiles..." (1)

"El análisis comparativo entre equipos ágiles y la media general de profesionales TIC, ha revelado tasas significativamente más altas de satisfacción en los miembros de equipos ágiles. Además no sólo en los trabajadores, sino también en los administradores se da mayor satisfacción con sus empleos. Este es un claro indicador de que los métodos ágiles no son un movimiento orientado sólo a los programadores".
El informe completo y todas tablas comparativas del estudio en:
(1) Melnik, G., & Marurer, F. (2006). Comparative Analysis of Job Satisfaction in Agile and Non-agile Software Development Teams. Extreme Programming and Agile Processes in Sotware Engineering, 32-42.
» Leer más, comentarios, etc...
Koalite
Geolocalizando direcciones IP con C#
Mayo 2nd, 2013
Conocer a nuestros usuarios es cada vez más importante para poder ofrecerles servicios más ajustados a sus necesidades.
Cuando estamos trabajando con una aplicacion web existen herramientas como Google Analytics que nos permiten recabar todo tipo de información (también podemos usar Google Analytics con aplicaciones de escritorio) , pero hay veces que necesitamos conocer detalles como la ubicación del usuario en el momento de interactuar con él, y no sólo a posteriori para realizar análisis estádísticos.
Una de las cosas que nos puede ayudar a mejorar la experiencia de usuario es conocer el lugar en el que se encuentra el usuario.
En HTML5 existe un api de geolocalización, pero no siempre está disponible y, además, requiere del consentimiento del usuario.
Otra opción es utilizar lo que se conoce como geolocalización de IP, que consisten en tratar de averiguar la ubicación del usuario basándonos en su dirección IP. Esto no es completamente fiable porque el usuario puede acceder a través de un proxy o usar una IP que está físicamente alejada de su ubicación real (la IP está realmente asociada al ISP no al equipo del usuario), pero en muchas ocasiones es suficiente.
Geolocalizando una IP
Para conocer la ubicación del usuario existen muchos servicios, pero en este post vamos a ver uno muy simple y, además, gratuito (con ciertos límites): IPInfoDB.
El servicio ofrecido por IPInfoIDB requiere que nos registremos para conseguir un API KEY, pero este registro es completamente gratuito.
Una vez completado el registro, el código necesario para geolocalizar una IP en C# es muy sencillo:
public Location GeoLocate(string ip)
{
const string API_KEY = "YOUR_API_KEY";
var url = "http://api.ipinfodb.com/v3/ip-city/?key={0}&ip={1}&format=xml";
var doc = XDocument.Load(string.Format(url, API_KEY, ip));
return new Location
{
CountryCode = doc.Descendants("countryCode").First().Value,
CountryName = doc.Descendants("countryName").First().Value,
Region = doc.Descendants("regionName").First().Value,
City = doc.Descendants("cityName").First().Value,
ZipCode = doc.Descendants("zipCode").First().Value,
Latitude = decimal.Parse(doc.Descendants("latitude").First().Value, CultureInfo.InvariantCulture),
Longitude = decimal.Parse(doc.Descendants("longitude").First().Value, CultureInfo.InvariantCulture)
};
}
Como véis no hay mucha complicación, cargamos en un documento XML los datos devueltos por el servicio y construimos un objeto Location a partir de la información contenida en el documento XML.
No tan rápido
Antes de empezar a usar este código en tu próxima aplicación, hay que tener en cuenta algunas limitaciones:
- El código anterior es código de demo. No hay ningún tipo de control de errores, y lo último que querrías es que tu aplicación dejase de funcionar porque el servicio de IPInfoDB está caído. Una buena opción cuando uno se integra con este tipo de servicios es aplicar el patrón disyuntor (circuit breaker).
- Utilizar esta técnica para geolocalizar IPs requiere una llamada a un servidor externo, y esa llamada puede ser más lenta de lo que nos gustaría. Es importante tenerlo en cuenta y sería muy recomendable realizarla de forma asíncrona (y con un timeout, por cierto) para evitar bloquear nuestra aplicación eternamente.
Conclusiones
Geolocalizar una IP usando C# es muy sencillo con servicios como IPInfoDB y puede ayudarnos a hacer aplicaciones más inteligentes que resulten más agradables de utilizar para nuestros usuarios, pero no hay que olvidar que estamos introduciendo una dependencia sobre un servicio externo y hay infinidad de cosas que pueden salir mal, por lo que nuestra aplicación deberá estar preparada para tratar con estas contingencias.
No hay posts relacionados.
» Leer más, comentarios, etc...
Koalite
Gracias Gusenet
Abril 29th, 2013
Este fin de semana se ha celebrado en Santa Pola el evento #TuNodeYoXaml de Gusenet, el Grupo de Usuarios .NET del Sureste. Han sido un par de días memorables, llenos de charlas interesantes con una participación de primer nivel tanto entre los ponentes como entre los asistentes.
El buen ambiente ha sido la tónica general durante todo el fin de semana y creo que todos nos hemos ido de allí con un excelente sabor de boca.
Ha sido un verdadero placer conoceros personalmente a muchos de vosotros a los que sólo os conocía a través de twitter y vuestros blogs y, sobre todo, comprobar de primera mano que en este país hay mucha gente intentando hacer las cosas bien y, a la vez, disfrutando con su profesión.
Muchas gracias Oscar Montesinos (@2flores), Pedro Hurtado (@_PedroHurtado) y Xavier Jorge Cerdá (@XaviPaper) por el excelente trabajo que habéis hecho para organizar este evento y hacernos sentir a todos como en casa. Os merecéis el éxito que habéis tenido.
A título personal, no puedo dejar de agradecer a mis compañeros de viaje Luis Ruíz (@LuisRuizPavon), Sergio León (@panicoenlaxbox) y Carlos Duque (@ChkDuke) lo fácil que me lo han puesto todo y lo mucho que nos hemos divertido juntos.
¡Nos vemos en la próxima!
No hay posts relacionados.
» Leer más, comentarios, etc...
Variable not found
Enlaces interesantes 115
Abril 29th, 2013
Estos son los enlaces recopilados del 22 al 26 de abril de 2013. Espero que os resulten interesantes :-)
.Net
- Enterprise Library 6.0
S. Somasegar - Project-less scripted C# with ScriptCS and Roslyn
Scott Hanselman - Identifying the Namespaces in an Assembly
Richard Carr - Asynchronous Programming in C# - Advanced Topics
Motti Shaked - Build Where clause dynamically in Linq
Fitim Skenderi - Concurrent Object Pool, the Right Way
Roman Atachiants
Asp.net
- Combos en ASP.NET MVC
Eduard Tomás - Quick view of ASP.NET MVC
Brij - TwitterBootstrapMvc
Dmitry A. Efimenko - Remove Unwanted HTTP Response Headers
Mathurvarun - Prevent Repeated Requests using ActionFilters in ASP.NET MVC
Rion Williams - [ASP.NET WebAPI] Como recibir tipos complejos en nuestros controladores por URL
Luis Ruiz Pavón - Website Performance with ASP.NET - Part4 - Use Cache Headers
Markus Greuel - Learning SignalR: Unable to get property 'client' of undefined or null reference
Hongmei Ge - Host ASP.NET MVC Apps on Azure WebSite Without Spending a Cent on Databases
Suprotim Agarwal - ASP.NET Web API: CORS support and Attribute Based Routing Improvements
Scott Guthrie
Azure / Cloud
- Deploying Windows Azure Cloud Services Apps
Shiju Varghese - Building Windows Azure Cloud Services App with Web Role, Worker Role, Table Storage and Service Bus
Shiju Varghese
Conceptos/Patrones/Buenas prácticas
- Diseño de Modelos
Juan María Hernández
Data access
- EF 6:Nuevas pequeñas grandes cosas...
Unai Zorrilla - Connecting to MongoDB using C#
Ravindra T C
Html/Css/Javascript
- Use jQuery’s $.closest() vs $.parents()
Sam Deering - Models and Services in Angular
Rob Conery - Stealing the users back button with the History API
Ryan Seddon - Magic Numbers in CSS
Chris Coyier - ‘LESS’– A dynamic language that simplifies your CSS
Nishanth Anil - Previewing image uploads with FileReader in HTML5
Michael Williamson - JavaScript Best Practices
Tim Corey - Preventing the Performance Hit from Custom Fonts
Chris Coyier
Visual Studio/Complementos/Herramientas
- NuGet 2.5 Release Notes
Nuget team - SemanticMerge, una forma diferente de realizar los merges en nuestro repositorio
Juan Quijano - [Fiddler] Scratchpad es tu aliado
Luis Ruiz Pavón
Otros
- Exposed- A Blog Comment Spammer's Source Template
Scott Hanselman - La segunda mitad del tablero de ajedrez
José Manuel Alarcón - Todos los Eventos de Second Nug
Javier Conesa
Publicado en Variable not found
» Leer más, comentarios, etc...
El blog de pico.dev
Ejemplo lista de tareas con Backbone, RESTEasy y Tapestry
Abril 26th, 2013

El ejemplo consiste en una lista de tareas, pudiéndose introducir nuevas tareas y marcarlas como realizadas. También se podrá eliminar de la lista las tareas completadas y ver un resumen con el número de tareas completadas y de tareas totales.
En la parte cliente de la aplicación se hace uso de RequireJS para manejar la carga de los archivos javascript necesarios, de backbone para gestionar la lista de tareas y realizar la comunicación con el servicio REST del servidor y de Mustache como motor de plantillas para generar la vista (el html). En la parte del servidor usa Tapestry como framework web y RESTEasy como librería para implementar un servicio REST que proporcionará la persistencia en memoria de la lista de tareas.
Backbone permite organizar el código aplicando el patrón de diseño MVC (modelo-vista-controlador) ampliamente extendido en los frameworks web. Este patrón de diseño tiene la ventaja que separa la aplicación en tres partes diferenciadas:
- El modelo: que contiene los datos del dominio que maneja la aplicación que cuando es modificado lanza eventos para que el controlador y la vista actúen en consecuencia.
- El controlador: que reacciona ante los eventos que se produzcan en el modelo o por el usuario y modifica adecuadamente el modelo o la vista según el evento.
- La vista: que a partir del modelo produce la interfaz que se ofrece al usuario para que pueda manipularlos, los eventos producidos en la vista son manejados por el controlador.
En una aplicación con la combinación de Backbone, Mustache y un servicio REST el servidor devuelve el html de la página inicial, las plantillas y los datos del servidor y se delega en el navegador del usuario la tarea de renderizar el html final. Esto tiene la ventaja de que entre el navegador y el servidor viajan menos datos (los datos y las plantillas tendrán menos tamaño que los datos formateados a html) y la parte del servidor se simplifica (no es necesaria la lógica para formatear a html los datos que dependiendo del framework hacen los jsp, tml de tapestry o gsp de grails). Aunque el código de la parte cliente crecerá, el código de la parte del servidor se hará más simple. Una vez cargada la página inicial entre el servidor y el cliente solo viajan los datos en formato json.
Veamos el ejemplo a continuación empezando por el modelo. El modelo con Backbone se define con Backbone.Model.extend, donde podremos indicar las propiedades de los objetos, también podemos definir funciones de utilidad. En este caso el modelo estará formado por los objetos Tarea que tendrán las siguientes propiedades:
- Un id que identificará la tarea.
- La descripción de la misma.
- Y un indicador de si está completada.
La interfaz del servicio web REST es:
En Backbone la vista y el controlador de define en una misma entidad con Backbone.View extend. Aunque se definen ambas cosas a la vez la vista principalmente está formada por el método render, el resto será parte del controlador. Otra sección importante de la vista es la propiedad events donde indicaremos que eventos manejará el controlador y sobre que elementos de la vista, básicamente se define como clave el evento (click, change, ...) y un selector similar a los usados en jquery y como valor el nombre de la función que manejará el evento. En la vista también podremos definir métodos de utilidad. Tendremos dos vistas: una para para una tarea y otra para la aplicación de la lista de tareas.
En el método render se hace uso de Mustache para generar la vista a partir de una plantilla que contiene el código html, la plantilla combinado con los datos del modelo genera un resultado que será incluido en el html de la página. En las funciones initialize se inicializa la vista principalmente para definir que eventos que produzca el modelo serán tratados por el controlador, estos son las funciones on sobre el modelo, por ejemplo, nos interesará saber que se añade, modifica o elimina una tarea de la colección de tareas para actualizar la vista adecuadamente.
En el caso de TareasApp en el método initialize además se inserta en el elemento «el» que se le pasa en el constructor la vista inicial de la aplicación. De esta forma podríamos crear varias instancias de TareasApp.
Las plantillas de Mustache las he definido entre elementos <![CDATA[ ... ]]> ya que las plantillas de Tapestry han de ser xhtml válido, algunas expansiones de Mustache como {{attrs.checked}} de la plantilla tarea-template Tapestry no las entiende como válidas ya que realmente la plantilla Mustache en este caso no es html válido.
Una vez desarrollada la parte del servicio REST en el servidor con RESTEasy integrarlo con Backbone es muy sencillo, únicamente deberemos llamar en los puntos adecuados a las funciones save y destroy de los modelos. Al método save se le llama cuando se añade una nueva tarea en TareasApp.addTarea y cuando se modifica en TareaView.onChangeCompletada. Cuando se hace clic en el botón para limpiar las tareas completadas se llama a la función destroy de cada uno de los modelos. Estos métodos lanzarán las peticiones AJAX al servicio REST de forma automática variando su método y con su correspondiente url según hemos definido con las anotaciones del servicio REST: POST (crear), PUT (actualizar) y DELETE (eliminar).
![]() |
| Petición al crear una nueva tarea |
![]() |
| Petición al marcar como completada una tarea |
![]() |
| Petición al limpiar tareas completadas |
Esta es una captura del ejemplo:
Después de haber usado Backbone en este ejemplo simplemente tengo que decir que es una gran herramienta y que facilita y ayuda a organizar el código javascript en gran medida. Permite separar los datos de la aplicación que forman el modelo del controlador y la vista que reaccionan mediante los eventos producidos en el modelo. También permite desarrollar aplicaciones con un gran peso de javascript en la parte cliente sin que el código se convierta posteriormente en un infierno de mantenimiento aún con la ayuda de jQuery. Es una ayuda tan grande para hacer algunas cosas de la interfaz del cliente como lo es jQuery para manipular los elementos html.
Algunas partes no las he explicado como las plantillas de Mustache, el uso de RequireJS o la parte del servidor del servicio RESTEasy ya que ya lo que he hecho en entradas anteriores que puedes visitarlas mediante sus enlaces.
Como en el resto de entradas el código fuente completo lo puedes encontrar en mi repositorio de GitHub. Si quieres probarlo en tu equipo lo puedes hacer de forma muy sencilla con los siguientes comandos y sin instalar nada. Si no dispones de git para clonar mi repositorio de GitHub puedes obtener el código fuente del repositorio en un archivo zip con el anterior enlace.
Referencia:
Introducción y ejemplo de RequireJS
Introducción y ejemplo de Mustache
Logging en Javascript con log4javascript
Capturar errores de Javascript
Optimizar módulos de RequireJS y archivos Javascript
Patrón de diseño MVC del lado cliente con Backbone.js
» Leer más, comentarios, etc...
Picando Código
Lanzador de aplicaciones en KDE: Homerun – similar a Unity Dash
Abril 25th, 2013
En diciembre pasado pasé de ArchLinux a Ubuntu en mi laptop de trabajo. Un tiempo atrás podían contarme entre los haters de Unity, la interfaz gráfica de usuario de Ubuntu. Creo que lo probé por primera vez con Ubuntu 11.04, y lo primero que hacía en ese entonces después de instalar el sistema era activar el fallback a GNOME.
En ese momento escribía comentarios como: “Creo que usar Unity me está causando daño cerebral. ¡¿Qué quisieron hacer con esto?! Instalando gnome-shell…”
Pero con el paso del tiempo las cosas van cambiando. Ubuntu no solo me malcrió como usuario que espera que el sistema “simplemente funcione” sin configurar mucho. Ahora también, me gusta usar Unity. El cambio de paradigma en el uso del sistema me parece muy acertado.
Trabajar con Unity me resulta cómodo por el hecho de que es casi 100% transparente. Cuando uso Ubuntu, no siento que estoy usando “el entorno gráfico Unity”, simplemente estoy usando mi navegador web, mi editor de texto, mi cliente de mensajería, etc. Unity no estorba, no se nota que está ahí, y me facilita mucho el trabajo de interactuar con mi computadora. Excelente Canonical, ESO es lo que debería un sistema operativo y su entorno gráfico, y parece que lo lograron. Como todas mis interacciones con la computadora, esto está sujeto a cambiar y puedo adoptar una posición completamente opuesta de acá a un tiempo.
En un momento comenté “Es increíble como me acostumbré a usar Unity y ya creo que es hasta mas cómodo que KDE
” (recuerden que odiaba Unity). Así que estaba teniendo un problema con la diferencia entre las experiencias de mi laptop con Unity y mi desktop con KDE. Por suerte la comunidad de desarrollo de KDE no descansa y ya se puso a tiro con el paradigma que provee Unity Dash.
Homerun
Homerun es un lanzador de aplicaciones a lo Dash para KDE. Funciona a pantalla completa y el contenido está organizado en pestañas. Cada pestaña tiene varias fuentes, que a su vez proveen una o más secciones de una pestaña. Ya viene con algunas fuentes creadas pero se pueden personalizar mas fuentes.
Recientemente salió la versión 0.2.2, con varios arreglos y bastante estable. Esta fue la versión que me hizo reemplazar el lanzador de aplicaciones y menú de aplicaciones por defecto de KDE por Homerun.
Funciona bastante bien, y tiene la habilidad de buscar cosas a lo Dash, lo que nos soluciona bastante la ejecución de aplicaciones, búsqueda de archivos y demás.
En ArchLinux pueden instalar Homerun desde AUR:
yaourt -S aur/kdeplasma-applets-homerun |
O sigan el procedimiento habitual desde AUR. Viene bastante completo, y creo que lo voy a seguir usando por defecto mientras mantenga el escritorio KDE en ArchLinux y no pase a otra cosa ![]()
Algunas imágenes de Homerun en funcionamiento:
Atajos de teclado:
Teclas de flechas: Navegar los resultados
Enter: Abrir el resultado marcado
Ctrl+Repág, Ctrl+Avpág: cambiar de pestañas
Ctrl+F: Mover el foco al campo de búsqueda
Alt+Izquierda, Alt+Derecha: Ir para atrás y adelante (útil al navegar jerarquías)









