Noticias Weblogs Código

Blog Bitix

Usar Apache Tapestry en una aplicación «standalone»

July 31, 2014 11:06 AM

Apache Tapestry

Apache Tapestry es un framework de desarrollo para aplicaciones o páginas web en el que habitualmente se emplea el lenguaje Java y se despliega en un servidor de aplicaciones como entorno de ejecución. Pero Tapestry es una pieza de software que se compone de diferentes partes algunas de las cuales pueden ser utilizadas fuera del contexto de una aplicación web. Este es el caso del contenedor de dependencias que proporciona IoC (IoC, «Inversion of Control») en Tapestry, podemos usarlo en una aplicación «standalone», es decir, en un programa que se inicia con el típico «public static void main(String[] args)» de las aplicaciones Java.

El contenedor de dependencias de Tapestry tiene algunas propiedades interesantes como que dos servicios pueden ser mutuamente dependientes y que se puede contribuir configuración a cualquier servicio para cambiar en cierta medida su comportamiento además de otras características que explico más en detalle en el libro PlugIn Tapestry. Para usarlo en una un programa que se ejecuta de la linea de comandos usando el main de una clase Java primeramente deberemos incluir en el proyecto la dependencia sobre tapestry-ioc, si usamos Gradle de la siguiente manera:

<noscript><pre><code>compile 'org.apache.tapestry:tapestry-core:5.4-beta-3' compile 'org.apache.tapestry:tapestry-ioc5.4-beta-3'</code></pre></noscript>

Una vez que tenemos la dependencia en el programa deberemos iniciar el contenedor IoC e indicarle los diferentes módulos que contendrán la definición de los servicios.

<noscript><pre><code>RegistryBuilder builder = new RegistryBuilder(); builder.add(TapestryModule.class, HibernateCoreModule.class, HibernateModule.class, BeanValidatorModule.class, TapestryOfflineModule.class, GeneratorModule.class); builder.add(new SpringModuleDef(&quot;applicationContext.xml&quot;)); Registry registry = builder.build(); registry.performRegistryStartup();</code></pre></noscript>

En este caso he usado Spring para la transacionalidad e Hibernate para la persistencia. Después de esto tenemos la referencia al registro de servicios, podemos obtener cualquiera en base a la interfaz que implementa, en este caso el servicio que implementa la interfaz MainService.

<noscript><pre><code>registry.getService(MainService.class);</code></pre></noscript>

Al final de la aplicación deberemos llamar al método shutdown del registro.

<noscript><pre><code>registry.shutdown();</code></pre></noscript>

Otra cosa que nos puede interesar es poder generar contenido html usando el sistema de plantillas y componentes de Tapestry, ya sea en una aplicación «standalone» o en una aplicación web para enviar el contenido en un correo electrónico o quizá guardarlo en un archivo. Hay muchos sistemas de plantillas, cada framework suele tener uno propio o usar una solución específica como Thymeleaf pero la mayoría usa un modelo push en vez de un modelo pull, en el caso de Tapestry se emplea el modelo pull que tiene algunas ventajas como explico en el artículo anterior. Si usamos una aplicación Tapestry usándolo también para generar el contenido de los correos o cierto contenido estático evitamos tener que aprender una segunda tecnología además de aprovechar todo el código reutilizable que posiblemente hemos desarrollado en algunos componentes. Para generar el contenido estático que generaría una página en Tapestry tenemos el módulo Tapestry Offline. Como no está en los repositorio de maven debemos descargarnos el jar e incluir la dependencia como un archivo.

<noscript><pre><code>compile files('misc/libs/tapestry-offline.jar')</code></pre></noscript>

Para generar una página de Tapestry fuera de una petición web y de un servidor de aplicaciones debemos usar el servicio OfflineComponentRenderer. Su uso sería el siguiente:

<noscript><pre><code>@Override public File generatePage(String page, Object[] context, Map&lt;String, String&gt; params) throws IOException { File file = new File(to, getToPage(page, context, params).getPath()); logger.info(&quot;Generating page «{}» ({}, {})...&quot;, page, file, params.toString()); file.getParentFile().mkdirs(); Writer w = new FileWriter(file); render(page, context, params, Globals.LOCALE, w); w.close(); return file; } private void render(String page, Object[] context, Map&lt;String, String&gt; params, Locale locale, Writer writer) throws IOException { TypeCoercer coercer = Globals.registry.getService(TypeCoercer.class); OfflineComponentRenderer renderer = Globals.registry.getService(&quot;BlogStackOfflineComponentRenderer&quot;, OfflineComponentRenderer.class); EventContext activationContext = new ArrayEventContext(coercer, context); PageRenderRequestParameters requestParams = new PageRenderRequestParameters(page, activationContext, false); DefaultOfflineRequestContext requestContext = new DefaultOfflineRequestContext(); for (Map.Entry&lt;String, String&gt; param : params.entrySet()) { requestContext.setParameter(param.getKey(), param.getValue()); } requestContext.setLocale(locale); renderer.renderPage(writer, requestContext, requestParams); }</code></pre></noscript>

Tengo que decir que al generar la página fuera de una petición web tendremos alguna limitación como solo poder usar assets con el prefijo context. Pero esto por lo menos como he explicado en el caso de Blog Stack no me ha supuesto ningún problema.

Esto quizá no sea lo habitual pero en Blog Stack ambas posibilidades me han resultado de gran utilidad al desarrollar el proyecto. Las posibilidades son muchas por ejemplo podríamos usar alguna combinación de esto mismo con el microframework Spark si nuestra aplicación estuviese más orientada a una API aunque también podríamos usarlo junto con RESTEasy.

Referencia:
Libro PlugIn Tapestry
Documentación sobre Apache Tapestry

» Leer más, comentarios, etc...

proyectos Ágiles

The spirit of Agile - 2 minutes video

July 29, 2014 08:31 PM

This 2' video shows "The spirit of Agile" as a lever for success in the mindset change.

the_spirit_of_agile

 

Credits:

  • Agile Coach: Silvia Sistaré.
  • Concept and Production: Blua Producers and Xavier Albaladejo

» Leer más, comentarios, etc...

Arragonán

Semana 320

July 29, 2014 12:19 PM

Esta vez el retraso viene de que la semana la terminé en Noja, quería aprovechar el trayecto en autobús para escribir la retro, pero casualmente coincidí con Fernando y entre unas cosas y otras al final pasé de hacerlo.

Cosas del verano, supongo, esta semana no encuentro nada en especial que destacar más allá del trabajo hecho:

  • En Nort terminé el trabajo de integración con facebook en el backend y empecé a trabajar en la versión móvil (una aplicación híbrida), estudiando el framework javascript que usamos y empezando la integración con la autentificación con facebook en este escenario
  • Tras la primera propuesta del rediseño de la web de minchador (que me moló bastante), tocó tratar algunos cambios y añadidos para poder seguir avanzando.
  • Estuve dedicando algún rato a mhop, cuestiones de organización más que otra cosa, además de preparar el viaje.

Buena semana.

» Leer más, comentarios, etc...

Ingenieria de Software / Software Engineering / Project Management / Business Process Management

Del módelo al Ejecutable

July 29, 2014 04:57 AM

Expresso Logic, una interesante propuesta para realizar aplicaciones de manera consistente y rápidas

Modern Analyst, Expresso Logic

» Leer más, comentarios, etc...

Variable not found

<Vacaciones>

July 28, 2014 11:50 AM

Pues llegó la mejor época del año, prácticamente la única en la que algunos afortunados podemos alejarnos un poco de los mundos binarios y dedicarnos a otros menesteres seguro más importantes, como disfrutar de la familia y descansar, al menos unos días.

Variable not found quedará a la deriva hasta septiembre, cuando volveremos al ataque con las pilas cargadas y listos para afrontar la nueva temporada :)

¡Feliz verano, amigos!

» Leer más, comentarios, etc...

Blog Bitix

Servir recursos estáticos desde un CDN en Apache Tapestry

July 26, 2014 07:00 PM

Apache Tapestry

Un Content Delivery Network (CDN) no es más que un servidor, servidores o servicio dedicado a servir el contenido estático o actuar de cache para los clientes. Alguno de los motivos por los que podríamos querer usar un CDN en una aplicación son:

  • Algunos servicios CDN están repartidos geográficamente por el mundo de modo que el contenido sea servido de un lugar más cercano al usuario esto hace que el tiempo que tarda en cargar un página o servirse el contenido sea menor.
  • Descargar la tarea de servir al menos parte del contenido de la aplicación al CDN hará que no nos tengamos que preocupar de tener la capacidad para servirlo. Cuando se cargar una página se hacen varias peticiones al servidor para obtener el contenido como el html, imágenes, estilos, … haciendo que los contenidos estáticos sean servidos por el CDN hará que el servidor tenga menos carga, dependiendo del número de usuarios de la aplicación o los picos de tráfico notaremos una mejoría.
  • La alta fiabilidad de servicio que ofrecen.

Amazon ClodFront es una de las opciones que podemos usar como CDN. En este artículo voy a comentar como tener un CDN para servir el contenido estático en una aplicación que emplee el framework de desarrollo de aplicaciones web en la plataforma Java Apache Tapestry.

Para que el contenido estático se sirva del CDN debemos hacer que las URL de las imágenes y hojas de estilo se generen con la URL propia del CDN, al menos, deberemos cambiar el host de esas URL. No hay que hacer mucho más ya que CloudFront creo que se puede configurar para que cuando le lleguen las peticiones del contenido si no las tiene las delegue en la aplicación, una vez que las tiene cacheadas ya no necesita solicitarselas a la aplicación y las sirve él mismo.

Una de las cosas muy interesantes de Tapestry es que podemos modificar prácticamente cualquier comportamiento del mismo, esto es debido a que la mayor parte de sus funcionalidades son ofrecidas mediante servicios que podemos sobreescribir con los que nosotros proporcionemos, el contenedor de dependencias (IoC) de tapestry lo hace muy fácil. Para modificar las URL de los recursos estáticos que son generados en Tapestry deberemos implementar la clase AssetPathConverter. Una implementación podría ser la siguiente:

<noscript><pre><code>package es.com.blogspot.elblogdepicodev.plugintapestry.misc; import java.util.Map; import org.apache.tapestry5.ioc.annotations.Inject; import org.apache.tapestry5.ioc.annotations.Symbol; import org.apache.tapestry5.ioc.internal.util.CollectionFactory; import org.apache.tapestry5.services.AssetPathConverter; import es.com.blogspot.elblogdepicodev.plugintapestry.services.AppModule; public class CDNAssetPathConverterImpl implements AssetPathConverter { private String protocol; private String host; private String port; private String path; private Map&lt;String, String&gt; resources = CollectionFactory.newMap(); public CDNAssetPathConverterImpl(@Inject @Symbol(AppModule.CDN_DOMAIN_PROTOCOL) String protocol, @Inject @Symbol(AppModule.CDN_DOMAIN_HOST) String host, @Inject @Symbol(AppModule.CDN_DOMAIN_PORT) String port, @Inject @Symbol(AppModule.CDN_DOMAIN_PATH) String path) { this.protocol = protocol; this.host = host; this.port = (port == null || port.equals(&quot;&quot;)) ? &quot;&quot; : &quot;:&quot; + port; this.path = (path == null || path.equals(&quot;&quot;)) ? &quot;&quot; : &quot;/&quot; + path; } @Override public String convertAssetPath(String assetPath) { if (resources.containsKey(assetPath)) { return resources.get(assetPath); } String result = String.format(&quot;%s://%s%s%s%s&quot;, protocol, host, port, path, assetPath); resources.put(assetPath, result); return result; } @Override public boolean isInvariant() { return true; } }</code></pre></noscript>

También deberemos añadir un poco de configuración al módulo de la aplicación para que se use esta nueva implementación. Esto se hace en el método serviceOverride de la clase AppModule.java, donde también en el método contributeApplicationDefaults configuramos los símbolos que se usarán al generar las URLs entre ellos el dominio del CDN.

<noscript><pre><code>package es.com.blogspot.elblogdepicodev.plugintapestry.services; ... public class AppModule { private static final Logger logger = LoggerFactory.getLogger(AppModule.class); public static final String CDN_DOMAIN_PROTOCOL = &quot;cdn.protocol&quot;; public static final String CDN_DOMAIN_HOST = &quot;cdn.host&quot;; public static final String CDN_DOMAIN_PORT = &quot;cdn.port&quot;; public static final String CDN_DOMAIN_PATH = &quot;cdn.path&quot;; ... public static void contributeServiceOverride(MappedConfiguration&lt;Class, Object&gt; configuration, @Local HibernateSessionSource hibernateSessionSource) { configuration.add(HibernateSessionSource.class, hibernateSessionSource); // Servicio para usar un CDN lazy, pe. con Amazon CloudFront configuration.addInstance(AssetPathConverter.class, CDNAssetPathConverterImpl.class); if (isServidorJBoss(ContextListener.SERVLET_CONTEXT)) { configuration.add(ClasspathURLConverter.class, new WildFlyClasspathURLConverter()); } } public static void contributeApplicationDefaults(MappedConfiguration&lt;String, Object&gt; configuration) { ... configuration.add(CDN_DOMAIN_PROTOCOL, &quot;http&quot;); configuration.add(CDN_DOMAIN_HOST, &quot;s3-eu-west-1.amazonaws.com&quot;); configuration.add(CDN_DOMAIN_PORT, null); configuration.add(CDN_DOMAIN_PATH, &quot;cdn-plugintapestry&quot;); } ... }</code></pre></noscript>

Estás serían las URLs antiguas y nuevas con la implementación del AssetPathConverter:

Así de simple podemos cambiar el comportamiento de Tapestry y en este caso emplear un CDN, esta implementación es sencilla y suficiente pero perfectamente pordríamos implementarla con cualquier otra necesidad que tuviesemos. El cambio está localizado en una clase, son poco más que 46 líneas de código pero lo mejor es que es transparente para el código del resto de la aplicación, ¿que más se puede pedir?

Referencia:
Libro PlugIn Tapestry
Documentación sobre Apache Tapestry

http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/AssetPathConverter.html
https://code.google.com/p/corner/source/browse/corner3/trunk/src/main/java/corner/asset/services/impl/CDNAssetPathConverterImpl.java
http://aws.amazon.com/es/cloudfront/dynamic-content/
http://stackoverflow.com/questions/16914673/serve-images-outside-web-application
https://issues.apache.org/jira/browse/TAP5-2201
http://apache-tapestry-mailing-list-archives.1045711.n5.nabble.com/T5-some-automagical-way-to-export-versioned-assets-to-CDN-td2399058.html
http://apache-tapestry-mailing-list-archives.1045711.n5.nabble.com/General-CDN-problem-that-came-up-with-Tree-component-td5671331.html
http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/DownloadDistS3AndCustomOrigins.html
http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/PrivateContent.html

» Leer más, comentarios, etc...

Picando Código

Juegos en GNU/Linux: GOG.com provee soporte oficial

July 25, 2014 02:52 PM

Siendo el pedido más popular del wishlist de su comunidad, GOG.com provee soporte oficial para la plataforma GNU/Linux. GOG.com es una plataforma de distribución digital de videojuegos, un servicio para comprar y descargar juegos así como una comunidad de videojugadores.

GOG.com GNU/Linux

GOG.com GNU/Linux

Me hice una cuenta en GOG en 2011 porque había varios juegos clásicos disponibles a precios sumamente baratos que funcionaban bastante bien con Wine. Incluso llegué a comprar en una oferta Assasin’s Creed para PC y logré jugarlo de forma bastante decente sin tener una partición con Windows en mi computadora.

Volviendo a mi biblioteca, ya veo uno de los juegos disponible en mi sistema:

Gog.com biblioteca

Gog.com biblioteca

Tras la demanda de la comunidad, y seguramente con la presión de Valve con Steam para GNU/Linux y sus Steam Machines (computadoras con el sistema operativo SteamOS basado en GNU/Linux), GOG anunció hace un tiempo que también soportaría nuestro querido S.O. libre. Pensaban esperar a tener al menos 100 juegos para el lanzamiento. Pero teniendo 50 títulos -tanto clásicos como nuevos- prontos para distribución, infraestructura y un equipo de soporte técnico entrenado a la espera, decidieron publicarlo ya.

Cabe destacar que los juegos de GOG no tienen DRM, una ventaja respecto a Steam. Los juegos proveen dos opciones para descargar, un archivo tar.gz que debería funcionar en cualquier distro y paquetes .deb para las dos distros más populares entre los usuarios: Ubunty y Linux Mint. Van a soportar las versiones LTS actuales y futuras.

No solo van a distribuir videojuegos que ya tienen su port para Linux, también están trabajando en traer más juegos al sistema. Con motivo de su lanzamiento, tienen más de la mitad de estos juegos a un descuento del 75% de su precio.

Ya no quedan dudas ni falta de pruebas de que Linux es una plataforma viable para jugar. Esperemos que esto le traiga más popularidad y aportes al ecosistema del software libre.

Personalmente vengo jugando poco en la computadora, ya que volví a mi primer amor: Nintendo. El juego que más sigo jugando frecuentemente es Kingdom Rush en Steam. Pero sigo atento a nuevos lanzamientos en materia de juegos para PC para ver si alguno llama mi atención…

» Leer más, comentarios, etc...

Arragonán

Semana 319

July 23, 2014 11:53 PM

Muy tarde, pero prefería no dejar que se unieran otras 2 semanas de retrospectiva semanal. Esta semana nada de saraos, más allá la boda de uno de los mejores desarrolladores que conozco (debo decir que fue un fiestón de los buenos :)).

Pero os cuento que al fin terminé con The Agile Samurai, libro que compré hace ya un buen puñado de meses y que he ido leyendo muy a ratos, es un libro ameno y que se lee rápido pero al no haber sido constante en su lectura me ha costado mucho más que a cualquier persona normal XD.

The Agile Samurai

Portada de The Agile Samurai

Es un libro que me hubiera aportado mucho más si lo hubiera leído hace 3 o 4 años. No es que me parezca un libro con mal contenido, ni mucho menos, simplemente que no me ha descubierto grandes cosas ni pienso que se meta mucho en detalle de los diferentes temas de los que trata. El libro se centra en cuestiones de equipos, clientes, conceptualización (hablando sobre agile inception)… y pasa un tanto de puntillas por el tema de ciertas prácticas técnicas. Os recomiendo un post de Yeray Darias que tiene más detalle sobre el contenido.

En mi opinión un libro que resume bastante bien muchas cuestiones agilistas principalmente de temas que consideraría algo más de gestión, y por ello creo que es ideal a modo de introducción al mundillo (desde luego que mucho mejor que muchos cursos de dudosa calidad que se imparten por ahí), además de para perfiles técnicos acostumbrados las prácticas técnicas para empezar a conocer prácticas de gestión.

No lo usaré como referencia, pero es posible que algún capítulo termine releyéndolo en algún momento para repasar algunas cosas que en mi día a día no suelen ser habituales.

Sobre los proyectos en marcha:

  • Estuve dándole una vuelta al rediseño de minchador, que lo teníamos parado. Así como empezar a plantear hacer algún tipo de demo a modo de documentación para enseñárselo a colaboradores y a algunos beta testers.
  • En Nort estuve trabajando en una aplicación para la integración con facebook para publicar ciertos contenidos. También tuvimos un pequeño code review para ponernos de acuerdo en algunos convenios para la organización del código, y a raíz de eso tocó hacer algo de refactoring, claro.
  • Solucioné algún bug de mhop y estuve empezando a trabajar en el flujo de aprobación de campañas.
  • También he empezado a replantearme mi idea de Biera Solutions, que fundé con idea de dedicar a hacer producto propio exclusivamente, para utilizar la marca también en el desarrollo a terceros (más allá de mi persona como freelance).

Buena semana

» Leer más, comentarios, etc...

Cuaderno de software

Accidental versus essential complexity

July 23, 2014 07:02 AM

¿Cuántas veces te pasa que, al tener implementada la solución a un problema, te das cuenta de que el problema era mucho más fácil de lo que parecía y que ahora podrías implementar una solución mucho más sencilla? ¿Cuánta de la complejidad de una solución es inherente al propio problema y cuánta se produce por la propia solución planteada?

La complejidad intrínseca de un problema es difícil de abordar. No la has creado tú. Tienes que convivir con ella. No puedes actuar sobre ella a corto plazo. Es verdad que con el paso de los años, van surgiendo técnicas que ayudan a disminuirla. Pero sólo con el paso de las décadas se dan mejoras significativas.

La complejidad accidental de un problema es mucho más fácil de abordar. La has creado tú (o tu organización, o tu cliente…). Puedes actuar sobre ella a corto plazo y disminuirla de manera significativa.

La buena noticia es que casi toda la complejidad de lo que hacemos es complejidad accidental. Ocurre en los procesos que nos autoimponemos, en los análisis y diseños que hacemos y en el código que escribimos. Tenemos la certeza de que:

  • normalmente cuando planteamos un problema no llegamos al fondo del POR QUÉ o PARA QUÉ se está abordando ese problema. Lidiamos muchas veces con los efectos del problema en lugar de ir a la causa.
  • siempre que se plantea un proceso ligero, en pocos pasos, éste tiende a complicarse con el tiempo. Cada vez que ocurre una excepción que el proceso no contempla, se añade una nueva fase al proceso sin tener en cuenta que la mayoría de las veces que se ejecute, al no darse esa excepción, esa fase será una pérdida de tiempo.
  • cuando programamos, planteamos una serie de abstracciones en función de nuestro entendimiento del problema. Cuando solucionamos ese problema, adquirimos un entendimiento del mismo que se puede adquirir de ninguna otra manera que no sea, precisamente, hacer el acto de solucionarlo. Ese entendimiento nos hace ver que las abstracciones que planteamos no iban a la esencia y por lo tanto son demasiado complejas.
  • el diseño de interfaces es especialmente clave en estos casos. Las interfaces normalmente reflejan los procesos de negocio. Si el proceso es complejo, la interfaz es compleja. Si la interfaz es compleja, la programación que haya por detrás también.

En desarrollos iterativos e incrementales, si de base planteamos procesos, interfaces y código complejos, la complejidad del problema lo único que hará es crecer de manera exponencial.

Si uno es consciente de esto, puede actuar sobre ello. Por eso es importante establecer mecanismos que busquen la simplicidad y la esencia de las cosas. Una lista no exhaustiva sería:

  • pregúntate POR QUÉ y PARA QUÉ 5 elevando a la 5 veces. Usa técnicas de análisis de causa raíz.
  • plantea siempre la solución más sencilla que se te ocurra. Iterativamente, intenta reducirla al absurdo y usa a expertos para que le tiren piedras. Si no se cae, es la solución a implementar.
  • antes de automatizar procesos, replantea los procesos. Si automatizas la mierda, tendrás mierda automática.
  • refactoriza el código. Refactorizar es entender que generamos complejidad accidental y que una vez vista la solución existe una manera de hacer las cosas más sencillas. También es entender que no simplificarlo ahora, implica generar más complejidad en el futuro.

» Leer más, comentarios, etc...

Variable not found

#if DEBUG en Javascript (bueno, o algo así)

July 22, 2014 11:55 AM

DebugHay veces que desde Javascript nos interesa ejecutar un código u otro en función de si la ejecución se está produciendo en un servidor de desarrollo o en uno de producción. Por ejemplo, en el primer caso suele ser interesante disponer de logs o herramientas de ayuda a la depuración, mientras que en el segundo lo normal es que queramos introducir código más eficiente y sin este tipo de condimentos.

En este post vamos a ver algunas técnicas muy básicas que nos permitirán ejecutar un código u otro en el lado cliente de aplicaciones ASP.NET MVC (o ASP.NET en general) en función del modo de compilación.

1. El lado servidor

En el lado servidor es bastante fácil de conseguir porque disponemos de directivas de compilación que nos permiten detectar si estamos generando los binarios en modo depuración:
// HomeController.cs
public ActionResult Index()
{
#if DEBUG
ViewBag.CompilationInDebugMode = true;
#else
ViewBag.CompilationInDebugMode = false;
#endif
return View();
}

@* Home/Index.cshtml *@
<p>Compilation in debug mode: @ViewBag.CompilationInDebugMode</p>
También podemos conocer en tiempo de ejecución si ASP.NET está compilando en dicho modo (compilation debug="true" en el web.config):
@* In a Razor view: *@
<p>ASP.NET debugging: @Context.IsDebuggingEnabled</p>
Normalmente esta última vía será la que utilicemos, pues es la que suele indicar si estamos ejecutando sobre un servidor de desarrollo o no, básicamente porque el cambio en el web.config del valor del parámetro debug a false es uno de esos pasos que nos han recomendado desde el principio de los tiempos al pasar una aplicación a producción. Y seguro que todos lo hacemos, ¿verdad? ;-)

2. ¿Y el lado cliente?

En el lado cliente, debido a la propia naturaleza dinámica del lenguaje Javascript, no existe una compilación en modo depuración; bueno, de hecho no existe ni siquiera compilación como tal ;-), por lo que tenemos que recurrir a algunos truquillos para conseguir emular este comportamiento.

2.1. Cargar scripts diferentes en función del modo de depuración de ASP.NET

Una posibilidad muy sencilla, y válida en algunos escenarios, es tener un archivo de script diferente para cada modo de compilación. De hecho, es empleada por muchas bibliotecas Javascript, que se distribuyen en forma de archivo .js para la versión de producción, normalmente ya compactada y minimizada, y .debug.js para la versión a utilizar durante el desarrollo.

Por ejemplo, supongamos que tenemos los dos siguientes archivos:
// File: Myscript.debug.js (development script)
function log(msg) {
console.log(msg);
}

// =====================================

// File: Myscript.js (production script)
function log(msg) {
// Nothing to do
}
Para cargar manualmente un archivo u otro en función del modo de compilación bastaría con hacer lo siguiente a la hora de referenciarlo:
<script src="~/scripts/myscript@(Context.IsDebuggingEnabled? ".debug": "").js">
</script>
Sin embargo, si utilizamos bundles es aún más sencillo: de serie, el sistema de bundling ya distinguirá entre las distintas versiones de forma automática basándose en la extensión de los archivos. Por ejemplo, continuando con el caso anterior, al añadir un bundle como el siguiente el mismo componente de optimización seleccionará el archivo myscript.debug.js cuando la depuración de ASP.NET esté activada, y myscript.js en caso contrario:
bundles.Add(new ScriptBundle("~/bundles/myscript").Include(
"~/Scripts/myscript.*"
));
La referencia desde la vista en este caso sería así de simple:
@Scripts.Render("~/bundles/myscript")

2.2. Detección inline del modo de depuración

Pero no siempre el grano es tan grueso como en el apartado anterior. Muy frecuentemente no nos interesará sustituir completamente un script por otro en función del modo de compilación, sino conocer simplemente el modo en el que estamos para tomar una decisión a nivel de código. Esto nos permitiría escribir código como el siguiente:
function doSomething() {
if (DEBUG_MODE) {
log("Entering doSomething()");
}
// ... Code
if (DEBUG_MODE) {
log("Exiting doSomething()");
}
}
Por tanto, nuestro problema se reduce a establecer esa constante DEBUG_MODE a un valor que evalúe a cierto cuando estemos ejecutando sobre una aplicación compilada en modo depuración y a falso en caso contrario. Y aplicando lo visto anteriormente, podríamos conseguirlo de varias formas.

Una, realmente sencilla de implementar, consistiría en introducir un conciso bloque de scripts antes de cargar las bibliotecas o scripts que usen este valor, por ejemplo en el encabezado del layout o página maestra del sitio web, más o menos con lo siguiente:

@* Razor Layout *@
...
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My ASP.NET Application</title>
@Styles.Render("~/Content/css")
@Scripts.Render("~/bundles/modernizr")
<script>
window.DEBUG_MODE = @(Context.IsDebuggingEnabled? "true": "false");
</script>
</head>
...
Otra posibilidad sería establecer el valor de DEBUG_MODE en un script independiente del que tendríamos dos versiones, con extensiones .debug.js y .js, que respectivamente lo inicializarían a true y false, y usar cualquiera de las dos técnicas descritas anteriormente para cargar uno u otro en función del modo de compilación.

Publicado en Variable not found.

» Leer más, comentarios, etc...

Cuaderno de software

Un entorno productivo en pareja

July 22, 2014 07:16 AM

IMG_1711

 

Ésta foto tiene ya un tiempo. Es de finales de 2013 (ya me cuesta publicar artículos, a ver si cojo un poco de inercia… :-P). En esa época a Guille y a mí nos cayó la primera versión de un potente software no-tan-big data, a hacer con AngularJS/Javascript, Elastic Search y AKKA, alrededor de un dominio que desconocíamos completamente y donde teníamos difícil encontrar a alguien que nos lo explicara, con una deadline inminente.

Si en un escenario aplica lo de typing is not the bottleneck, creo que es en éste. Con lo que decidimos que en lugar de estar levantándonos de nuestro sitio cada cinco minutos a preguntarnos cosas el uno al otro o tomar decisiones técnicas sin contrastar, íbamos a hacer todo el trabajo en pareja y favorecer el ancho de banda de la comunicación entre nosotros muy por encima del ancho de banda de tecleo.

En la foto se puede ver lo que para mí es un entorno ideal para trabajar el software:

  • un ordenador principal con dos teclados y dos ratones y espacio más que de sobra para estar cómodos y centrados frente a las pantallas.
  • un ordenador secundario. Esto es clave. En un escenario de incertidumbre máxima te atascas cada dos minutos y tienes que guguelear a mansalva. Si estás con un sólo ordenador, uno busca, y el otro tiene que andar aguantándose las ganas de buscar cosas distintas. Lo mejor es buscar en paralelo e ir comentando lo que se encuentra.
  • panel físico para gestionar el To Do. Según avanzábamos, íbamos redefiniendo una y otra vez el trabajo a hacer. Destruyendo post-its y creando unos nuevos con nuestra nueva visión de las tareas a realizar. Para nosotros era clave poder hacer esto sin los impedimentos de una herramienta electrónica como Jira, mucho más lenta. Máxime cuando la trazabilidad, en éste punto, no nos servía para nada.
  • pared pintable para discutir ideas y diagramar cosas. Tuvo mucho flow el poder discutir una idea, pintarla, e ir implementándola teniéndola siempre de referencia y pudiéndonos dar la vuelta en cualquier momento a discutir cualquier aspecto.

Es fácil enamorarse de ésta forma de trabajar. Y, al enamorarte, perder un poco la perspectiva. El sistema nos funcionó bien para su principal función: maximizar la información radiada y la comunicación entre nosotros como estrategia para abordar un producto de alta incertidumbre técnica y de dominio. Lo que no nos funcionó:

  • fuimos incapaces de mantener un sólo “to do” en el panel. Como éramos novatos en todo, aprendíamos una manera más decente de hacer las cosas cada media hora, y todo el código anterior nos parecía refactorizable (y lo apuntábamos al “to do”). Al final manteníamos el panel, un archivo todo.txt en el intellij, y las anotaciones //TODO:  en el propio código. Aunque planteábamos sesiones dedicadas sólo a fundirnos esas listas, no lo conseguíamos.
  • hacer pairing el 100% del tiempo trabajando a veces en jornadas de 10 horas es demasiado. Ya sé que esto, leído, puede sonar bastante evidente. Pero cuando estás dándolo todo, cansado, hay cosas de las que no te das cuenta. Por ejemplo, yo adquirí muchos conocimientos de “angularjs pasivo“. Como Guille es más rápido que yo pensando y empezó el proyecto manejando más de AngularJS, yo solía ser un mero espectador cuando encontrábamos una solución a un problema en JS. No soy manco: a veces se me ocurría la solución a mí… pero normalmente (especialmente cuando estábamos cansados) la implementaba él porque le suponía menos esfuerzo. Con lo que yo no terminaba de asimilar las cosas.

Lecciones aprendidas. Cómo y cuando hacemos pairing ahora:

  • hacemos pairing siempre que lo que nos impide avanzar es el conocimiento (ya sea técnico o de dominio).
  • hacemos pairing siempre que se va a tomar una decisión de diseño importante en la que se va a basar el proyecto (cómo vamos a organizar los componentes, si se va a usar tal o cuál patrón, estrategia de testeo, etc…)
  • siempre que hacemos pairing tenemos una lista preparada con el orden del día. Y sí: normalmente hacemos pairing de jornadas completas, preparando el entorno previamente para ser productivos y tener toda la información disponible y la posibilidad de tener discusiones productivas.
  • es importante no hacer pairing cuando quieres asimilar algo que has aprendido.

Como consecuencia de todo esto, en el último proyecto que hemos hecho juntos habremos hecho un día a la semana de trabajo en pareja. Al menos durante la fase principal del proyecto, acumulando en ese día las decisiones de diseño y la lista de cosas que queríamos aprender del otro. Inventándome la cifra, diré que habremos hecho un 10-15% de pairing.

¿100% o 15% de pairing? Para mí aquí ( como siempre) la clave es el contexto. Y el punto de vista desde el que analizar ese contexto, es el del conocimiento. Si analizo el proyecto en el que hicimos 100% de trabajo en pareja y soy sincero conmigo mismo, no creo que hubiéramos acabado antes ni mejor haciéndolo por separado. No creo que hubiera salido más barato ( argumento clásico en contra del trabajo en pareja). Sin embargo, si analizo el proyecto donde hemos hecho 10-15% de pairing, si que creo que más tiempo hubiera supuesto perder el tiempo. Y también sé que trabajar más por mi cuenta en AngularJS me ha hecho mucho más ágil e independiente.

 

» Leer más, comentarios, etc...

Variable not found

Enlaces interesantes 167

July 21, 2014 02:26 PM

Enlaces interesantesAhí van los enlaces recopilados durante la semana pasada, espero que os resulten interesantes ;-)

.Net

ASP.NET

Azure / Cloud

Conceptos/Patrones/Buenas prácticas

Data access

Html/Css/Javascript

Visual Studio/Complementos/Herramientas

Cross-platform

Otros

Publicado en Variable not found

» Leer más, comentarios, etc...

Koalite

Tres años de Koalite

July 21, 2014 07:39 AM

Este mes se cumple el tercer aniversario de este pequeño proyecto personal. Son tres años en los que he escrito un montón de tonterías (222, concretamente), he conocido a mucha gente, he discutido con casi todos los que se han dejado y, sobre todo, he aprendido mucho.

¿Por qué?

Al hilo de un reciente encuentro virtual entre varios autores de blogs, Rubén Fernández escribía en su blog “¿y por qué tengo un blog?“.

Hay muchos motivos para escribir un blog. Hay quien busca crear una marca personal, hacer marketing de contenidos para otros, o incluso intentar ganar algún premio, pero la mejor motivación que puedes encontrar es disfrutar escribiéndolo. Si no disfrutase escribiendo, estoy seguro de que no sería capaz de sacar un hueco todas las semanas para escribir un nuevo post.

Es verdad que mantener un blog ayuda a aprender cosas nuevas, a conocer gente interesante, a potenciar una “marca personal” (para los que les preocupe) y a unas cuantas cosas más. Esos factores pueden ser lo que te hagan a dar el paso para empezar a escribir un blog, pero cuando llevas un tiempo haciéndolo, es fundamental que disfrutes con ello.

Eso de escribir para disfrutar implica también escribir para uno mismo, y por eso cada vez es más fácil encontrar en este blog posts que no interesan a nadie (bueno, o a casi nadie), pero, sinceramente, disfruto más escribiendo eso que un tutorial sobre la tecnología de moda que tenga 500 veces más visitas (y hablo con conocimiento de causa ;-))

…y ¡vacaciones!

No se me ocurre mejor forma de celebrar este tercer aniversario que tomarme unas semanitas de descanso. Este año se me ha hecho muy largo, tanto personal como profesionalmente, y ya me va tocando desconectar (un poco, que si no me aburro), relajarme, y resolver algunos asunto más mundanos que llevo posponiendo tiempo, así que, por lo menos hasta mediados de agosto, no creo que publique mucho.

De todas formas, como se suele decir, la cabra tira al monte y seguro que durante este tiempo seguiremos discutiendo de vez en cuando en twitter.

¡Felices vaciones a todos!

lago_enol

Imagen de Dominik Morbitzer en flickr

No hay posts relacionados.

» Leer más, comentarios, etc...

Cuaderno de software

Panel Time Control

July 21, 2014 06:37 AM

Éste es un pequeño apunte para recordar un panel que nos ha funcionado muy bien a Guille y a mí en un desarrollo que hemos hecho a presupuesto cerrado.

la foto

 

El panel es una especie de Story Map traspuesto. Cada columna es una entrega (diseñamos una estrategia con dos demos y una entrega final). Cada fila es un vertical de la aplicación (un “context boundary” que se diría en DDD).  La primera celda de cada fila contiene un glosario de términos del vertical y una lista de dudas o incertidumbre a ir aclarando.

No dimos color a los post-it´s. Distinguimos la funcionalidad del spike o tarea técnica poniendo de título al post-it “spike”. Viéndolo en retrospectiva hubiera estado bien darles un color de post-it distinto. Lo que si distinguimos fue el feedback/detalle descubierto en las demos(post-it amarillo), de la funcionalidad original (verde). Para visualizar si una funcionalidad estaba terminada, simplemente la tachábamos.

Para visualizar las horas que habíamos empleado (clave en éste proyecto a precio cerrado) usamos el post-it fucsia. Cada semana actualizábamos las horas, moviendo el post-it hacia la derecha proporcionalmente. De tal manera que era fácil ver si estábamos en el último tercio de horas y todavía en el segundo tercio de funcionalidades (vamos tarde), etc…

En definitiva, nos ha funcionado muy bien para controlar el release plan y controlar el tema económico. También para gestionar la incertidumbre del proyecto y el detalle de las funcionalidades.

Creo que éste panel no funcionaría en cualquier contexto. El panel obedece a un escenario donde no hay bugs (porque todo el trabajo diseñado es anterior a que se ponga en producción de verdad el software) y donde al estar dos desarrolladores es fácil saber quién está haciendo qué.

 

» Leer más, comentarios, etc...

Blog Bitix

Anotación Cached de Apache Tapestry

July 19, 2014 07:00 PM

Apache Tapestry

Como he comentado en un artículo anterior sobre los modelos push y pull empleados en los motores de plantillas donde comentaba algunas diferencias entre ellos, en el modelo pull es la vista la que pide los datos al controlador y no el controlador el que proporciona los datos a la vista como se hace en el modelo push. Un problema que puede plantear el que la vista pida los datos al controlador es que si la devolución de los datos solicitados son costosos en tiempo del cálculo, carga para el sistema en CPU o memoria, o intensivos en entrada/salida de disco o red y se piden varias veces puede suponer como resultado que el tiempo empleado para generar la página sea elevado o la aplicación consuma recursos innecesarios.

Apache Tapestry que emplea el modelo pull dispone de la anotación Cached que permite cachear el resultado de un método a nivel de componente y página durante la generación de la misma. Su uso sería el siguiente:

<noscript><pre><code>package info.blogstack.pages; import info.blogstack.entities.Post; import info.blogstack.entities.Source; import info.blogstack.misc.Globals; import info.blogstack.misc.Utils; import info.blogstack.services.MainService; import info.blogstack.services.dao.Direction; import info.blogstack.services.dao.Pagination; import info.blogstack.services.dao.Sort; import java.util.ArrayList; import java.util.List; import org.apache.tapestry5.annotations.Cached; import org.apache.tapestry5.annotations.Property; import org.apache.tapestry5.ioc.annotations.Inject; public class Label { private Object[] context; @Property private Integer page; @Inject private MainService service; @Property private Source source; @Property private info.blogstack.entities.Label label; @Property private Post post; void onActivate(Object[] context) { this.context = context; } Object[] onPassivate() { return context; } void setupRender() { String hash = Utils.getHash(new Object[] { context[0] } ); label = service.getLabelDAO().findByHash(hash); page = (context.length &gt;= 3) ? (Integer) context[2] : null; page = (page == null) ? 0 : page; } /** * Método que devuelve las articulos publicadas o actualizadas más recientemente de una etiqueta. */ public List&lt;Post&gt; getPosts() { List&lt;Sort&gt; sorts = new ArrayList&lt;&gt;(); sorts.add(new Sort(&quot;date&quot;, Direction.DESCENDING)); Pagination pagination = new Pagination(Globals.NUMBER_POSTS_PAGE * page, Globals.NUMBER_POSTS_PAGE * (page + 1), sorts); return service.getPostDAO().findAllByLabel(label, pagination); } @Cached(watch = &quot;label&quot;) public Long getPostsCount() { return service.getPostDAO().countBy(label); } public boolean isFirstPage() { return page == 0; } public boolean isLastPage() { return (page + 1 &gt; Globals.NUMBER_PAGES_LABEL || getPostsCount() &lt;= Globals.NUMBER_POSTS_PAGE * (page + 1)); } public Object[] getPreviusContext() { return (page - 1 &lt;= 0) ? new Object[] { label.getName() } : new Object[] { label.getName(), &quot;page&quot;, page - 1 }; } public Object[] getNextContext() { return (page + 1 &gt; Globals.NUMBER_PAGES_LABEL) ? new Object[] { label.getName() } : new Object[] { label.getName(), &quot;page&quot;, page + 1 }; } }</code></pre></noscript>

En este ejemplo cada vez que se llama a los métodos getPosts, getPostsCount se accede a una base de datos (o sistema externo) que lanza una consulta, supongamos, costosa de calcular o que simplemente es innecesaria hacerla varias veces. Usando la anotación Cached podemos hacer la aplicación más eficiente evitando las segundas llamadas a los métodos. Si el componente Label del ejemplo se usa dentro de un bucle de un componente loop y como parámetros se le van pasando varios labels las llamadas a los métodos getPosts y getPostCount se realizarán solo para cada valor diferente.

Algunas veces puede interesarnos que el cacheo dependa de un dato, es decir, que para cada valor de un dato la anotación Cached devuelva diferentes resultados. Y esto es lo que se hace en el ejemplo con el parámetro watch de la anotación, por cada valor de la propiedad label el resultado probablemente sea diferente pero nos interesa que el método solo se ejecute una vez por cada diferente valor, dado que los artículos y el número de ellos únicamente variarán en función de esta propiedad. Esto también puede ser usado para que solo se evalúe los métodos una vez por iteración de un bucle estableciendo la expresión watch al índice del bucle.

<noscript><pre><code>&lt;html t:type=&quot;layout&quot; xmlns:t=&quot;http://tapestry.apache.org/schema/tapestry_5_4.xsd&quot; xmlns:p=&quot;tapestry:parameter&quot;&gt; &lt;t:postcomponent post=&quot;post&quot; excerpt=&quot;true&quot;/&gt; &lt;t:remove&gt; Llamadas ádicionales para obtener los artículos, devolverán los mismos datos. &lt;/t:remove&gt; &lt;t:postcomponent post=&quot;post&quot; excerpt=&quot;true&quot;/&gt; &lt;t:postcomponent post=&quot;post&quot; excerpt=&quot;true&quot;/&gt; &lt;/html&gt;</code></pre></noscript>

Aún así, la anotación Cached funciona a nivel de petición, cada vez que que se haga una petición a la aplicación y se llame al método anotado por primera vez y por cada valor de la expresión watch se ejecutará el método. Si tenemos muchas peticiones o un determinado componente tarda mucho en generar su contenido, por ejemplo, porque depende de un sistema externo lento (base de datos, http, …) quizá lo que debamos hacer es un componente que almacene durante un tiempo el contenido que genera y sea devuelto en múltiples peticiones, de modo que evitemos emplear un tiempo costoso en cada petición. Para ello, podríamos desarrollar un componente que usase una librería de cahce como por ejemplo EHCache.

Referencia:
Libro PlugIn Tapestry
Documentación sobre Apache Tapestry

» Leer más, comentarios, etc...

Cuaderno de software

Cynefin: ¿complejo o complicado?

July 17, 2014 06:38 AM

Siguiendo un poco con la importancia de distinguir entre simple, complejo, complicado, etc… Es muy interesante el marco de decisión( o de análisis) Cynefin.

Cynefin_framework_Feb_2011

¿Cómo tomar decisiones o analizar un problema?

  • Si existe una relación clara entre causa y efecto, el escenario es simple. Puedes percibir la situación, categorizarla y, en función de la categoría, responder. Existen unas recetillas que te dicen en función de la categorización cuál es la mejor respuesta (best practices).
  • Si existe una relación entre causa y efecto, pero no está clara, el escenario es complicado. Requiere experiencia y análisis antes de actuar. Pueden existir unas buenas prácticas pero no una sola manera de hacer las cosas bien.
  • Si existe una relación entre causa y efecto, pero sólo eres capaz de establecerla a posteriori y, además, las consecuencias de lo que está ocurriendo no son predecibles, el escenario en el que te encuentras es complejo. La situación requiere probar varias cosas, coger feedback y actuar. La manera buena de hacer las cosas va emergiendo en función del feedback. No es conocida a priori.
  • Si no existe ninguna relación entre causa y efecto, ni siquiera analizando las cosas a posteriori, estás en una situación caótica. Probar para obtener feedback no sirve porque al repetir no necesariamente va a ocurrir lo mismo. La única posibilidad es dar un paso al frente, actuar y ver qué pasa. Aquí eres siempre un novato y no puedes extraer buenas prácticas en función de la experiencia.

Creo que en general nos movemos entre lo complejo y lo complicado. Y que es difícil saber cuando pararte a analizar bien las cosas y cuando no analizarlas tanto e intentar hacer algo para tener feedback rápido. Me gusta la manera en la que Cynefin enmarca todo esto.

Para un análisis muy bueno sobre Cynefin y Agilismo el post de Liz Keogh.. y ¡ojo a la respuesta de Jon Reffies!

 

» Leer más, comentarios, etc...

xailer.info

Controles de usuario

July 16, 2014 11:24 AM

Estimados usuarios de Xailer,

La futura versión de Xailer incorporará una nueva funcionalidad que creemos puede ser de gran utilidad para todos vosotros y de la cual estamos muy satisfechos por el resultado obtenido, que es la creación automática de controles de usuario.

Actualmente Xailer ya permite la creación de controles de usuario que se pueden utilizar posteriormente en el IDE y en sus aplicaciones finales. De hecho el proyecto \Samples\Controls\Samples.xpj es un claro ejemplo del mismo. Dicho ejemplo crea todos los controles que puede encontrar en la pestaña ‘Samples‘ de la barra de controles. Para su utilización tan sólo deberá incluir en sus proyectos la librería \Xailer\lib\Samples. No obstante, muy pocos usuarios se han adentrado en la creación de controles propios, seguramente por creer que se trata de un proceso complicado y tedioso.

Para facilitaros las cosas un poquito más la nueva versión de Xailer incorporará un completo sistema de creación de controles automático en base a cualquier control que tengáis de cualquier formulario. La idea es que todas las propiedades que hayáis indicado en dicho control, junto con los recursos que utilice, se conviertan en un control adicional que se pueda utilizar en cualquier proyecto actual o futuro.

El funcionamiento es muy sencillo: Seleccionamos primeramente el control personalizado que deseamos crear como control propio, pulsamos el botón contextual del mismo y seleccionamos la opción de ‘Creación de control de usuario…‘. A continuación se nos mostrará el siguiente diálogo:

UserControlDialog

Tan sólo deberá indicar el nombre de la nueva clase, el tooltip que se le mostrará cuando lo pre-seleccione en el IDE, la imagen que se mostrará en la barra de controles y el nombre de la pestaña donde se mostrará el nuevo control. Incluso puede dejar todos los valores por defecto si lo desea.

El IDE creará, si no existiese, un nuevo proyecto de nombre ‘MyControls‘ en su carpeta de ‘Archivos de usuario\Componentes‘ (ver opciones generales) e irá introduciendo todos los controles de usuario que vaya creando. Dicho proyecto será el responsable de crear la DLL MyControls.dll para el IDE y de las librerias para su uso bien por MinGW o Borland C++. Observe como en el diálogo de creación de control usuario existe una casilla para abrir este nuevo proyecto de forma inmediata.

Para poder empezar a utilizar sus nuevos controles deberá cargar en el IDE la librería MyControls. DLL utilizando la opción de menú Componentes->Gestor de componentes.

UserControlGestorComponentes

El IDE de Xailer controla de forma automática los recursos que se requieran. Cuando se crea el control de usuario, sus recursos son copiados al proyecto ‘MyControls‘ y cuando el control es utilizado en cualquier otro proyecto sus recursos son a su vez copiados desde el proyecto ‘MyControls‘ a su proyecto de forma completamente transparente.

Esperamos que con esta nueva funcionalidad ya no tengáis motivos para no crear vuestros propios controles. ;-) Cualquier comentario al respecto será bienvenido.

Un cordial saludo

» Leer más, comentarios, etc...

Arragonán

Semanas 317 y 318

July 15, 2014 04:13 PM

Cosas de ir liado de trabajo, tener un puñado de otros compromisos y una parte de pereza para ponerme a escribir cuando tenía un rato; se me ha vuelto a juntar una semana sin publicar la retro semanal.

En estos 15 días: inauguración de piso, Geek’s Talks, Slap!, cierre del trimestre anterior, consultobirra con los amigos de Certicalia, boda de amigos, gestionar algún tema de viejos proyectos, el UXZaragoza

A destacar el UXZaragoza, claro. Fueron 4 charlas que creo que se complementaron muy bien, y me pareció interesante que en cada una de ellas se hablara desde las experiencias propias.

Adriano y Pablo hablaron de cómo han ido integrando técnicas de UX con las de desarrollo ágil, cosas que les han ido funcionando mejor y peor durante los 4 meses que llevan trabajando en spines: Las cosicas del Lean UX.

Guillermo hizo una charla en la que habló más que nada sobre experimentar (o zancochar :)), probar herramientas, técnicas… que quizás no se vayan a usar a corto plazo pero de las que quizás podamos sacar partido en el futuro: La locura, esa facultad fundamental.

Daniel expuso como trabajos de consultoría que a priori son de diseño o UX de producto digital, terminan teniendo impacto en el mundo físico. Básicamente en cambiar flujos de trabajo que ayudan a mejorar la experiencia de usuario del cliente: Service design de pueblo.

Verónica nos estuvo hablando de tests con usuarios de laboratorio, del modo de observación, de análisis cualitativos y cuantitativos, de analizar los resultados, etc: Falla rápido y barato, falla investigando.

Podéis ver la actividad en twitter con #uxzaragoza.

Y acerca de los trabajos en marcha:

  • Vuelta a dedicar algún tiempo a gestiones para proyectoSinNombre (un día igual lo cerramos y todo…).
  • Empecé ya con Nort, nuevo proyecto al que mayor dedicación le voy a dar durante los próximos meses. Entro a trabajar a modo de contractor con un equipo de varias personas con quienes estaremos desarrollando la aplicación usando Grails. Sobre el cliente y la temática del proyecto no puedo hablar, al menos por el momento.

    Ya hemos hecho el primer sprint, empezamos a implementar historias de usuario, hemos hecho la primera demo, la primera retro… y ver que, evidentemente, tenemos muchos puntos de mejora.

  • He hecho algunas cosas en mhop. Le he dedicado menos disponibilidad, pequeños detalles y algunas reuniones sobre el camino a seguir pensando en el medio plazo.

Buena semana.

» Leer más, comentarios, etc...

Variable not found

Novedades de SignalR 2.1 (y III): progreso de acciones

July 15, 2014 03:18 PM

image[2]La revisión 2.1 de SignalR ha incluido otra característica que puede ser interesante en determinados escenarios: la posibilidad de notificar al cliente del progreso de la ejecución de una acción invocada en el hub.

Aunque en los métodos que normalmente implementamos no tiene demasiada utilidad, sí puede ayudar a mejorar la interacción y experiencia de usuario cuando se trate de ejecutar métodos costosos como el siguiente:
public class GodsOfTheForest: Hub
{
[...]
public async Task<int> GetTheAnswerToLifeTheUniverseAndEverything()
{
for (var i = 0; i < 10; i++)
{
await Task.Delay(1000);
// Do something very important here
  }
return 42;
}
}
Hasta ahora, si desde el cliente llamábamos a este método del hub, sólo podíamos tomar el control cuando la llamaba finalizaba, bien obteniendo el resultado si todo había ido bien, o bien capturando el error producido. Por ejemplo, en Javascript lo conseguíamos con un código como el siguiente:
proxy.server.getTheAnswerToLifeTheUniverseAndEverything()
.done(function (result) {
console.log("The answer is: " + result);
})
.fail(function(err) {
console.log("Oops, something went wrong: " + err.message);
});

Notificación de progreso en SignalR 2.1

Para notificar el progreso de la ejecución en un método del hub, a partir de SignalR 2.1 tenemos que hacer básicamente dos cosas:
  • En primer lugar, añadir al método del hub un parámetro de entrada adicional de tipo IProgress<T>, que obligatoriamente debe ser el último del método. T es el tipo de dato que vayamos a utilizar para notificar el progreso al cliente; por ejemplo, si vamos a notificar un porcentaje, podríamos usar Progress<int>.
  • En segundo lugar, en el punto del código desde donde queremos realizar la notificación, invocar al método Report() del parámetro anterior suministrándole el valor de progreso deseado.
Pero seguro que queda más claro si lo vemos con un poco de código basándonos en el ejemplo anterior:
public async Task<int> GetTheAnswerToLifeTheUniverseAndEverything(
IProgress<int> progress)
{
for (var i = 1; i <= 100; i++)
{
await Task.Delay(1000);
progress.Report(i);
}
return 42;
}
De esta forma tan simple estaremos enviando al cliente el progreso de la operación :-) Por supuesto, podríamos enviar al cliente notificaciones de cualquier tipo, como en el siguiente ejemplo, donde usamos IProgress<string> para poder notificar con mensajes de texto:
public async Task<int> GetTheAnswerToLifeTheUniverseAndEverything(
IProgress<string> progress)
{
progress.Report("Just starting...");
await Task.Delay(1000);
progress.Report("Working hard...");
await Task.Delay(1000);
progress.Report("Almost done...");
await Task.Delay(1000);
progress.Report("One second to finish...");
await Task.Delay(1000);
progress.Report("Finished");
return 42;
}

Consumo desde clientes del hub

El consumo de los datos de progreso sería bastante simple desde nuestro cliente Javascript, simplemente usar este método progress() para especificar la función callback que recibirá los datos de progreso:
proxy.server.getTheAnswerToLifeTheUniverseAndEverything()
.progress(function (value) {
log("Progress: " + value + "%");
})
.done(function (result) {
log("The answer is: " + result);
});
Ah, y un detalle muy importante a este respecto: el uso de esta característica requiere la versión 1.7 o superior de jQuery, más que nada porque en el objeto Deferred que retornan las llamadas a las funciones del hub antes no existía el método progress() que estamos usando para recibir las notificaciones de progreso.

Desde clientes .NET es igualmente sencillo. Se han añadido sobrecargas al método Invoke() de forma que podemos indicar el código a ejecutar al recibir el progreso de la operación. El siguiente código muestra una llamada a un método que retorna un entero y cómo gestionar las notificaciones de progreso que nos llegan también en forma de número entero:
int result = await proxy.Invoke<int, int>(
"GetTheAnswerToLifeTheUniverseAndEverything",
(int progress) =>
Debug.WriteLine("Progress: " + progress + "%")
);
El primer parámetro genérico del método Invoke()es el tipo de datos que retornará la llamada al hub, como ha sido siempre hasta ahora, mientras que el segundo es el utilizado para notificar el progreso. Así, si los mensajes de progreso nos llegan en forma de cadena de caracteres, deberíamos usar:
int result = await proxy.Invoke<int, string>(
"GetTheAnswerToLifeTheUniverseAndEverything",
(string progress) => Debug.WriteLine(progress)
);
Bueno, pues con este post podemos dar por cerrada la serie sobre las novedades de SignalR 2.1. Hay algunas más, pero creo que no son tan interesantes como las que hemos ido viendo; si tenéis interés en conocerlas, podéis acudir al repositorio de Github de SignalR.

Publicado en Variable not found.

» Leer más, comentarios, etc...

Variable not found

Enlaces interesantes 166

July 14, 2014 11:45 AM

Enlaces interesantesAhí van los enlaces recopilados durante la semana pasada, espero que os resulten interesantes ;-)

.Net

ASP.NET

Conceptos/Patrones/Buenas prácticas

Data access

Html/Css/Javascript

Visual Studio/Complementos/Herramientas

Otros

Publicado en Variable not found

» Leer más, comentarios, etc...

Koalite

El lado oscuro de los gestores de paquetes

July 14, 2014 05:06 AM

Hace no tanto tiempo (aunque en la escala de internet pueda parecer siglos) gestionar las dependencias de un proyecto era algo relativamente complicado o, al menos, laborioso.

En aquellos lejanos y oscuros tiempos, para incluir en tu proyecto, por ejemplo, NHibernate, debías buscar la página de descarga correspondiente, elegir la versión correcta para tu versión de .NET, buscar posibles dependencias (log4net, Castle.DynamicProxy, etc.), cruzar los dedos para que estuvieran en el zip que te habías descargado de la página de NHibernate y, si no estaban, empezar a buscarlos en sus respectivas páginas de descarga, asegurándote de bajar las versiones exactas que necesitabas para NHibernate y rezando para que no hubiera colisiones entre unas versiones y otras, especialmente si había algún assembly firmado (recuerdo con especial “cariño” log4net y sus versiones 1.2.9 y 1.2.10). Finalmente copiabas todo con mucho cuidado en una carpeta .\lib al lado de los fuentes de tu proyecto y lo añadías a tu sistema de control de código fuente (subversion si tenías suerte, Visual Source Safe si no). Vamos, todo muy rápido, divertido y agradable.

Por suerte hoy en día prácticamente todas las plataformmas de desarrollo cuentan con gestores de paquetes para realizar esta gestión de dependencias y, lo que es más importante, se han convertido en la manera “oficial” y habitual de instalarlas. En realidad todo esto de los gestores de paquetes no es algo nuevo, existen desde hace mucho tiempo, pero en algunas plataformas como .NET su popularidad es relativamente reciente (que Microsoft publicase NuGet fue clave en una comunidad que había ignorado antes intentos como OpenWrap o Horn).

Utilizando un gestor de paquetes, todo el proceso de descarga e instalación de una dependencia que describía antes se reduce a algo como:

npm install underscore

Así de simple. Con sólo un comando el gestor de paquetes se conectará a un servidor que contiene un índice con los paquetes publicados y se descargará el paquete seleccionado, ejecutará los scripts de instalación adecuados y hará lo mismo recursivamente con las dependencias del paquete indicado.

Además, estos paquetes que has instalado quedan registrados en un fichero de configuración asociado al proyecto, por lo que existe un punto centralizado en el que revisar qué dependencias tienes instaladas y con qué versiones.

Visto así, es innegable que los gestores de paquetes suponen un gran avance para el desarrollo de software facilitando la gestión de dependencias y seguramente sean uno de los motivos por los cuales hoy en día se utilizan cada vez más componentes externos (muchos de ellos open source) en todo tipo de proyectos.

Cuidado con las dependencias que asumes

Desgraciadamente esta facilidad de uso también tiene puede tener consecuencias negativas y es importante tenerlas en cuenta.

Al ser tan sencillo añadir nuevas dependencias a un proyecto, es habitual encontrar proyectos con decenas de dependencias externas, cada una de ellas con sus propias dependencias. Esto no es algo malo en si mismo, a fin de cuentas, ¿para qué reinventar la rueda?, pero no hay que olvidar que cada dependencia que asumes en un proyecto es una dependencia que tienes que mantener.

Es tan fácil añadir un nuevo paquete que a veces, en lugar de implementar una función de 10 líneas de código, se acaba asumiendo una dependencia sobre una librería de miles de líneas de código para usar sólo esa función. El utilizar una librería externa, probada y ampliamente usada proporciona una sensación de seguridad que no siempre es real y, cuando se produce un problema, bien porque hay un bug en esa librería o porque hay problemas de compatibilidad entre dos librerías que estamos utilizando, depurarlo puede ser bastante complicado.

Cuando aparece un problema, no sirve de nada decir “el problema no está en mi código sino en la versión 1.9.10 de jQuery, yo no tengo la culpa”. De cara a tus usuarios y tus clientes, lo que no funciona es tu aplicacion, esa por la que ellos han pagado. Al final tienes que responder no sólo por tu código sino por el de todas las dependencias que estás utilizando.

Esto no quiere decir que no haya que utilizar librerías externas. Igual que no tiene mucho sentido ponerte a escribir tu propio servidor web y es más razonable usar IIS, Apache o lo que más te guste, no merece la pena invertir tiempo en escribir tu propio ORM cuando hay alternativas perfectamente válidas.

Lo que sí es necesario es ser consciente de que cada dependencia que asumes tiene un precio. Cuando programamos, si vemos una clase que recibe 12 parámetros en el constructor lo consideramos como algo problemático (o al menos algo que requiere cierta justificación), porque sabemos que esa clase tiene demasiadas dependencias, lo que aumenta su acoplamiento con el resto del sistema y complica su futuro mantenimento. Pues con las dependencias de los proyectos pasa algo parecido. Si un proyecto depende de 12 paquetes distintos, estamos introduciendo 12 puntos de problemas potenciales, por lo que más nos vale ser conscientes de ellos y tener un buen motivo para hacerlo.

Recuperación automática de paquetes

Todos los gestores de paquetes que conozco cuentan con alguna opción para volver a descargarse los paquetes desde un repositorio central. Si recordáis el flujo de trabajo que describía al principio del post, antes era frecuente almacenar los paquetes en alguna carpeta de proyecto e incluirlos en el control de código fuente junto con nuestro propio código. Hoy en día, esto no es lo habitual e incluso se considera una mala práctica, puesto que podemos incluir únicamente un fichero de configuración y descargar los paquetes bajo demanda de sus servidores de origen cuando los necesitemos.

La principal ventaja de esto es que el tamaño de nuestro repositorio de código será mucho menor, especialmente si los paquetes se distribuyen de forma binaria (como es habitual en .NET o Java), que es más difícil de comprimir y de almacenar usando deltas cuando hay cambios de versión.

En un mundo en el que el almacenamiento en disco es tan barato puede parecer que esto no es importante, pero cuando pensamos en el tráfico de red al clonar un repositorio, especialmente con sistemas de control de versiones distribuidos como Git o Mercurial en los que se clona el repositorio completo (y no sólo una versión), y en accesos a través de conexiones a internet en lugar de redes locales, sí que es un factor a tener en cuenta.

Como siempre, esto no es gratis y utilizar la recuperación automática de paquetes en lugar de almacenar las dependencias en el control de código fuente también tiene sus inconvenientes. Ahora, para poder compilar nuestro proyecto, dependemos de que el repositorio de paquetes esté disponible. Por suerte no es muy habitual que estos repositorios centrales se caigan, pero tampoco es imposible.

Esto se puede resolver creando un repositorio de paquetes propio, dentro de tu red local, con una copia de los paquetes que usas en tus proyectos. Es una buena opción, especialmente en organizaciones grandes donde compensa aprovechar las ventajas de no incluir las dependencias en el control de código fuente y se necesitan unas ciertas garantías de disponibilidad, pero montar estos servidores requiere algo de tiempo y es una pieza más del sistema a mantener.

Personalmente, cuando estoy desarrollando un proyecto profesionalmente, siempre incluyo todas las dependencias en el control de código fuente. En mi escenario concreto, con todos los desarrolladores trabajando en una misma red local, no me compensa el riesgo de tener que lidiar con un servidor externo caído o un paquete eliminado para ahorrarme 10 segundos al clonar un repositorio (operación que, por otra parte, tampoco es muy frecuente en nuestro caso).

Conclusiones

Los gestores de paquetes son un gran invento para facilitarnos las cosas al desarrollar aplicaciones porque nos permiten gestionar las dependencias de una manera fácil y cómoda, pero eso no debe hacernos olvidar que cada paquete que instalamos es una dependencia de nuestra aplicación por la que deberemos responder en el futuro.

La existencia de servidores centralizados desde los que descargar las dependencias simplifica el acceso a las mismas y puedo ayudarnos a reducir el almacenamiento en disco y el tráfico de red, aunque a cambio estaremos introduciendo una dependencia entre nuestros procesos y la disponibilidad de esos servidores, por lo que será necesario valorar si realmente merece la pena.

No hay posts relacionados.

» Leer más, comentarios, etc...

Blog Bitix

Plantillas en Apache Tapestry

July 11, 2014 06:54 PM

Apache Tapestry

Una página web está formada por un conjunto de páginas enlazadas entre ellas. Cada página está formado por un html diferente pero normalmente todas las páginas de una misma web comparten el mismo aspecto variando solo una sección donde está el contenido propio de la página. La cabecera de la página, el pie de la página o los menús de navegación suelen estar presentes en todas las páginas de la web y suelen ser los mismos.

En este artículo voy a explicar como crear un componente que nos de a todas las páginas un aspecto común de una aplicación usando apache Tapestry como framework web de tal forma que esa parte común no esté duplicada en la aplicación y pueda ser reutilizada fácilmente. En el caso de Blog Stack las páginas se componen de las siguientes partes.

El esquema de la plantilla será una cabecera, una barra de navegación con enlaces a diferentes secciones de la web, un menú lateral con contenido variable según la página, el contenido que variará según la página y un pie de página. Como todo componente de Apache Tapestry está formado de una clase Java y una plantilla. El componente puede tener diferentes parámetros, y en el caso del de la plantilla muchos para poder variar el contenido por defecto de las diferentes secciones de la página, estos son aside1, aside2, aside3, aside4.

<noscript><pre><code>package info.blogstack.components; import info.blogstack.entities.Adsense; import org.apache.tapestry5.BindingConstants; import org.apache.tapestry5.Block; import org.apache.tapestry5.ComponentResources; import org.apache.tapestry5.annotations.Import; import org.apache.tapestry5.annotations.Parameter; import org.apache.tapestry5.annotations.Property; import org.apache.tapestry5.ioc.annotations.Inject; import org.joda.time.DateTime; @Import(stack = &quot;blogstack&quot;, module = &quot;app/analytics&quot;) public class Layout { @Parameter(defaultPrefix = BindingConstants.LITERAL) @Property(read = false) private String title; @Parameter(defaultPrefix = BindingConstants.LITERAL) @Property(read = false) private String subtitle; @Parameter(defaultPrefix = BindingConstants.BLOCK) @Property private Block aside1; @Parameter(defaultPrefix = BindingConstants.BLOCK) @Property private Block aside2; @Parameter(defaultPrefix = BindingConstants.BLOCK) @Property private Block aside3; @Parameter(defaultPrefix = BindingConstants.BLOCK) @Property private Block aside4; @Parameter @Property private Adsense adsense; @Property private String page; @Inject ComponentResources resources; void setupRender() { page = resources.getPageName(); } public int getYear() { return DateTime.now().getYear(); } public String getTitle() { if (title == null) { return String.format(&quot;%s&quot;, getSubtitle()); } else { return String.format(&quot;%s | %s&quot;, title, getSubtitle()); } } public String getSubtitle() { return (subtitle == null) ? &quot;Blog Stack&quot; : subtitle; } public String getContentClass() { return (isAside()) ? &quot;col-xs-12 col-sm-12 col-md-8 content&quot; : &quot;col-xs-12 col-sm-12 col-md-12 content&quot;; } public boolean isAside() { return (aside1 != null || aside2 != null || aside3 != null || aside4 != null); } }</code></pre></noscript>

El archivo tml asociado al componente plantilla será el que genere el contenido html que se enviará al navegador del usuario. En esta plantilla se incluye una cabecera con el logo de la aplicación y una frase que lo describe, posteriormente está una barra de navegación con varios enlaces, con <t:body/> se incluye el contenido propio de la página que usa el componente plantilla y usando el componente <t:delegate/> se incluye el contenido de los diferentes bloques aside si se han personalizado en el uso de la plantilla, con el componente <t:if test=“aside”> se comprueba si hay algún aside usándose el método isAside de la clase Layout asociada al componente plantilla y del tml. Finalmente, está el pie que será común a todas las páginas que usen este componente.

<noscript><pre><code>&lt;!DOCTYPE html&gt; &lt;html xmlns=&quot;http://www.w3.org/1999/xhtml&quot; xmlns:t=&quot;http://tapestry.apache.org/schema/tapestry_5_4.xsd&quot; xmlns:p=&quot;tapestry:parameter&quot;&gt; &lt;head&gt; &lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=utf-8&quot;/&gt; &lt;meta t:type=&quot;any&quot; t:pagina=&quot;${page}&quot; /&gt; &lt;title&gt;${title}&lt;/title&gt; &lt;!-- Resources --&gt; &lt;link href=&quot;//fonts.googleapis.com/css?family=Open+Sans:400,700&quot; rel=&quot;stylesheet&quot; type=&quot;text/css&quot;/&gt; &lt;link href=&quot;//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css&quot; rel=&quot;stylesheet&quot; type=&quot;text/css&quot;/&gt; &lt;link href=&quot;/feed.atom.xml&quot; rel=&quot;alternate&quot; type=&quot;application/atom+xml&quot; title=&quot;Portada&quot;/&gt; &lt;link href=&quot;${context:images/favicon.png}&quot; rel=&quot;icon&quot; type=&quot;image/png&quot;/&gt; &lt;/head&gt; &lt;body&gt; &lt;header&gt; &lt;div class=&quot;container-fluid&quot;&gt; &lt;div class=&quot;row&quot;&gt; &lt;div class=&quot;col-xs-12 col-sm-12 col-md-4&quot;&gt; &lt;h1&gt;&lt;a t:type=&quot;pagelink&quot; page=&quot;index&quot; class=&quot;blogstack&quot;&gt;&lt;span class=&quot;glyphicon glyphicon-th&quot;&gt;&lt;/span&gt; Blog &lt;span class=&quot;stack&quot;&gt;Stack&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt; &lt;/div&gt; &lt;div id=&quot;horizontalSkycraper&quot; class=&quot;col-xs-12 col-sm-12 col-md-8&quot;&gt;&lt;/div&gt; &lt;/div&gt; &lt;div class=&quot;row&quot;&gt; &lt;div class=&quot;col-xs-12 col-sm-12 col-md-12&quot;&gt; &lt;h4&gt;Un poco más que un agregador/planeta de bitácoras sobre programación, desarrollo, software libre, gnu/linux, tecnología, ...&lt;/h4&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/header&gt; &lt;div class=&quot;container-fluid&quot;&gt; &lt;div class=&quot;row&quot;&gt; &lt;div class=&quot;col-xs-12 col-sm-12 col-md-12&quot;&gt; &lt;nav role=&quot;navigation&quot;&gt; &lt;ul class=&quot;nav nav-pills menu&quot;&gt; &lt;li&gt;&lt;a t:type=&quot;pagelink&quot; page=&quot;index&quot;&gt;Inicio&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a t:type=&quot;pagelink&quot; page=&quot;archive&quot; context=&quot;[]&quot;&gt;Archivo&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a t:type=&quot;pagelink&quot; page=&quot;faq&quot;&gt;Preguntas frecuentes&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt; &lt;/nav&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;div class=&quot;container-fluid&quot;&gt; &lt;div class=&quot;row&quot;&gt; &lt;div t:type=&quot;any&quot; class=&quot;prop:contentClass&quot;&gt;&lt;t:body /&gt;&lt;/div&gt; &lt;t:if test=&quot;aside&quot;&gt; &lt;aside class=&quot;col-xs-12 col-sm-12 col-md-4&quot;&gt; &lt;t:socialnetworks/&gt; &lt;t:if test=&quot;aside1&quot;&gt; &lt;t:delegate to=&quot;aside1&quot;/&gt; &lt;/t:if&gt; &lt;div id=&quot;bigRectangle&quot;&gt;&lt;/div&gt; &lt;t:if test=&quot;aside2&quot;&gt; &lt;t:delegate to=&quot;aside2&quot;/&gt; &lt;/t:if&gt; &lt;div class=&quot;row&quot;&gt; &lt;div class=&quot;col-xs-3 col-md-2&quot;&gt; &lt;div id=&quot;wideSkycraper&quot;&gt;&lt;/div&gt; &lt;/div&gt; &lt;t:if test=&quot;aside3&quot;&gt; &lt;div class=&quot;col-xs-3 col-md-2&quot;&gt; &lt;t:delegate to=&quot;aside3&quot;/&gt; &lt;/div&gt; &lt;/t:if&gt; &lt;/div&gt; &lt;t:if test=&quot;aside4&quot;&gt; &lt;t:delegate to=&quot;aside4&quot;/&gt; &lt;/t:if&gt; &lt;/aside&gt; &lt;/t:if&gt; &lt;/div&gt; &lt;/div&gt; &lt;footer&gt; &lt;div class=&quot;container-fluid&quot;&gt; &lt;div class=&quot;row&quot;&gt; &lt;div class=&quot;col-xs-12 col-sm-12 col-md-12&quot;&gt; &lt;div class=&quot;footer&quot;&gt; &lt;a t:type=&quot;pagelink&quot; page=&quot;index&quot;&gt;Blog Stack&lt;/a&gt; por &lt;a href=&quot;https://twitter.com/picodotdev/&quot;&gt;pico.dev&lt;/a&gt; está publicado bajo la licencia de software libre &lt;a href=&quot;http://www.gnu.org/licenses/agpl-3.0.html&quot;&gt;GNU Affero General Public&lt;/a&gt;.&lt;br/&gt; El contenido agregado conserva la licencia de su bitácora.&lt;br/&gt; «Powered by» &lt;a href=&quot;https://github.com/picodotdev/blogstack&quot;&gt;Blog Stack&lt;/a&gt;, &lt;a href=&quot;http://tapestry.apache.org/&quot;&gt;Apache Tapestry&lt;/a&gt;, &lt;a href=&quot;https://www.openshift.com/&quot;&gt;OpenShift&lt;/a&gt;, &lt;a href=&quot;https://pages.github.com/&quot;&gt;GitHub Pages&lt;/a&gt;, &lt;a href=&quot;http://www.oracle.com/es/technologies/java/overview/index.html&quot;&gt;Java&lt;/a&gt; y más software libre o de código abierto, inspirado en &lt;a href=&quot;http://octopress.org/&quot;&gt;Octopress&lt;/a&gt;.&lt;br/&gt; &lt;span class=&quot;copyleft&quot;&gt;&amp;copy;&lt;/span&gt; pico.dev ${year} &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/footer&gt; &lt;div id=&quot;fb-root&quot;&gt;&lt;/div&gt; &lt;t:ads adsense=&quot;adsense&quot;/&gt; &lt;/body&gt; &lt;/html&gt;</code></pre></noscript>

Para terminar nos queda ver como sería usar este componente en una página donde queremos usarlo. En la etiqueta html se usa la plantilla con t:type para indicar que esa etiqueta es un componente de Tapestry y se le pasan los aside1 y aside2 que en esta página tienen contenido propio. El contenido de la etiqueta html se sustituirá por la etiqueta <t:body/> de la plantilla, el contenido inlcluido en los componentes <t:block/> aunque esté dentro de la etiqueta html solo se mostrará cuando se haga uso de un <t:delegate/>, como se hace el componente plantilla. Este es el caso de la página índice de Blog Stack. A pesar de todo el contenido que genera y solo consta de 34 líneas de código, esto muestra lo fácil que es en Tapestry dividir las diferentes partes de una página en componentes que puede ser reutilizados.

<noscript><pre><code>&lt;html t:type=&quot;layout&quot; t:aside1=&quot;aside1&quot; t:aside2=&quot;aside2&quot; xmlns:t=&quot;http://tapestry.apache.org/schema/tapestry_5_4.xsd&quot; xmlns:p=&quot;tapestry:parameter&quot;&gt; &lt;t:data/&gt; &lt;t:loop source=&quot;posts&quot; value=&quot;post&quot;&gt; &lt;t:postcomponent post=&quot;post&quot; excerpt=&quot;true&quot;/&gt; &lt;/t:loop&gt; &lt;section class=&quot;index-pagination&quot;&gt; &lt;div class=&quot;container-fluid&quot;&gt; &lt;div class=&quot;row&quot;&gt; &lt;div class=&quot;col-xs-4 col-sm-4 col-md-4&quot;&gt; &lt;t:if test=&quot;!lastPage&quot;&gt; &lt;a t:type=&quot;pagelink&quot; page=&quot;index&quot; context=&quot;nextContext&quot;&gt;&lt;span class=&quot;glyphicon glyphicon-arrow-left&quot;&gt;&lt;/span&gt; Más antiguo&lt;/a&gt; &lt;/t:if&gt; &lt;/div&gt; &lt;div class=&quot;col-xs-4 col-sm-4 col-md-4 col-xs-offset-4 col-sm-offset-4 col-md-offset-4 text-right&quot;&gt; &lt;t:if test=&quot;!firstPage&quot;&gt; &lt;a t:type=&quot;pagelink&quot; page=&quot;index&quot; context=&quot;previusContext&quot;&gt;Más nuevo &lt;span class=&quot;glyphicon glyphicon-arrow-right&quot;&gt;&lt;/span&gt;&lt;/a&gt; &lt;/t:if&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/section&gt; &lt;t:block id=&quot;aside1&quot;&gt; &lt;t:feeds/&gt; &lt;/t:block&gt; &lt;t:block id=&quot;aside2&quot;&gt; &lt;t:lastposts/&gt; &lt;t:lastsourceswithposts/&gt; &lt;/t:block&gt; &lt;/html&gt;</code></pre></noscript>

Usando el mismo componente podemos darle un aspecto común pero variando el contenido de las diferentes secciones. En este caso usamos la misma plantilla donde se muestra la misma cabecera, enlaces de navegación y pie de página pero sin el contenido lateral como en el caso de la página de preguntas frecuentes de Blog Stack, en este caso no usamos los componentes aside.

<noscript><pre><code>&lt;html t:type=&quot;layout&quot; t:title=&quot;Preguntas frecuentes&quot; xmlns:t=&quot;http://tapestry.apache.org/schema/tapestry_5_4.xsd&quot; xmlns:p=&quot;tapestry:parameter&quot;&gt; &lt;article class=&quot;text-justify&quot;&gt; &lt;header&gt; &lt;h1&gt;Preguntas frecuentes&lt;/h1&gt; &lt;/header&gt; &lt;h2&gt;¿Qué es Blog Stack?&lt;/h2&gt; &lt;p&gt;Blog Stack (BS) es una agregador, planeta, o fuente de información de bitácoras sobre programación, desarrollo, desarrollo ágil, software, software libre, hardware, gnu/linux o en general temas relacionados con la tecnología.&lt;/p&gt; &lt;h2&gt;¿Por qué otro agregador?&lt;/h2&gt; &lt;p&gt; Hay varios motivos, la semilla es que quería hacer un proyecto personal con cierta utilidad para otras personas empleando de alguna forma el framework para el desarrollo de aplicaciones web &lt;a href=&quot;http://tapestry.apache.org/&quot;&gt;Apache Tapestry&lt;/a&gt;. &lt;/p&gt; ... &lt;/article&gt; &lt;/html&gt; </code></pre></noscript>

Por supuesto, podemos crear tantos componentes plantilla como necesitemos en una aplicación y usar uno o otro en función del tipo de página.

Referencia:
Código fuente de Blog Stack
Libro PlugIn Tapestry
Documentación sobre Apache Tapestry

» Leer más, comentarios, etc...

Cuaderno de software

Adaptación al medio: evolución y mutación

July 11, 2014 06:39 AM

A menudo me gusta pensar en mi equipo como un organismo vivo multicelular que evoluciona y se adapta al entorno cambiante que le rodea. Tiene que ver con que soy un flipao y un pedante… :-P Sin embargo, esa adaptación creo que es el resultado de dos dinámicas que a veces pueden parecen contradictorias:

  • evolución: intentamos hacer retrospectivas basadas en datos (horas gastadas, tecnologías empleadas, bugs, etc.), de ahí intentamos entender en profundidad cuáles son las raíces reales de nuestros problemas, los priorizamos e intentamos tomar acciones para eliminar ese problema. Lo máxima aquí es: “si tomas acciones alrededor de un problema no prioritario o intentas atacar al problema sin saber cuál es su raíz, estás perdiendo tiempo y dinero”.
  • mutación: probamos cosas nuevas por instinto o por jugar. Aquí la máxima es: “conduct frequent experiments” pero asegurándote de que no penalizas económicamente al producto/cliente/empresa con el que estás trabajando. Yo suelo decirlo mucho: en un equipo no puede salir en una retrospectiva “hagamos integración continua” si ninguno de sus miembros a oído hablar jamás de ello. Rachel Davies en el  Agile Coaching afirma: “Actions are not always about fixing a problem: you need to understand a problem before you can fix it. Team may need to start with actions to explore the problem and gather data.” Sin embargo, aquí también se está teniendo un carácter totalmente reactivo: tenemos un marrón, investiguemos ahora qué hay para solucionar este marrón. Creo que eso es necesario, pero no suficiente.

Creo que todo lo que escribo es de sentido común: céntrate en solucionar los problemas reales que tienes y, también, prueba cosas nuevas mientras no cueste mucho dinero a nadie. Sin embargo, lo que suele suceder en los equipos es que se dividen en “evolucionadores” o “mutadores” cada uno con su zona de confort:

  • los “evolucionadores” se excusan para no probar nada nuevo y no aprender, en que lo que se propone no corresponde a la raíz de ningún problema. Tiene a gala que ellos son ingenieros que ponen el dedo en la picota.
  • los “mutadores” quieren jugar, y tienden a dejarse deslumbrar ante el brillo de lo nuevo sin tener en cuenta lo que puede suponer al equipo o al producto ni justificarlo económicamente. Tiene a gala que ellos son tecnólogos capaces de hincarle el diente a cualquier cosa que salga nueva que, como es nueva, es mejor (mega-sesgo).

Creo que aquí hay que trabajar en la identidad individual de las personas y que todo el mundo se vea a si mismo como evolucionador y mutador (sí, estas dos palabras no existen) porque ambas fuerzas son necesarias para adaptar y evolucionar al equipo.

 

» Leer más, comentarios, etc...

Blog Bitix

Arquitectura y hospedaje de Blog Stack

July 10, 2014 07:00 PM

Blog Stack

Normalmente en esta bitácora suelo poner ejemplos para explicar como funciona una determinada herramienta, son ejemplos que podrían aprovecharse para algo productivo pero para mantenerlos simples por si mismos no suelen serlo. En esta entrada voy a comentar cual es la arquitectura y como está alojado Blog Stack, que es un ejemplo de algo práctico y real por si la experiencia de este ejemplo le sirve de ayuda o de inspiración a alguien. En la red he encontrado comentada la experiencia de Planeta Linux y Meneame, otros proyectos web.

Blog Stack (BS) es una agregador, planeta, o fuente de información de bitácoras sobre programación, desarrollo, desarrollo ágil, software, software libre, hardware, gnu/linux o en general temas relacionados con la tecnología, en definitiva es un servicio que está disponible en una dirección de internet, www.blogstack.info.

Una condición importante que buscaba para BS es que de cualquier opción no debería ser muy cara al menos en los inicios hasta que viese cuales eran los ingresos por la publicidad que era capaz de generar, estimaba que no iban a ser muchos hasta que se agregase unas decenas de bitácoras y tuviese algunos lectores suscritos a las fuentes se sindicación. Por coste cualquier opción que fuese en la nube prácticamente quedaba desechada, la opción más barata era la de Amazon EC2, reservando una instancia t1.micro (0,615 GB) por tres años el coste era de unos 80 € anuales y de 140 € anuales para una instancia de tipo m1.small (1,7 GB), otras opciones como OpenShift con la capa gratuita de 3 gears si un small gear (512 MB) era suficiente no tenía coste pero si necesitase un gear medium (1 GB) el coste era superior, de unos 438 € anuales. Finalmente otra opción que evalué era Google Compute con un coste de 115 € anuales para una instancia f1-micro (0,60 GB) y de 305 € para una g1-small (1,7 GB). Si en cualquier opción de estas en la nube el coste debía de asumirlo yo probablemente al cabo de un tiempo, pocos años, acabaría cerrando el servicio, por ello el coste era importante. OpenShift y su capa gratuita tenía muchas posibilidades.

Por otro lado estaba bastante contento con la generación estática de mi bitácora con Octopress y el alojamiento en GitHub Pages. Me pereció que una opción con un coste mínimo podría generar de forma estática el contenido y alojarlo en GitHub Pages al igual que hago con Octopress. Finalmente, me quedaba donde hacer la generación estática, otra opción era usar la Raspberry Pi pero esto me obligaba a mantenerla encendida siempre, cualquier problema en la Raspberry Pi o conexión en mi red o si la necesitaba apagar en algún momento podría dejar BS sin actualizarse. Si era posible necesitaba una forma de nube, por suerte OpenShift ofrece un plan gratuito de 3 gears con 512 MiB de memoria y 1 GiB de espacio en disco cada uno y en las primeras pruebas que hice era suficiente. Además, OpenShift ofrecía todas las herramientas que necesitaba git para subir el contenido estático al repositorio de GitHub, iconv, cron, bases de datos relacionales y no sql, poder usar Undertow como servidor y un «cartridge» diy («Do It Yourself»). Tenía varias piezas, finalmente elegí la combinación de OpenShift para la generación estática y GitHub Pages para servir el contenido. El contenido podría servirlo desde OpenShift pero no estaba seguro de que el gear fuese capaz de aguantarlo todo si el tráfico fuese grande. Por el espacio en disco de 1 GB tenía dudas de cuanto podría necesitar pero si fuera mucho podría adquirir 1 GiB adicionar por 1 € al mes.

Generar el contenido de forma estática tiene limitaciones y obliga a hacer las cosas de diferente forma que empleando una aplicación web pero el requisto del coste era más importante que la funcionalidad y la forma de ofrecerla. El coste de BS es de unos ¡10 €… al año! y consiste básicamente en la compra del dominio. El dominió lo compre en DonDominio por que por recomendación era buena opción, en los 10 € está incluido el WHOIS privado que en otras opciones tienen un coste adicional. Teniendo el nombre elegido de Blog Stack para el proyecto tenía que saber que dominios de nivel superior (TLD) estaban disponibles, los “okupas” de los dominios tenían reservado el .com y el .net, como BS era un sitio de información no me importó mucho comprar el .info.

Con todo la arquitectura de despliegue y hospedaje de Blog Stack es la siguiente:

En cuanto a la aplicación y el código Java en si las partes importantes están en un servicio que se encarga de la indexación extrayendo el contenido de las fuentes y guardalo en una base de datos, otro servicio para la generación del contenido del sitio de forma estática (html, css, js, imgs, …) y finalmente el programa Java principal que sirve para lanzarlo desde la linea de comandos, el resto son las clases del modelo de datos y los DAO de persistencia y acceso a dataos. La aplicación sigue el esquema tradicional de 3 capas, la de presentación (páginas y componentes de tapestry), la de lógica de negocio formada por los servicios de indexación, generación y los DAOs (formada por el contenedor de servicios de tapestry y spring) y finalmente la capa de datos formada por una base de datos H2 (donde hibernate interviene). En realidad en BS no hay un servidor de aplicaciones sino que se usa Tapestry en una aplicación «standalone».

La raíz del proyecto fue que quería usar Apache Tapestry de alguna forma en algún proyecto que fuese de utilidad para otras personas, al mismo tiempo que meditaba la idea Planeta Linux dejó de funcionar durante unas semanas y tampoco estaba contento del todo con los otros planetas en los que tenía mi bitácora agregada. Con Planeta Arch Linux porque después de unos meses seguía sin ni siquiera respuesta a la solicitud de agregación y Planeta Código ya que creía que podría ofrecer mejor funcionalidad, por ejemplo, los gist no se importaban bien (utilizan javascript y por seguridad deben estar excluidos). Ya que empezaba el proyecto creía que podría proporcionar alguna idea que permitiese descubrir contenido a los usuarios a través de como hace stackoverflow con las etiquetas de modo que una persona se pueda suscribir (mediante rss/atom) a una etiqueta para recibir el contenido de solo el tema que le interese de cualquier bitácora agregada, descubrir nuevas bitácoras con contenido interesante a veces no es fácil. La opción más parecida y mejor que he encontrado es bitacoras.com, salvo las votaciones, el ranking y que tiene un ámbito más amplio al poder incluir bitácoras de más temáticas y que la agregación de bitácoras no está automatizada, BS en lo importante ofrece lo mismo de forma más simple y está más especializado tanto en la temática de las bitácoras como en la suscripción por etiquetas.

Apache Tapestry junto con el módulo Tapestry Offline y el hecho de que el contenedor de dependencias de Tapestry se puede usar en una aplicación «standalone» me permitía resolver la parte técnica en la mayor parte, el resto era usar unas cuantas librerías para tener la funcionalidad que necesitaba. Una de ellas era jsoup con la que podía filtrar el contenido agregado y evitar problemas de seguridad. Permitir en el contenido scripts e iframes puede ser un problema de seguridad pero permitiendo algunas fuentes confiables podía importar el contenido de forma segurar sin perder funcionalidades. En algunas bitácoras como la mía se suelen utilizar como trozos de código gist, vídeos de youtube o vimeo, presentaciones de Speaker Deck o usar el programa de afliados de amazon. Iba a necesitar una base de datos ya que las entradas van desapareciendo de las fuentes agregadas a medida que se publica contenido. La base de datos que elegí fue H2 principalmente porque puede estar contenida en un archivo que puedo descargar a mi ordenador, de esta manera podría evitar los volcados de PosgreSQL. Si BS resultase que creciese bastante posteriormente podría cambiar H2 por PosgreSQL (la otra opción que consideraba), H2 al principio era más que suficiente. El resto era la colección de herramientas que se suelen utilizar el proyectos Java como Hibernate, Spring, Gradle, Apache Commons, Rome (para obtener las fuentes)…

El despliegue de nuevo código está totalmente automatizado tal y como aconseja el libro The Pragmatic Programmer, con lo que me es muy simple y me consume muy poco tiempo hacer un nuevo despliegue, el tiempo ahorrado lo puedo emplear para desarrollar. El despliegue consiste en unos cuantos comandos ejecutados por una tarea de gradle que se envían a OpenShift. Los comandos de despliegue se encargan de todo mediante ssh, hacer previamente una copia de seguridad de la base de datos (scp/unzip), sincronizar los archivos cambiados del proyecto con rsync, establecer los permisos de ejecución de los archivos bash y actualizar el esquema de la base de datos con liquibase.

No estaba usando Apache Tapestry de la forma habitual que se suele emplear que es para desarrollar aplicaciones web que se despliegan en un servidor de aplicaciones Java. Lo iba a usar como motor de plantillas para generar el contenido estático, aparte de querer usar Tapestry ¿por que esta opción si hay soluciones específicas para esto como Thymeleaf, Freemarker, …? Uno de los motivos es que en en la mayoría de opciones se sigue un modelo «push» en el que se combinan los datos con la vista para producir el resultado. Este modelo «push» cada vez me convence menos a pesar de ser ampliamente usado en muchos motores de plantillas y frameworks web, la razón es que las vistas al final acaban conteniendo lógica si son algo complejas, eso no es bueno. Tapestry por el contrario usa un modelo «pull» en el que la plantilla para generar el contenido puede acceder al controlador por lo que las plantillas no tienen lógica, estando extraída a una clase java con lo que nos beneficiaremos del compilador y el IDE para esa lógica. Además, los componentes de Tapestry son una manera fácil de reutilizar código, por si fuera poco si pasado un tiempo quisiese desplegar Blog Stack como aplicación web podría aprovechar prácticamente todo el código.

Estando convencido del uso del software libre para ser coherente no tenía otra opción que publicar el código fuente bajo alguna licencia de software libre, la opción que elegí fue AGPL. Uno puede tener dudas de “dar” el código fuente pero creía que ganaba si alguien en un caso remoto hiciese un fork de BS, ya que Tapestry siendo una parte tan importante sería lo usado y por tanto habría hecho que otra persona usase Tapestry. Si basándose en la misma idea se hacía un proyecto similar competiríamos y que triunfase la mejor opción. Pero lo más probable que ocurra es que se colabore con el desarrollo de BS, con esta opción también ganaría. De tres opciones posibles en dos ganaría, en la tercera se competiría y las opciones que elegí eran de lo mejor que hay en el mundo Java, BS está bien armado.

Con el desarrollo de Blog Stack he tenido que resolver unos cuantos problemas, en posteriores entradas explicaré como he implementado las soluciones a algunas funcionalidades que son comunes a los proyectos web, como la correspondencia entre las urls y los artículos o etiquetas para no poner identificadores de entidades de la base de datos en las urls y que queden más limpias y amigables, como hacer la transliteración de los títulos de las entradas para la construcción de las urls, como obtener un extracto de la entrada (puede parecer simple pero no lo es tanto), como usar un servidor embebido que sirva el contenido estático o como procesar recursos estáticos como archivos less para generar el css con wro4j.

Referencia:
Presentación de Blog Stack

» Leer más, comentarios, etc...

Bitácora de Javier Gutiérrez Chamorro (Guti)

Sieve en C3

July 09, 2014 07:48 AM

Con Sieve en Javascript optimizado, había dado por cerrado el ciclo de rendimientos basados en Sieve en Java, BASIC, Pascal, y C. Sin embargo después del buen sabor de boca con C3 (Clipper Compatible Compiler), me quedaba por comprobar como de bueno sería su rendimiento. La clave de Clipper, era que generaba ejecutables nativos, pero [...]

» Leer más, comentarios, etc...

Meta-Info

¿Que es?

Planeta Código es un agregador de weblogs sobre programación y desarrollo en castellano. Si eres lector te permite seguirlos de modo cómodo en esta misma página o mediante el fichero de subscripción.

rss subscripción

Sponsors

Puedes utilizar las siguientes imagenes para enlazar PlanetaCodigo:
planetacodigo

planetacodigo

Si tienes un weblog de programación y quieres ser añadido aquí, envíame un email solicitándolo.

Idea: Juanjo Navarro

Diseño: Albin