Noticias Weblogs Código

Blog Bitix

Productividad y errores de compilación con Apache Tapestry

March 28, 2015 09:30 PM

Apache Tapestry

Hace ya unos años comentaba varios motivos para elegir Apache Tapestry como framework para desarrollar una aplicación o página web. Entre las varias razones comentaba la productividad como característica destacada. Uno de los motivos es esta productividad era por la alta reutilización de código que se puede conseguir al usar los componentes múltiples veces en un mismo proyecto o en diferentes proyectos creando una librería de componentes como comento más detalladamente en el libro PugIn Tapestry. Otra parte de la productividad que comentaba más ligeramente era poder detectar de forma rápida errores de compilación no solo en el código Java a través del IDE sino porque con Tapestry es posible detectar errores de compilación en todas las plantillas tml que generan el html fácil y rápidamente con un botón sin tener que probar manualmente toda la funcionalidad. El tema de este artículo es mostrar más en detalle como detectar los errores de compilación en las vistas con este framework.

Java

Por «errores de compilación» me refiero a ese tipo de erorres que hace el código ni siquiera pueda ser interpretado correctamente por el computador, puede ser porque falta un import, un nombre de variable, propiedad o método mal puesto y que no existe… Poder detectar errores de compilación fácilmente en toda la aplicación es tremendamente útil y evitará que lleguen a producción con las consiguientes molestias para los usuarios y que posteriormente tengamos que dedicar tiempo a corregirlos cuando hemos perdido el contexto de las modificaciones hechas. También tendremos más seguridad de que no introducicmos errores al hacer refactorizaciones importantes en el código. Los errores de compilación suelen ser fáciles y rápidos de corregir pero igualmente pueden impedir totalmente el uso de la aplicación. Cuando antes detectemos los errores más fácilmente los corregiremos y más productivos seremos ya que evitaremos corregirlos en un momento posterior en que costará más tiempo y esfuerzo, además de tener que realizar un nuevo despliegue con los cambios corregidos que dependiendo del tiempo que nos lleve puede suponer otro problema.

La errores de compilación no depende de escribir pocas lineas de código o ahorarnos pulsar unas cuantas teclas, mi experiencia con los lenguajes dinámicos como Groovy y el framework Grails es que se producen ocasionales pero constantes errores de compilación en el código Groovy y en las plantillas de vistas gsp. En parte estos errores se pueden detectar teniendo teses pero la realidad es que en pocos proyectos hay una cobertura del 100% del código sobre todo para esas partes en las que «seguro no se dan errores» o poco relevantes que no merece la pena hacerlos, tener una cobertura completa del código exige tener muchos teses y datos de prueba que los ejerciten para pasar por todas las combinaciones de condiciones y bucles, para detectar errores en las vistas también deberíamos hacer teses y esto ya no suele ser tan habitual hacerlo. Y de forma similar esto se puede extender a algunas otras combinaciones de lenguajes y frameworks web por sus características similares. Si en el proyecto solo participa una persona o el proyecto es pequeño como suele ocurrir en las pruebas de concepto con las que solemos divertirnos el número de errores no debería ser muy elevado ya que el código estará bajo control por esa persona pero cuando en un proyecto real en el que participan unos cuantos programadores haciendo continuamente modificaciones el número de errores de compilación pueden producirse y se producirán en producción. También hay que tener mucho cuidado en merges con conflictos, reemplazos grandes o proyectos grandes con muchos archivos ya que en uno complicado es fácil dejar un código que produce errores de compilación, en un lenguaje dinámico más por no tener la ayuda del compilador que nos avise de los mismos, tambien hay que resistir la tentación de hacer cambios sin probarlos confiando en que no introduciremos errores.

Con Java y un IDE podremos detectar los errores de compilación que en un lenguaje dinámico solo observaremos en tiempo de ejecución. En Tapestry además podemos detectar los errores de compilación en las plantillas tml que generan el contenido html con un botón en la página Dashboard que ofrece incorporada Tapestry. Usando como ejemplo la aplicación que hice para el libro PlugIn Tapestry vamos a ver como detectar estos errores. De forma intencionada introduciré un error en la página que muestra el detalle de un producto en el mantenimiento CRUD del ejemplo. En vez de producto.nombre introduciré el error de compilación poniendo producto.nombra, nombra es una propiedad que no existe en la clase Producto, error que solo detectaremos después de crear un producto en otros frameworks al ejercitar el código pero que en Tapestry detectaremos también desde la página Dashboard. Por otra parte dado que en Tapestry las plantillas tml son xml válido si una etiqueta está mal balanceada también nos avisará.

<noscript><pre><code>... &lt;t:form t:id=&quot;form&quot; context=&quot;producto.id&quot; validate=&quot;producto&quot; clientValidation=&quot;none&quot; class=&quot;well&quot; role=&quot;form&quot;&gt; &lt;t:errors class=&quot;literal:alert alert-danger&quot; /&gt; &lt;t:delegate to=&quot;botonesEdicionBlock&quot;/&gt; &lt;div style=&quot;margin-top: 10px;&quot;&gt; &lt;div class=&quot;form-group&quot;&gt; &lt;t:label for=&quot;nombre&quot; /&gt; &lt;div class=&quot;controls&quot;&gt; &lt;input t:type=&quot;textfield&quot; t:id=&quot;nombre&quot; value=&quot;producto.nombra&quot; size=&quot;100&quot; label=&quot;Nombre&quot; /&gt; &lt;/div&gt; &lt;/div&gt; ...</code></pre></noscript>

Entrando a la página Dashboard y pulsando el botón Load all pages detectaremos el error sin necesidad de crear un producto. El error es el siguiente que nos indicará claramente en que página o componente se ha producido el error y una descripción bastante clara de la causa del problema.

En la imagen con el mensaje del error se puede ver de forma muy detallada cual es la causa, nos indica que el error está en la página admin/Producto y que la clase es.com.blogspot.elblogdepicodev.plugintapestry.Producto no tiene una propiedad llamada nombra, con este mensaje rápidamente nos damos cuenta del error de escritura que hemos cometido, corregirlo basta con sustituir nombra por nombre y pulsando de nuevo el botón Load all pages comprobamos que no hay más errores en esa misma página o ninguna otra de la aplicación.

Los errores en producción son un problema para los usuarios de la aplicación que no podrán trabajar normalmente y para la productividad de los desarrolladores ya que habremos perdido el contexto de los cambios causantes del fallo y nos costará más corregirlos. En caso de que se nos escape algún error la página de Excepcion nos dará información detallada y un mensaje que suele ser bastante descriptivo por si solo para descubrir donde está el bug. Otro aspecto que ayuda a la productividad y que ya incorporan varios frameworks es la recarga de clases, en Tapestry es posible para los artefactos del framework (páginas, componentes y servicios, recursos i18n, imágenes, estilos css), sí, incluido código Java, con lo que tenemos las ventajas de los lenguajes de scripting y la ayuda del compilador para detectar errores inmediatamente, lo mejor de ambas opciones sin sus debilidades.

Por supuesto, no evitaremos tener otro tipo de errores en la aplicación pero al menos los de compilación si podremos detectarlos, un error habitual que se puede seguir produciendo son los NullPointerException (NPE) pero que con las novedades introducidas en Java 8 y usando la clase Optional también deberíamos poder evitarlos. Para mi esto es una gran ayuda tanto para la productividad como para aún mejor evitar que lleguen errores a producción.

Si quieres conocer más características interesantes de Apache Tapestry puedes descargarte el libro gratuito PlugIn Tapetry. El código fuente completo del ejemplo está uno de mis repositorios de GitHub.

Referencia:
Libro PlugIn Tapestry
Página Dashboard de Apache Tapestry
Artículos sobre Apache Tapestry[alfresco]: http://www.alfresco.com/

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

Juanjo Navarro

Sangría francesa en CSS

March 26, 2015 09:44 PM

Nunca se me había ocurrido como crear una sangría francesa en HTML+CSS, quizá porque no es un efecto muy usual en páginas web. Es muy sencillo. Pero antes una pequeña definición:

Una sangría francesa es un estilo de sangría de párrafo en la que la todas las líneas están indentadas excepto la primera. Es sobre todo útil para listados de elementos (donde no utilicemos topos) que se muestren en columnas estrechas (y hoy en día, con los móviles, cualquier texto puede mostrarse así) ya que si no es difícil diferenciar entre cuando es un elemento nuevo o cuando es la continuación del anterior. En inglés se le llama hanging indent.

Bueno, para lograrlo es facilísimo. Simplemente hay que utilizar el conocido text-indent con valor negativo:

p {
    margin-left: 2em;
    text-indent: -2em;
}

Aquí lo puedes ver en un codepen, donde se puede apreciar el efecto. Como se puede ver, en la lista con sangría francesa (a la izquierda) se diferencian perfectamente los distintos elementos, mientras que en la lista de la derecha se confunden unos con otros.

See the Pen PwVdmq by Juanjo Navarro (@juanjonavarro) on CodePen.


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

Juanjo Navarro

FakeSMTP: Un programa para simular un servidor SMTP cuando desarrollamos

March 25, 2015 05:13 PM

Cuando estamos desarrollando alguna aplicación que necesita enviar correo nos encontramos a veces con que no tenemos ningún servidor SMTP con el que probar. Esta misma mañana me ha ocurrido en un proyecto, en el cual ni siquiera podía acceder a un servidor SMTP público porque el desarrollo lo hacía en una VPN desde la que no podía acceder a internet.

FakeSMTP es una pequeña utilidad que en esos casos nos puede venir muy bien.

FakeSMTP

Se trata de una aplicación desarrollada en Java. Simplemente tenemos que hacer doble click sobre el jar para lanzarlo o, desde la línea de comandos:

java -jar fakeSMTP-1.13.jar

Una vez hecho esto, seleccionamos el puerto en el que queremos escuchar (el puerto por defecto de SMTP es el 25) y le damos al botón Start server.

A partir de ese momento cualquier programa podrá “enviar correo” a través de localhost. Y lo entrecomillo porque evidentemente la utilidad no envía realmente los correos, pero nos permite ver el log del diálogo SMTP, acceder al listado de correos “enviados” (de los que guarda una copia en un directorio) y ver directamente (en la pestaña Last message) el último correo enviado.

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

Poesía Binaria

Algunas hojas de cálculo recortan los CSV en las comillas [Minipost]

March 25, 2015 09:20 AM

Es algo que me ha pasado estos días al exportar un archivo CSV de una base de datos (por ejemplo con este método). Era un CSV de unas 3000 filas y algunas de ellas tenían textos entre comillas en una de sus columnas.

El problema es que al abrirlo con LibreOffice Calc (tengo entendido que con WPS Office también pasaba), aquellas columnas que tenían textos entre comillas no salían bien y terminaban cortándose y dividiéndose.

Esto es porque, mi CSV tenía las comillas escapadas (como este carácter es el delimitador, se introduce \” (barra comillas) para diferenciarlo), pero, para este tipo de programas, los CSV no tienen carácter de escapado (\). En estos programas tenemos que utilizar dobles comillas (“”) para que se interprete que es un texto entre comillas dentro de un campo.

Como el CSV era bastante grande y no era plan de ir línea por línea, podemos utilizar este comando para convertirlo rápidamente:

1
sed 's/\\"/""/g' original.csv > destino.csv

Con esto, convertimos todos los \” que encontramos en “” y ya podremos utilizarlo dentro de nuestro programa de hoja de cálculo.

The post Algunas hojas de cálculo recortan los CSV en las comillas [Minipost] appeared first on Poesía Binaria.

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

Juanjo Navarro

Emoticonos en una página web

March 24, 2015 11:20 PM

Estuve buscando soluciones para poder añadir emoticonos a una página web, concretamente para este weblog, tanto en las entradas como en los comentarios. Quería que fuera una solución de cliente, en javascript, ya que no quiero que el html generado incluya los emoticonos de ninguna manera.

Encontré dos soluciones:

La primera, llamada CSS Emoticons, es la más sencilla y tiene un soporte para emoticonos muy básico. Como ventajas tiene que es una solución pura CSS, no utiliza imágenes. De hecho, realmente lo que hace es girar realmente el texto mediante css, y meterlo en un circulo amarillo. Tiene además un efecto muy curioso: cuando se pasa el ratón por encima (por lo menos en Chrome), el emoticono se anima convirtiéndose en el texto original. Por ahora es la que he utilizado en el weblog. Aquí lo podemos ver en funcionamiento en un codepen:

See the Pen XJoGRR by Juanjo Navarro (@juanjonavarro) on CodePen.

La segunda, llamada emojify.js, es una librería más potente. Como desventaja tiene que se basa en imágenes, que se tienen que cargar de una manera u otra. Como ventaja tiene que la calidad gráfica de los emoticonos es mayor y además tiene mayor variedad, con objetos, vehículos y hasta la caca con ojos. De hecho, se puede usar cualquiera de los emoticonos estándar (sí, hasta para esto hay estándares :-) ). Aquí lo podemos ver en funcionamiento:

See the Pen EaGMvv by Juanjo Navarro (@juanjonavarro) on CodePen.

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

Variable not found

¿Y el Web.config de la carpeta /Views en MVC 6?

March 24, 2015 08:15 AM

image10
Cuando hace unas semanas hablábamos de la desaparición del Web.config en ASP.NET 5, seguro que a muchos os surgió una duda en cuanto a las consecuencias que esto podría tener en aplicaciones MVC: ¿y qué ocurre con el Web.config que encontrábamos en la carpeta /Views o /Area/<AreaName/Views?

Pues a ver si este post nos lo aclara un poco ;) Pero eso sí, siempre tened en cuenta que las nuevas versiones están aún en beta y algunas cosas pueden cambiar.



Como recordaréis, en versiones usábamos este archivo de configuración básicamente para tres cosas:
  • Primero, para evitar el acceso directo a los archivos de vista desde el exterior. Para ello, en la implementación por defecto de este archivo se añadía un handler que respondía a todas las peticiones con un “not found”.
           
  • Segundo, para indicar cuál es el tipo base de las vistas por defecto, es decir, la clase de la que heredarán los objetos que representan a las vistas de nuestra aplicación. Sólo era necesario tocarlo si queríamos modificar la clase base por algún motivo, como añadir propiedades personalizadas.
     
  • Tercero, para incluir globalmente, a todas las vistas afectadas, espacios de nombres de clases o elementos utilizados en ellas, como helpers, view-models y otros, y así ahorrarnos el introducir los @using en todas ellas.
La siguiente porción de código corresponde a uno de estos archivos Web.config, y están señalados los puntos donde se realizan las tareas anteriores:



En MVC 6, la versión que acompañará a ASP.NET 5, estas tareas siguen pudiéndose hacer de alguna u otra forma, pero dado que no existen los archivos Web.config, se consiguen de otra manera.

1. Evitar el acceso directo a la carpeta Views

La carpeta wwwrootBueno, esto no es que se haga de otra forma… directamente es que no es necesario. En el servidor web sólo estarán accesibles los archivos que cuelguen de la nueva carpeta wwwroot y ahí no encontraremos las vistas, por lo que problema solucionado.

Y en todo caso, aunque no fuera así, ASP.NET 5 no servirá archivos salvo que le indiquemos lo contrario, es decir, que incluyamos en el pipeline el middleware encargado de hacerlo. Y en él podemos configurar explícitamente las carpetas que contienen los archivos estáticos, por lo que nunca se podría llegar a una carpeta que no nos interesara.

2. Cambiar el tipo base de una vista

Si queremos cambiar la clase base de una única vista, obviamente no necesitamos un archivo independiente para ello. Bastará con usar en ella la directiva @inherits, que ya estaba presente en versiones anteriores de Razor:

@inherits MyBaseView

Sin embargo, aún no he conseguido dar con la forma de modificar la clase base por defecto en todas las vistas de la aplicación. Existe alguna referencia a cómo se hacía con versiones alfa/beta anteriores, pero en la actualidad no funciona bien, y todos mis intentos adicionales de momento han sido infructuosos.

Seguiremos atentos a ver cómo se desarrollan los acontecimientos, aunque ciertamente el hecho de que pueda añadir propiedades a una vista usando la directiva @inject hace que la herencia no sea necesaria en muchos casos, por lo que tampoco es algo que me quite el sueño en este momento.

3. Importar espacios de nombres en todas las vistas

La importación de namespaces en todas las vistas de nuestra aplicación es bastante cómoda porque evita que tengamos que introducir @using repetidos en todas ellas cuando usan componentes definidos en los mismos espacios de nombres.

Esto lo haremos, como siempre, utilizando la directiva @using de Razor, pero en lugar de hacerlo en cada una de las vistas, lo haremos de forma general en el nuevo archivo _GlobalImports.cshtml, del que ya hemos hablado en otra ocasión.

Probablemente os preguntaréis que por qué no usar para ello el clásico _Viewstart.cshtml, y la respuesta es sencilla: sólo se ejecuta con vistas completas, y no parciales o layouts, por lo que el ámbito de aplicación sería menor. Por esta razón, en MVC 6 _ViewStart.cshtml se ha dejado exclusivamente para introducir código ejecutable de inicialización de vistas (como puede ser el establecimiento de la propiedad Layout), y las directivas que queramos aplicar globalmente nos las llevaremos a _GlobalImports.cshtml.

El archivo _GlobalImports.cshtml es muy similar a _ViewStart.cshtml: se encuentra en su misma ubicación, se procesará al ejecutar las vistas, y se aplicará a todas las que se encuentren en su jerarquía de directorios. Sin embargo, a diferencia del segundo, no sólo se ejecutará en las vistas “principales”, sino también en las parciales y layouts. Por tanto, todas las vistas que se encuentren por debajo de la carpeta del archivo de importaciones globales heredarán las directivas especificadas en éste.

Bueno, y con esto hemos acabado por hoy. Como habréis visto, tras la desaparición del Web.config de las carpetas de vistas en ASP.NET 5, más o menos podremos conseguir el mismo resultado que antes, simplemente se trata de acostumbrarnos al nuevo escenario ;)

Publicado en Variable not found.

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

Arragonán

Semana 354

March 24, 2015 01:32 AM

La semana pasada fue una de esas que empiezan bien, pero se va haciendo más y más cuesta arriba tal y como van pasando los días (y no, os prometo que St. Patrick’s no tuvo nada que ver). Para colmo, la lista de tareas pendientes fue creciendo a un ritmo mucho mayor de lo que era capaz de sacarlas.

Dediqué bastante de mi tiempo a cuestiones relacionadas con el Startup Open Space; principalmente con la puesta a la venta de las entradas, temas de patrocinios y trabajar en la comunicación del sarao.

Por otro lado, en SenpaiDevs seguimos con el arranque del proyecto que queremos desarrollar, y además tuvimos visita de Alberto Gualis que nos contó buena parte del flujo de trabajo que llevan en frogtek.

También estuve empezando a pensar en una posible propuesta para un proyecto que, por temática y tecnología, me resulta bastante atractivo. Aún hay que aclarar como enfocarlo, ya que parece difícil fijar un alcance y tocará ver cómo podemos plantear al cliente algún tipo de contrato ágil con el que nos sintamos todos cómodos.

Y de cosas que llevamos entre manos:

  • Lanzamos la primera versión web de Mosica, donde queremos publicar todos los conciertos de Zaragoza. Es un proyecto al que le iba dedicando ratos de vez en cuando y que yo venía usando de modo personal. Al fin nos juntamos con Guillermo para poder tener una primera versión digna que publicar, pero aún nos quedan un puñado de cosas en el tintero que lanzar.
  • Tocó volver a pasar el scraper de Brazil, por un error mío en la última entrega en la que faltaban un buen puñado de datos.
  • Desatascamos One-stop. Había problemas en un sistema en el que había que integrarse vía REST, y del que finalmente nos montaremos un proceso de importación de los datos, ya que no se actualizan más que de vez en cuando. En este par de semanas habría que intentar dejarlo listo para que lo prueben en un entorno de staging.

Buena semana.

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

Juanjo Navarro

Archivando un proyecto: Locos Bajitos

March 23, 2015 07:40 PM

Locos Bajitos fue uno de los weblogs que monté hace tiempo y que también hace tiempo que estaba sin actividad. La última entrada es del 2007.

Durante varios años no me decidía a cerrarlo porque odio el linkrot pero lo cierto es que no tiene sentido seguir manteniendo el dominio año tras año cuando es algo que tengo la seguridad de que no voy a continuar. Hay que hacer sitio para lo nuevo.

Finalmente lo he archivado, pasando todos sus contenidos a un subdominio de mi dominio principal, para que no se pierdan: locosbajitos.juanjonavarro.com. Ahora todas las urls del sitio antiguo redirigen a las urls equivalentes del sitio archivado y cuando llegue el momento dejaré caducar el dominio.

Quiero agradecer su trabajo a todos los amigos que participaron en el proyecto: Enric, Mariana, Miguel Ángel, José R. y todas las personas que nos enviaron enlaces y nos dejaron sus comentarios.

Por cierto (ningún artículo sin su parte técnica :-) ) para hacer la redirección de todas las urls hay que crear el siguiente fichero .htaccess en la raíz del antiguo dominio (www.locosbajitos.com):

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteRule ^(.*)$ http://locosbajitos.juanjonavarro.com/$1 [L,R=301]
</IfModule>

Esto indica que se ha movido de forma permanente el recurso solicitado (código 301, Moved Permanently).

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

xailer.info

Xailer 4

March 23, 2015 01:24 PM

Estimados usuarios de Xailer,

Tenemos el placer de publicar una nueva versión de Xailer. En esta ocasión la número 4. Estamos especialmente orgullosos de esta nueva versión porque incorpora grandes mejoras que esperamos sean de vuestro agrado. Os enumero los más importantes:

  • Actualizado a la última versión de Harbour
  • Actualizado a la última versión de MinGW
  • Editor visual de sentencias SQL para su propiedad cSelect y el gestor de bases de datos para SQLite
  • Soporte de internacionalización a través de Plugin propio (se incluyen los fuentes)
  • Actualización a la versión a 3.5.4 de Scintilla
  • Soporte de #DEFINES en Intellisense
  • Mejoras en visualización de ‘tolltips’ de Intellisense
  • Se han añadido más de 700 funciones de Harbour al Intellisense
  • Resaltado de palabras coincidentes en el editor
  • Completamente renovado gestor de bases de datos SQLite. Incluye importador masivo de tablas DBF, exportación a MySql/MariaDB y el ya comentado ‘Query builder’
  • Actualización a MariaDB 2.0
  • Actualización a SQLite 3.8.8.3
  • Soporte de escáners
  • Nueva clase TImageEditor para edición de imágenes
  • Grandes mejoras en TLabelEx admitiendo ahora mucho más tipos de ‘tags’
  • Mejoras de velocidad y soporte de transacciones en TWebDataSource
  • Actualización a la última versión de Fast-Report 4
  • Importantes mejoras en Fast-Report: campos calculados, uso de matrices vacías y selección de campos a exportar para datasets

Podéis descargar esta nueva versión de Xailer desde la siguiente dirección:

http://www.xailer.com/download?es&file=1

Como ya se ha comentado, esta nueva versión de Xailer utiliza unos binarios de Harbour y MingGW muy actualizados que deberá igualmente descargar desde nuestra página Web en:

http://www.xailer.com/download?es&file=2

Observad como en este misma página tenéis también acceso a los binarios de Harbour para Xailer 3 y anteriores.

Aunque no es estrictamente necesario, pero debido al cambio de compiladores, os recomendamos que reconstruyáis completamente todos vuestros proyectos, incluidas las librerías propias que estéis usando.

Si queréis mantener ambas instalaciones de Xailer 3 y Xailer 4 en la misma máquina eso no supone ningún problema. Tan sólo deberéis instalar Xailer 4 en otro directorio, por ejemplo: c:\xailer4. Los nuevos binarios de Harbour se pueden instalar igualmente en un directorio dentro de Xailer 4, por ejemplo: c:\xailer4\harbour. En cualquier caso, deberías actualizar los parámetros de directorios en las opción de menú Herramientas->Opciones generales.  De esta forma podréis conseguir que ambas versiones de Xailer convivan sin problemas.

Cuando se mantienen dos versiones de Xailer, es muy importante configurar correctamente el apartado de directorios en Herramientas->Opciones generales en ambas instalaciones.

Para poder utilizar Xailer 4 es necesario tener una suscripción activa, lo cual puede comprobar desde la opción de menú Ayuda->Información de registro. En caso contrario sólo podréis utilizarla en modo DEMO cuya única limitación es que los ejecutables no son autónomos y tienen que ser lanzados desde el propio IDE.

Os recordamos que sólo podrán acceder a la actualización los usuarios que en el momento de la publicación de Xailer 4 posean una licencia de Xailer 3. Usuarios de Xailer de versiones anteriores deberán comprar el producto completo y no podrán acogerse a ninguna oferta de actualización.

Un cordial saludo
[El equipo de Xailer]

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

Variable not found

Enlaces interesantes 191

March 23, 2015 01:06 PM

Enlaces interesantesAhí van los enlaces recopilados durante los últimos quince días. Espero que os resulten interesantes y no os atragantéis, que son bastantes ;)

.Net

ASP.NET

Azure / Cloud

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...

Poesía Binaria

Usando SQLite en nuestros programas en C/C++ (I)

March 23, 2015 09:49 AM


Foto: Eirik Stavelin (Flickr CC-by)
A menudo, nuestros programas necesitan almacenar información (temporal o no) de forma ordenada, rápida y que no nos complique la vida. Luego también necesitamos poder acceder a ella con la misma facilidad. Para eso vale SQLite. Tendremos un pequeño motor de base de datos que con sólo un par de archivos (.h y .c) más un archivo de datos lo tendremos todo listo.

Una pequeña introducción

SQLite nos proporciona una forma muy sencilla de introducir y eliminar información (si estamos familiarizados con el lenguaje SQL) sin las complicaciones de tener un motor de base de datos corriendo (MySQL, MariaDB, PostreSQL, MSSQL…). Por un lado, al no realizar conexiones, todo debería ir mucho más rápido, en bases de datos relativamente pequeñas se nota. Además, no podremos hacer llamadas de forma remota (como hemos hecho siempre, conectando con el gestor de base de datos), ni tampoco podremos montar clusters ni nada de eso (directamente, seguro que hay algún proyecto por ahí que lo permita). Las instrucciones SQL soportadas no son muchas (comparado con motores grandes) pero en muchísimos casos tendremos suficiente.

¿Quién usa SQLite?

En un primer momento, podríamos no adoptar una tecnología que no esté ampliamente aceptada (es normal, en cualquier momento es desatendida y ¡todo nuestro código a la basura!), y una tecnología que no sea libre (sobre todo para poder estudiarla, para ver que no tenga una cara oculta). Éstas son dos condiciones que pongo personalmente cuando empiezo a trabajar con alguna.
SQLite se usa en muchos programas como Skype, algunos programas de Adobe, Firefox, Chrome, Safari y muchos más. Además, tenemos extensiones para SQLite en muchos lenguajes de programación como Java, Python, PHP, y muchísimos más (Wikipedia)

¿Dónde me bajo lo necesario?

Directamente en la web oficial: SQLite download. Yo siempre descargo sqlite-amalgamation, aquí tenemos todo el sistema sqlite en dos archivos sqlite.h y sqlite.c listos para trabajar.
Si utilizas GNU/Linux, lo más probable es que tu distribución tenga un paquete sqlite y otro sqlite-dev con el código fuente (necesitaremos los dos).

Para los ejemplos cuento con que tenemos sqlite3.h en el mismo directorio que el código de nuestro programa.

Compilar el código

Todos los códigos que pondré en el post se compilan de la misma manera con gcc. Aunque tenemos dos posibilidades. Por un lado podemos incluir sqlite en nuestro ejecutable. Nuestro programa pesará más, pero no será necesario tener la biblioteca instalada. Eso sí, para compilar, necesitamos el archivo sqlite3.c de la amalgama de código de sqlite. Esto lo haremos así:

$ gcc -o ejecutable fuente.c sqlite3.c -ldl -lpthread

(Si usas Windows, necesitarás otras bibliotecas diferentes de dl y phtread).

Por otro lado, si queremos aprovechar la biblioteca dinámica de sqlite3 instalada en nuestro sistema (ya que muchas aplicaciones lo utilizan, ahorramos cerca de 1Mb de código en cada ejecutable), lo podemos hacer de la siguiente manera:

$ gcc -o ejecutable fuente.c -lsqlite3

De esta forma, los ejecutables ocuparán muchísimo menos, pero necesitaremos tener SQLite instalado en nuestro sistema.

Hola mundo – Sacando la versión del motor

Como primer programa, antes de hacer nada con la base de datos, vamos a consultar la versión de SQLite que tenemos. Por ejemplo, si utilizamos la versión de SQLite instalada en el sistema es muy interesante, porque puede que nosotros estemos utilizando características propias de una versión de la biblioteca y debemos asegurarnos de que la versión que tiene el usuario es igual o posterior.

sqversion.c

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
#include <stdlib.h>
#include "sqlite3.h"

int main(int argc, char* argv[])
{
   printf ("SQLITE LIB Version: %s\n", sqlite3_libversion());
   printf ("SQLITE LIB Version (int): %d\n", sqlite3_libversion_number());

   if (sqlite3_libversion_number()<3001008)
     printf ("Lo siento, tu versión de SQLite es muy antigua\n");
}

En este caso, para visualizar la versión, nos viene bien la forma bonita, a modo de cadena de caracteres, con sus puntos y todo; pero cuando queremos realizar la comparación, nos será mucho más fácil utilizar la forma numérica.

Abriendo y cerrando la base de datos

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <stdio.h>
#include <stdlib.h>
#include "sqlite3.h"

int main(int argc, char* argv[])
{
   sqlite3 *db;
   int res;

   /* Open database */
   res = sqlite3_open("test.db", &db);
   if (res)
   {
      fprintf(stderr, "No puedo abrir la base de datos: %s\n", sqlite3_errmsg(db));
      exit(0);
   }
   else
   {
      fprintf(stderr, "Base de datos OK\n");
   }

   sqlite3_close(db);

   return 0;
}

Este pequeño programa no hace nada… bueno sólo abre y cierra la base de datos, que normalmente se hará con éxito. Si queremos probar un caso en el que falle, así rápidamente, podemos quitar los permisos de lectura:

$ chmod -r test.db

al archivo test.db para que veamos que también puede fallar el programa.

Nuestra primera consulta : Creando una tabla

Podemos usar este pequeño código para empezar creando una tabla. En este ejemplo, vamos a hacer un pequeño log de eventos de nuestro programa, en el que podemos almacenar la marca de tiempo, nivel (si es más o menos crítico), tipo (otro número), y mensaje.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <stdio.h>
#include <stdlib.h>
#include "sqlite3.h"

int main(int argc, char* argv[])
{
   sqlite3 *db;
   char *error = 0;
   int res;
   char *sql;

   /* Open database */
   res = sqlite3_open("test.db", &db);
   if (res)
     {
       fprintf(stderr, "No puedo abrir la base de datos: %s\n", sqlite3_errmsg(db));
       exit(0);
     }
   else
     {
       fprintf(stderr, "Base de datos OK\n");
     }
   /* Create SQL statement */
   sql = "CREATE TABLE events ("
     "`timestamp` DATETIME, "
     "`level` NUMBER, "
     "`type` NUMBER, "
     "`message` TEXT)";

   /* Execute SQL statement */
   res = sqlite3_exec(db, sql, NULL, 0, &error);
   if (res != SQLITE_OK)
     {
       fprintf(stderr, "Error: %s\n", error);
       sqlite3_free(error);
     }
   else
     {
       fprintf(stdout, "Tabla creada!\n");
     }

   sqlite3_close(db);

   return 0;
}

De nuevo, si lo ejecutamos, nos dirá que la tabla ha sido creada, y saldrá del programa.

Insertando información en la tabla

Para ello, vamos a cambiar el SQL por lo siguiente:

1
2
3
4
5
6
7
8
   /* STRFTIME('%s','now') - Unix timestamp
    DATETIME(STRFTIME('%s','now')) = DATETIME('now') but we can operate with
       the timestamp
*/

   sql = "INSERT INTO events VALUES (DATETIME(STRFTIME('%s','now'), 'unixepoch'), 1, 2, 'This is a test');"
     "INSERT INTO events VALUES (DATETIME(STRFTIME('%s','now')+86400, 'unixepoch'), 10, 4, 'This is a test again');"
     "INSERT INTO events VALUES (DATETIME(STRFTIME('%s','now')+86400*2, 'unixepoch'), 100, 8, 'This is a test again x2');"
     "INSERT INTO events VALUES (DATETIME(STRFTIME('%s','now')+86400*20, 'unixepoch'), 1000, 16, 'This is a test again x3');";

Hemos insertado cuatro filas de datos, en las que rellenamos la fecha y hora, ponemos valores numéricos en level y type y un mensaje final.
Me he complicado la vida un poco para poner la fecha y hora, más que nada para que se vayan sumando algunos días y las fechas sean diferentes.

Obteniendo datos de la tabla

Ahora el tema va a ser ligeramente diferente, porque vamos a extraer información en lugar de generarla y, en muchos casos, puede ser gran cantidad de información la que estamos extrayendo. En este caso, cuando llamamos a la función sqlite3_exec(), el tercer parámetro que hasta ahora es NULL, va a ser el nombre de una función de callback encargada de recibir los datos por parte de SQLite, y ya, nosotros veremos lo que hacemos con ellos. La forma de pasar los datos, será parecida a cómo recibe los argumentos (desde la función main()) un programa en C o C++, gracias a dos variables: una que devuelve el número de argumentos recibidos (argc) y otra que recibe el contenido de éstos (argv). Además, tendremos otra variable más que recibirá los nombres de los campos. Esta función será llamada a cada fila recibida.

Podemos hacer algo como esto:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <stdio.h>
#include <stdlib.h>
#include "sqlite3.h"

static int selectCb(void *nada, int argc, char **argv, char **colNames){
   int i;

   for(i=0; i<argc; i++){
      printf("%s => %s\n", colNames[i], argv[i]);
   }
   printf("\n");
   return 0;
}

int main(int argc, char* argv[])
{
   sqlite3 *db;
   char *error = 0;
   int res;
   char *sql;

   /* Open database */
   res = sqlite3_open("test.db", &db);
   if (res)
     {
       fprintf(stderr, "No puedo abrir la base de datos: %s\n", sqlite3_errmsg(db));
       exit(0);
     }
   else
     {
       fprintf(stderr, "Base de datos OK\n");
     }

   /* Create SQL statement */
  sql = "SELECT * FROM events;";

   /* Execute SQL statement */
  res = sqlite3_exec(db, sql, selectCb, 0, &error);
   if (res != SQLITE_OK)
     {
       fprintf(stderr, "Error: %s\n", error);
       sqlite3_free(error);
     }
   else
     {
       fprintf(stdout, "SELECT Ok!\n");
     }
   sqlite3_close(db);

   return

En este caso tendremos una salida completa con todos los valores de la tabla, aunque bien podemos usar WHERE, LIMIT, etc.

Tenemos un argumento curioso para sqlite3_exec(), que directamente he colocado como 0 en todas las peticiones, ese valor será una variable que podemos pasar al callback cada vez que se ejecute, y que puede tomar el valor que queramos (y también recibir, que para eso es un puntero), lo que significa que tenemos muchas más posibilidades, como poner un identificador para decidir qué hacer con la información recibida o crear otra estructura basada en listas enlazadas para almacenar todo el SELECT y poder leerlo tras ejecutar sqlite3_exec() si todo ha ido bien.

Una pequeña buena práctica

Es bueno utilizar llamadas a sqlite3_initialize() cuando vamos a empezar a utilizar la biblioteca. Y a sqlite3_shutdown() cuando terminamos de utilizarla y no vamos a hacerlo más. Es verdad que no hace realmente falta, pero podemos compilar especificando el define SQLITE_OMIT_AUTOINIT, como su nombre indica, no auto-inicializará. No ganaremos excesiva velocidad no inicializando porque sqlite3_initialize() cuando ya está el sistema inicializado no hará nada, pero son llamadas y comprobaciones que podemos hacer y si nuestra aplicación exige mucho uso de SQLite o el sistema es modesto, seguro que se agradece.

The post Usando SQLite en nuestros programas en C/C++ (I) appeared first on Poesía Binaria.

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

Koalite

Machine Learning con Princesas Disney

March 23, 2015 05:06 AM

A menos que hayas estado viviendo en una cueva desconectado del mundo durante los últimos años, seguro que estás al tanto de uno de los fenómenos que ha resurgido con fuerza: las Princesas Disney. Aunque algunas llevan desde los años cuarenta del siglo pasado entre nosotros y el concepto de unir a todas las protagonistas femeninas de películas Disney data de los 90, desde finales de los 2000 han recuperado su tirón en determinados sectores de la población.

Al igual que las Princesas Disney, en los últimos tiempos el machine learning (aprendizaje automático) ha vuelto a ser una disciplina en auge. No se trata de algo nuevo, las ideas en las que se basa y muchos de los algoritmos que utiliza son de hace varias décadas, pero la potencia de cálculo disponible actualmente (bendita nube) y el volumen de datos recolectado por los sistemas modernos ha hecho que vuelva a estar en el candelero.

OJO: No soy un experto en machine learning (ni en Princesas Disney) y no creo que lo sea a corto plazo, así que es probable que en este post haya imprecisiones o, directamente, errores. Estaré encantado de recibir correcciones en los comentarios.

Machine Learning

A grandes rasgos, el machine learning consiste en el proceso de grandes volúmenes de datos no estructurados para intentar detectar generalizaciones entre ellos. Por ejemplo, a partir de información de miles de compras podríamos intentar buscar patrones que nos indiquen si una compra es potencialmente fraudulenta o si un cliente es más propenso o no a volver a comprar después de adquirir determinados productos.

Existen varios tipos de algoritmos de machine learning, pero en general podríamos separarlos en dos tipos:

  • Aprendizaje supervisado: son algoritmos que alimentamos con un conjunto de valores de entrada para los cuales conocemos la salida obtenida. El algoritmo se encargará de construir un modelo que, dado un valor de entrada, sea capaz de generar la salida esperada. Un ejemplo típico son los filtros bayesianos que se utilizan en lo sistemas antispam. A partir de muchos correos de los que sabemos a priori si son spam o no, construimos un modelo que nos permita decidir si un nuevo correo es o no es spam.
  • Aprendizaje no supervisado: son algoritmos en los cuales sólo tenemos ejemplos, pero no existe una categorización a priori. Estos algoritmos lo que hacen es clasificar los valores de entrada, permitiéndonos encontrar relaciones entre ellos que no conocíamos inicialmente. Un ejemplo de esto serían algoritmos que nos permitiesen segmentar una base de datos de clientes para luego lanzar ofertas personalizadas.

Estos algoritmos suelen tener una base estadística importante y no siempre son fáciles de implementar, pero por suerte existen librerías que nos dan parte del trabajo hecho.

Analizando las Princesas Disney

Para jugar un poco con estas ideas vamos a utilizar numl, una librería para .NET disponible como paquete NuGet que implementa varios algoritmos de machine learning, tanto supervisados como no supervisados, y que resulta bastante fácil de manejar una vez que comprendemos ciertos conceptos.

Los algoritmos de machine learning necesitan recibir un conjunto de ejemplos iniciales para generar las funciones que nos permitan predecir el valor de salida en el caso de los algoritmos de aprendizaje supervisado, o establecer las categorías en el caso e los algoritmos de aprendizaje no supervisado.

Cada ejemplo constará de una serie de características (features) que serán las propiedades que definan el ejemplo, y un resultado (label) que será lo que intentemos predecir con el modelo generado por un algoritmo de aprendizaje supervisado. A partir de esa metainformación podremos crear un Descriptor para convertir nuestros datos de ejemplo en un formato manejable por los algoritmos genéricos.

Para representar los datos de entrada, que en nuestro caso es la información de Princesas Disney, usaremos la siguiente clase:

public class Princess
{
  public string Name { get; set; }
  public string Country { get; set; }
  public int Age { get; set; }
  public HairColor HairColor { get; set; }
  public bool Married { get; set; }
  public bool FatherAlive { get; set; }
  public bool MotherAlive { get; set; }
}

En ella incluimos datos como el nombre de la princesa, su país de procedencia, edad, color de pelo, si está casada, si su padre está vivo y si su madre está viva. Aunque podríamos decorar con atributos las propiedades para indicar qué es una feature y qué es un label, en este caso vamos a hacerlo de forma externa a la clase para poder analizar distintas facetas:

var marriedDescriptor = Descriptor.For<Princess>()
  .With(x => x.Age)
  .WithString(property: x => x.Country, asEnum: true, splitType: StringSplitType.Word)
  .With(x => x.FatherAlive)
  .With(x => x.MotherAlive)
  .With(x => x.HairColor)
  .Learn(x => x.Married);

Este Descriptor está estableciendo como datos de entrada las propiedades de la princesa (excepto el nombre) y como valor de salida si está casada o no. Es decir, pretendemos conseguir una función que nos ayude a determinar si una princesa se casará o no en función de si su color de pelo o de si sus padres están vivos o muertos. En el caso de Country, que es un poco más extraño, en realidad lo único que estamos diciendo es que trate los valores como si fuese un tipo enumerado aunque sean strings.

Ahora que tenemos el Descriptor vamos a construir un Generator que nos permita obtener el modelo con el que predecir si una princesa se casará o no. Para ello vamos a utilizar uno de los sistemas más sencillos: un árbol de decisión.

Simplificando mucho, en un árbol de decisión cada nodo interno discrimina sobre una característica y tiene tantos hijos como posibles valores pueda tomar esa característica. En las hojas se almacena el resultado final (label) asociado a una entidad cuyas propiedades se correspondiesen a haber recorrido el camino desde la raíz hasta ella. Si habéis jugado alguna vez al juego de las 20 preguntas para adivinar un personaje famoso, es la misma idea.

Sabiendo esto, vamos a generar un modelo a partir de un árbol de decisión:

var generator = new DecisionTreeGenerator(marriedDescriptor);
// Queremos aprender a detectar cuando el valor de Married será true
generator.SetHint(true);


// Lanzamos el proceso de aprendizaje (generación del árbol) con los 
// datos de todas las princesas conocidas hasta ahora
var model = Learner.Learn(PRINCESSES, 0.8m, 1000, generator);

Console.WriteLine(model);

Al pintar el árbol en consola, veremos algo así:

Model:
     [Country, 0,7342]
      |- ALEMANIA
      |       +(True, 1)
      |- FRANCIA
      |       +(True, 1)
      |- ATLÁNTIDA
      |       +(True, 1)
      |- CHINA
      |       +(True, 1)
      |- MALDONIA
      |       +(True, 1)
      |- ESCOCIA
      |       +(False, -1)
      |- NORUEGA
      |      [Age, 1,0000]
      |       |- 18 = x < 19,5
      |       |       +(True, 1)
      |       |- 19,5 = x < 21,01
      |       |       +(False, -1)
      
  Accuracy: 100,00 %

Interesante. Para que una Princesa Disney consiga casarse o no, influye más el país de origen que el color de pelo o que su madre (y futura suegra del esposo) esté viva o muerta.

Podemos utilizar este modelo para predecir si una princesa se casará o no:

var celia = new Princess 
{
  Name = "Celia",
  Age = 16,
  Country = "Noruega",
  FatherAlive = true,
  MotherAlive = true,
  HairColor = HairColor.Blonde,
}

var willGetMarried = model.Model.Predict(celia).Married;

Console.WriteLine("Celia se casará: {0}", willGetMarried);

El resultado de la predicción es que sí, afortunadamente Celia encontrará su principe azul. Tiene sentido, si tenemos en cuenta que según nuestro árbol de decisión generado previamente las princesas noruegas de menos de 19.5 años se casan.

No voy a alargar mucho el post con más ejemplos, pero podéis ver otros apasionantes análisis (ejem) sobre las Princesas Disney y las muertes de sus progenitores en este gist.

No hay magia

Obviando lo absurdo que es el ejemplo, es fundamental ser consciente de que esto del machine learning no tiene nada de magia. Es decir, vamos a construir un modelo a partir de unos datos de entrada y aunque parezca que ese modelo se construye a partir de algoritmos sofisticados muy inteligentes, en realidad nuestro análisis inicial va a condicionar la utilidad de los resultados que podamos obtener.

Cuando decidimos qué features queremos tener en cuenta estamos imponiendo ya una serie de ejes sobre los que tomar las decisiones, por lo que si los features no son representativos, los resultados tampoco lo serán.

Una vez que obtenemos el modelo, en el caso de ejemplo el árbol de decisión, debemos analizarlo con cuidado porque entramos en un terreno farragoso: ¿Es el modelo resultado de la casualidad? ¿Hay causalidad real? ¿Hay al menos correlación entre características y resultado? Podemos minimizar el peso la casualidad si tenemos un volumen suficientemente grande de datos, pero decidir si estamos ante una relación de causalidad o correlación es más complicado.

Conclusiones

El machine learning o aprendizaje automático es una disciplina muy interesante ahora que la potencia de cálculo es más accesible y tenemos grandes volúmenes de datos a nuestra disposición para alimentar los algoritmos.

Aunque la base matemática subyacente tiene cierta complejidad, con librerías como numl podemos empezar a jugar de una manera fácil con estos conceptos y, quién sabe, tal vez encuentres patrones interesantes en los datos que manejas en tu día a día que te ayuden a tomar decisiones.

No hay posts relacionados.

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

Blog Bitix

Introducción y ejemplo de API RPC con Apache Thrift

March 21, 2015 10:30 AM

Java

Las aplicaciones están pasando de ser elementos aislados, grandes y monolíticos a ser desarrolladas como varios microservicios que colaboran entre si para en conjunto ofrecer la funcionalidad deseada. Aunque los microservicios presentan sus propias problemáticas resuelven algunas que poseen los sistemas monolíticos, entre algunas de sus caracteríticas deseables están: mayor cohesión, menor acoplamiento, menor tamaño, mayor independencia de la tecnología usando la más adecuada en cada situación, más fácilmente reemplazables y despliegues más sencillos. También, la funcionalidad ofrecida por una aplicación puede quererse consumirse desde otra aplicación surgiendo de esta forma una API. Para ofrecer una API de una aplicación que pueda consumirse internamente, desde diferentes dispositivos o por terceras partes podemos usar SOAP o REST pero también han surgido algunas alternativas como Apache Thrift supliendo algunas carencias de las anteriores aún basándose en el mismo concepto de llamada a código en una máquina remota (Remote Procedure Call, RPC) ya visto en algunas opciones más antiguas como la misma SOAP, RMI o CORBA. En este artículo explicaré algunas diferencias entre SOAP, REST y Apache Thrift y mostraré un ejemplo sencillo con código de cómo empezar a usar Apache Thrift.

En los modelos RPC las llamadas a métodos se hacen a través de la red de forma transparente aunque tendremos que tener en cuenta que se utilizando un medio no fiable y con un rendimiento menor que llamadas en la misma máquina que notaremos más si se usan muchas llamadas. SOAP es una forma de RPC en la que se utiliza XML, algunas críticas a SOAP son que el XML utilizado para la comunicación es complejo y los servicios SOAP no son fácilmente consumibles desde por ejemplo un navegador. Por otra parte, las API REST tratan de solventar algunas de las deficiencias de SOAP como por ejemplo estar expuestas como recursos fácilmente accesibles utilizando los mismos mecanismos de la web y un formato para el intercambio de datos como JSON más sencillo y fácilmente consumible que XML. Sin embargo, algunas críticas que se le están haciendo REST son:

  • APIs asíncronas: el modelo RESTful de petición y respuesta no se adapta bien a un modelo donde hay necesidad de enviar datos de forma asíncrona evitando sondear continuamente el servidor con peticiones que consumen recursos de red y de servidor. El modelo asíncrono envía nuevos datos únicamente cuando estos se hacen disponibles.
  • Orquestación y experiencia de la API: la granularidad de una API REST no se adapta correctamente a algunas situaciones haciendo necesario realizar varias peticiones HTTP lo que añade carga al cliente, servidor y la red. Orquestando APIs internas en el servidor y publicando una que esté adaptada a lo que necesitan los diferentes clientes supone un mejor rendimiento y simplicidad.
  • SDKs vs APIs: los usuarios de las APIs finalmente las consumen desde un lenguaje de alto nivel como JavaScript, Python, Ruby, Java, PHP, C#, etc. con lo que los proveedores de las APIs necesitan ofrecer librerías cliente para algunos de estos lenguajes.
  • Protocolos binarios: los formatos binarios son más eficientes que el texto plano, lo que es útil en dispositivos limitados como los utilizados en el internet de las cosas (IoT).
  • Alta latencia: la sobrecarga que introduce el protocolo http en cada petición no lo hace adecuado en situaciones en que una baja latencia es necesaria para proporcionar un rendimiento óptimo.

Por otra parte algunos otros puntos a favor de RPC son:

  • Se tiene type safety y puede enviar excepciones que puede ser manejadas con la misma infraestructura ofrecida por el lenguaje de programación usado.
  • Si se hacen grandes volúmenes de llamadas y datos o hay requerimientos de ancho de banda se pueden usar protocolos de transporte más eficientes que HTTP.

Apache Thrift es un framework para desarrollar servicios eficientes e interoperables en diferentes lenguajes. Los lenguajes soportados en cualquier combinación de cliente y servidor son C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, OCaml y Delphi y alguno más. Para generar el código del servidor o cliente lo primero que debemos hacer es definir la interfaz del servicio en la que estén incluidas las operaciones, parámetros y retornos junto con sus tipos. A partir de esta interfaz Apache Thrift generará el cliente o servidor en el lenguaje que deseemos. Una vez publicada una versión de la interfaz podremos modificarla sin provocar problemas de compatibilidad en los clientes como ocurría en RMI. Una desventaja de Apache Thrift es que obliga a usar esta tecnología para consumir los servicios, en este sentido una API REST es más agnóstica en la que basta con el protocolo HTTP y JSON. Se puede optar por un modelo en el que de cara al exterior se ofrece una API REST pero internamente se usan APIs RPC. Veamos un ejemplo con Apache Thrift.

Primeramente, para usar Apache Thrift debemos instalar el paquete en la distribución que usemos. En Arch Linux con:

<noscript><pre><code>$ sudo pacman -S thrift</code></pre></noscript>

A continuación deberemos definir la interfaz del servicio, supongamos que queremos hacer un servicio que nos ofrezca un mensaje de ping, la hora del servidor y la suma de dos números. La interfaz de este servicio usando el DSL es:

<noscript><pre><code>namespace java io.github.picodotdev.blogbitix.thrift service Service { string ping() i32 add(1:i32 op1, 2:i32 op2) string date() } </code></pre></noscript>

Podemos elegir cualesquiera lenguajes deseemos de la amplia lista soportada anterior, en este caso usaré Java tanto para el servidor como para el cliente. Usando el comando thrift e indicando el lenguaje y la interfaz generamos los artefactos:

<noscript><pre><code>$ thrift -out src/main/java --gen java src/main/thrift/Service.thrift $ gradlew trift</code></pre></noscript>
<noscript><pre><code>description = 'HolaMundoApacheThrift' version = '0.1' apply plugin: 'eclipse' apply plugin: 'java' repositories { mavenCentral() } dependencies { compile 'org.apache.thrift:libthrift:0.9.2' runtime 'org.apache.logging.log4j:log4j-core:2.1' runtime 'org.apache.logging.log4j:log4j-slf4j-impl:2.1' } task wrapper(type: Wrapper) { gradleVersion = '2.3' } task thrift() { exec { commandLine 'thrift', '-out', 'src/main/java', '--gen', 'java', 'src/main/thrift/Service.thrift' } }</code></pre></noscript>

Esto nos genera unas clases en Java y una interfaz que implementaremos para proporcionar la funcionalidad del servicio, en el caso del ejemplo la interfaz es Service.Iface. Para que los clientes puedan consumir este servicio debemos iniciar el servidor que no será más que un programa Java que escucha las peticiones de los clientes en un puerto.

<noscript><pre><code>package io.github.picodotdev.blogbitix.thrift; import java.text.SimpleDateFormat; import java.util.Date; import org.apache.thrift.TException; import org.apache.thrift.server.TServer; import org.apache.thrift.server.TServer.Args; import org.apache.thrift.server.TSimpleServer; import org.apache.thrift.transport.TServerSocket; import org.apache.thrift.transport.TServerTransport; public class Server { public static void main(String[] args) { try { final Service.Processor&lt;Service.Iface&gt; processor = new Service.Processor&lt;Service.Iface&gt;(new Server.ServiceImpl()); Runnable simple = new Runnable() { public void run() { simple(processor); } }; new Thread(simple).start(); } catch (Exception e) { e.printStackTrace(); } } public static void simple(Service.Processor&lt;Service.Iface&gt; processor) { try { TServerTransport serverTransport = new TServerSocket(9090); TServer server = new TSimpleServer(new Args(serverTransport).processor(processor)); // Use this for a multithreaded server // TServer server = new TThreadPoolServer(new // TThreadPoolServer.Args(serverTransport).processor(processor)); System.out.println(&quot;Starting the service server...&quot;); server.serve(); } catch (Exception e) { e.printStackTrace(); } } private static class ServiceImpl implements Service.Iface { @Override public String ping() throws TException { System.out.println(&quot;Me han llamado ¡que ilusión! ^^&quot;); return &quot;¡Hola mundo!&quot;; } @Override public int add(int op1, int op2) throws TException { return op1 + op2; } @Override public String date() throws TException { return new SimpleDateFormat(&quot;dd-MM-yyyy HH:mm:ss Z&quot;).format(new Date()); } } }</code></pre></noscript>

Una vez están los servicios disponibles podemos consumirlos con las siguientes siguientes líneas de código de una implementación de cliente, basta hacer uso de las clase Service.Client generada a partir de la interfaz del servicio.

<noscript><pre><code>package io.github.picodotdev.blogbitix.thrift; import org.apache.thrift.TException; import org.apache.thrift.protocol.TBinaryProtocol; import org.apache.thrift.protocol.TProtocol; import org.apache.thrift.transport.TSocket; import org.apache.thrift.transport.TTransport; public class Client { public static void main(String[] args) { try { TTransport transport = new TSocket(&quot;localhost&quot;, 9090); transport.open(); TProtocol protocol = new TBinaryProtocol(transport); Service.Client client = new Service.Client(protocol); System.out.println(String.format(&quot;Ping: %s&quot;, client.ping())); System.out.println(String.format(&quot;Add: %d&quot;, client.add(4, 7))); System.out.println(String.format(&quot;Date: %s&quot;, client.date())); transport.close(); } catch (TException e) { e.printStackTrace(); } } }</code></pre></noscript>

Ejecutando el cliente y llamando a los métodos de la interfaz del servicio veremos en la terminal la siguiente salida:

Si te interesan las arquitecturas de aplicaciones con microservicios ya sea con API RPC o REST un libro muy interesante y recomendable es Building Microservices. Proporciona una visión detallada de los diferentes aspectos que deben tratar este tipo de aplicaciones.

Apache Thrift no es la única herramienta para hacer llamadas RPC, una muy similar es gRPC de Google e igualmente interesante al hacer uso de HTTP/2 y Protocol Buffers.

El código fuente completo del ejemplo lo puedes encontrar en mi repositorio de GitHub.

Referencia:
Is REST losing its flair? REST API Alternatives
What is the advantage of using Thrift, as opposed to exposing an HTTP REST API?
Creating a public API with Apache Thrift
Thrift vs Protocol Buffers vs Avro – Biased Comparison

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

Blog Bitix

Aplicación web Java autocontenida con Tomcat Embedded

March 20, 2015 05:00 PM

Java

La tendencia de las aplicaciones es que sean construidas como múltiples servicios pequeños que colaboran entre si en vez de consistir en una aplicación grande desplegada en un servidor de aplicaciones. La aparición de nuevas tecnologías como Docker facilitan la construcción y despliegue de los microservicios. Los microservicios con su máxima de bajo acoplamiento y alta cohesión tratan de incluir todo lo necesario para funcionar evitando las dependencias de cualquier tipo de elemento fuera de su ámbito no incluyendo el uso de otros servicios. Con Docker es posible desplegarlos en cualquier máquina que disponga del servicio, esto evita problemas de configuración al pasar la aplicación de un entorno de desarrollo a uno de producción ya que las características del entorno de desarrollo y el de producción pueden ser idénticos.

Los microservicios pueden ser autocontenidos de tal forma que incluyen todo lo necesario para prestar su servicio evitando por ejemplo sin depender de un servidor de aplicaciones en el que desplegar la aplicación que ha de ser instalado previamente, para ello pueden incluir un servidor embebido de Tomcat, de Jetty o usando Spring Boot. Esto evita malos funcionamiento por diferencias en la configuración o de versiones de los servidores en cada uno de los entornos, además hace más fácil el despliegue en una nueva máquina siendo lo único necesario el microservicio, sin necesidad de disponer previamente un servidor externo. Por otra parte si usamos Docker para el microservicio evitamos que configurar la máquina física o virtual directamente, todo lo que necesite el microservicio estará en la imagen Docker, nuevamente evitamos problemas de configuración entre entornos.

Para hacer cualquier aplicación autocontenida sin necesidad de instalar el servidor de aplicaciones como entorno en el que desplegar la aplicación podemos usar Tomcat Embedded. Disponiendo del archivo .war típico de una aplicación web en Java podemos desplegarlo en el servidor embebido, el inicio de la aplicación será como cualquier otra aplicación Java, con su método main, usando la API ofrecida por Tomcat podemos iniciar el servidor de forma programática y realizar el despliegue de la aplicación .war.

Para la demostración usaré la aplicación con los ejemplos que hice para el libro PulgIn Tapestry que trataba del el framework de desarrollo Apache Tapestry. Primeramente deberemos añadir al proyecto la dependencia de tomcat-embedded de forma que podamos importar las clases y paquetes de tomcat a usar en la clase que iniciará la aplicación.

<noscript><pre><code>description = 'TomcatEmbedded' version = '0.1' apply plugin: 'application' apply plugin: 'eclipse' apply plugin: 'java' mainClassName = 'io.github.picodotdev.blogbitix.tomcatEmbedded.Main' repositories { mavenCentral() } dependencies { compile('org.apache.tomcat.embed:tomcat-embed-core:8.0.20') compile('org.apache.tomcat.embed:tomcat-embed-logging-log4j:8.0.20') compile('org.apache.tomcat.embed:tomcat-embed-jasper:8.0.20') compile('junit:junit:4.12') } jar { manifest { attributes('Main-Class': 'io.github.picodotdev.blogbitix.tomcatEmbedded.Main') } } task wrapper(type: Wrapper) { gradleVersion = '2.3' } applicationDistribution.from('tomcat/webapps/PlugInTapestry.war') { into &quot;tomcat/webapps/&quot; }</code></pre></noscript>

Posteriormente crearemos una clase Java con su método main que inicie el servidor de aplicaciones embebido con la aplicación web desplegada en él, podemos indicar el puerto que queremos que escuche y las configuraciones que necesitemos tal como si lo configurásemos el archivo server.xml o context.xml pero usando código Java, usaremos la API ofrecida por las clases incluidas en las dependencias anteriores (org.apache.catalina.startup.Tomcat).

<noscript><pre><code>package io.github.picodotdev.blogbitix.tomcatEmbedded; import org.apache.catalina.startup.Tomcat; public class Main { public static void main(String[] args) throws Exception { Tomcat tomcat = new Tomcat(); tomcat.setBaseDir(&quot;tomcat&quot;); tomcat.setPort(8080); // Para configurar el puerto seguro // http://www.copperykeenclaws.com/adding-an-https-connector-to-embedded-tomcat-7/ // Connector httpsConnector = new Connector(); // httpsConnector.setPort(443); // httpsConnector.setSecure(true); // httpsConnector.setScheme(&quot;https&quot;); // httpsConnector.setAttribute(&quot;keyAlias&quot;, keyAlias); // httpsConnector.setAttribute(&quot;keystorePass&quot;, password); // httpsConnector.setAttribute(&quot;keystoreFile&quot;, keystorePath); // httpsConnector.setAttribute(&quot;clientAuth&quot;, &quot;false&quot;); // httpsConnector.setAttribute(&quot;sslProtocol&quot;, &quot;TLS&quot;); // httpsConnector.setAttribute(&quot;SSLEnabled&quot;, true); // // Tomcat tomcat = new Tomcat(); // Service service = tomcat.getService(); // service.addConnector(httpsConnector); // // Connector defaultConnector = tomcat.getConnector(); // defaultConnector.setRedirectPort(443); tomcat.addWebapp(&quot;/PlugInTapestry&quot;, &quot;tomcat/webapps/PlugInTapestry.war&quot;); tomcat.start(); // Puerto para enviar el comando SHUTDOWN // telnet localhost 8005 // SHUTDOWN tomcat.getServer().setPort(8005); tomcat.getServer().await(); } }</code></pre></noscript>

Generamos el war de la aplicación que queremos desplegar embebida, e iniciamos la aplicación con la clase que contiene el método main con Gradle o desde la linea de comandos con java, necesitaremos descargar las librerías de Tomcat Embedded y en este ejemplo copiarlas al directorio lib/ junto con la librería TomcatEmbedded-0.1.jar que contiene la clase Main construida con el comando gradlew build:

<noscript><pre><code>$ ./gradlew build $ ./gradlew run $ java -classpath &quot;lib/*&quot; io.github.picodotdev.blogbitix.tomcatEmbedded.Main $ ./TomcatEmbedded</code></pre></noscript>

La tendencia actual es que las aplicaciones evolucionen hacia microservicios por varias características deseables que ofrecen como al ser más pequeñas las funcionalidades sean más manejables, sean reemplazables, posibilidad de usar la tecnología más adecuada según el servicio desde lenguaje de programación al sistema de persistencia (relacional o noSQL), facilidad de despliegue, …. Si te interesan los microservicios un libro muy interesante y recomendable es Building Microservices. Proporciona una visión detallada de los diferentes aspectos que deben tratar las aplicaciones construidas según esta arquitectura.

El código fuente completo del ejemplo y el código fuente de la aplicación web usada los puedes encontrar en mi repositorio de GitHub. Finalmente he de decir que la aplicación usada aunque es un ejemplo no es simple (usa Tapestry, Spring, Hibernate, Shiro, H2) y a pesar de ello no he tenido ninguna excepción extraña que haya tenido que resolver, con esto quiero decir que usar Tomcat Embedded me ha resultado totalmente fiable.

Referencia:
Arquitectura orientada a servicios
Not Your Father’s Java: An Opinionated Guide to Modern Java Development, Part 1
An Opinionated Guide to Modern Java, Part 2: Deployment, Monitoring & Management, Profiling and Benchmarking
An Opinionated Guide to Modern Java, Part 3: Web Development

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

Poesía Binaria

Borrar la caché de Magento desde la línea de comandos

March 20, 2015 09:19 AM

Foto: Steve Jurvetson (Flickr CC)

Cuando estás haciendo muchos cambios en una instalación de Magento, debes borrar la caché a menudo. Un caso especial es si utilizas scripts periódicos para actualizar listas de precios o stock, en ese caso debes programar también el borrado de cachés.

Este pequeño script ha sido visto en muchas webs por lo que no me extenderé mucho y mencionaré dos fuentes donde podéis consultarlo (os invito a visitar estas páginas porque amplían la información que veis aquí).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
echo "Start Cleaning all caches at ... " . date("Y-m-d H:i:s") . "\n\n";
ini_set("display_errors", 1);

require '../app/Mage.php';
Mage::app('admin')->setUseSessionInUrl(false);
Mage::getConfig()->init();

$types = Mage::app()->getCacheInstance()->getTypes();

try {
    echo "Cleaning data cache... \n";
    flush();
    foreach ($types as $type => $data) {
        echo "Removing $type ... ";
        echo Mage::app()->getCacheInstance()->clean($data["tags"]) ? "Cache cleared!" : "There is some error!";
        echo "\n";
    }
} catch (exception $e) {
    die("[ERROR:" . $e->getMessage() . "]");
}

Aquí veremos un listado de tipos de caché que estamos utilizando en nuestra instalación y se van limpiando.

Si queremos borrar la caché de JS y CSS bastaría con llamar a (debemos asegurarnos de que el usuario que ejecuta el script (si es distinto al que procesa la web) tiene permisos para escribir en la carpeta de caché:

1
2
3
<?php
    Mage::getModel('core/design_package')->cleanMergedJsCss();
    Mage::dispatchEvent('clean_media_cache_after');

Ya podemos llamar al script desde la línea de comandos, por ejemplo, o programarlo como tarea cron.

Visto en:
Yameveo | Flush every Magento cache from the command line
Magento: Clear all caches from command line

The post Borrar la caché de Magento desde la línea de comandos appeared first on Poesía Binaria.

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

Juanjo Navarro

Google amenaza: Hora de adaptarse a móviles

March 19, 2015 11:05 PM

En los últimos días he recibido un par de mensajes del servicio de Google Webmaster Tools en el que me insta a mejorar el soporte para móviles en algunos de mis proyectos:

Google systems have tested 88 pages from your site and found that 100% of them have critical mobile usability errors. The errors on these 88 pages severely affect how mobile users are able to experience your website. These pages will not be seen as mobile-friendly by Google Search, and will therefore be displayed and ranked appropriately for smartphone users.

(las negritas son mías)

En este caso, son dos proyectos viejos que no creo que arregle (más bien es posible que los cierre) pero el mensaje que Google nos da es claro: Estamos en el 2015. Casi el 40% de los accesos a la web se hacen con dispositivos móviles. Es hora de adaptar nuestros sitios web al uso con móviles.

A continuación os dejo algunas referencias que he ido recopilando con el tiempo sobre diseño web responsive:

  • El artículo ya clásico Responsive Web Design, de Ethan Marcotte, explica el concepto y es una buenísima introducción.
  • Un buen libro (del mismo autor) para iniciarse es el de A Book Apart: Responsive Web Design. Aunque ya tiene unos años, explica las técnicas básicas y lo hace con ejemplos, que es lo importante.
  • Genbeta creó una serie con varios artículos de introducción. No obstante, advertir que el viewport que recomiendan a mi no me gusta nada, ya que impide que el usuario pueda hacer zoom. Yo prefiero el siguiente:
<meta name="viewport" content="width=device-width, initial-scale=1">
  • Para tomar ideas e inspirarse tenemos Media Queries. Un showcase de sitios web responsive donde podemos ver cómo los distintos diseños se adaptan a las distintas pantallas.
  • A Simple Device Diagram for Responsive Design Planning nos ayuda a decidirnos por los puntos de corte en los que cambiar los estilos. De todas formas, hoy en día, con el gran número de dispositivos existentes, cada uno con un tamaño de pantalla, los puntos de corte son cada vez menos importantes. Hay que hacer diseños que se vean bien en cualquier resolución.
  • El tema de las imágenes que se adaptan a cualquier resolución y a cualquier densidad de pantalla (retina) es un mundo aparte. Un par de artículos interesantes son Responsive Images For Retina: Using HTML5’s srcset y Responsive Images in Practice.
  • Por último, si tienes que presentar tus diseños responsive a un cliente hay un par de herramientas que te pueden interesar: Responsinator y Demostrating Responsive Design.

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

Poesía Binaria

Sonidos de un viejo disco duro IDE realizando tareas

March 18, 2015 09:35 AM

Ingredientes: Un disco duro viejo, un adaptador USB, un micrófono y algunos comandos para probar. Me pareció una cosa muy friki y bastante chula. Un disco duro que salió al mercado en Enero del año 2000 (¡¡15 años!!) y todavía funciona. No sabría decir si como el primer día, pero funciona bastante bien, aunque a estas alturas, como no tengamos a disposición un adaptador especial IDE sería muy difícil echarlo a andar.

De la época en la que 20.4Gb era muchísimo e instalábamos Windows en los ordenadores y no llegaba a ocupar 1Gb, y sobre todo, de la época en la que sabíamos a oídas si el ordenador estaba haciendo algo o no (aunque los primeros SATA hacían más ruido), en la que, cuando apagabas la corriente del ordenador, se podía apreciar el silencio puro.

The post Sonidos de un viejo disco duro IDE realizando tareas appeared first on Poesía Binaria.

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

Picando Código

Silicon Milkroundabout – Feria de trabajo en Edinburgh

March 17, 2015 03:00 PM

Silicon MilkroundaboutEl pasado Sábado 7 de marzo estuvo en Edimburgo la feria de trabajo Silicon Milkroundabout. Es parte de una serie de ferias de trabajo que comenzó en Londres, orientada a la comunidad de startups basadas en tecnología con la intención de atraer talento.

Otra persona de la comunidad latinoamericana de Rubystas que gusta de visitar Escocia es Cristian Rasch. Y viniendo los dos por estos lados a cada tanto, era cuestión de tiempo hasta que coincidiéramos por acá. Y así fue durante los primeros días de marzo.

Así que ese fin de semana coordinamos para encontrarnos en la feria. No fuimos con la idea concreta de buscar trabajo, sino más bien para ver de qué venía la mano y ver qué tal la oferta laboral. La inscripción era mediante un formulario en el sitio web donde debíamos ingresar un poco de información personal. Aparentemente la idea es filtrar a posibles recruiters molestos y dar prioridad a candidatos que se adaptaran a los perfiles que buscaban las empresas a presentarse.

Un dato curioso fue que el día del evento me llegó un correo informándome que había obtenido un “Gold Top Pass” para la feria, lo que me permitía llegar más temprano y conocer a las empresas y hablar con sus representantes antes que se abrieran las puertas a todo público. Por lo que decía el correo, se seleccionaba a mano a gente inscripta cuyas habilidades o experiencia se destacara como particularmente relevante a las compañías, y yo fui una de esas personas. Así que existía la posibilidad de que si hubiera ido buscando trabajo, hubiera conseguido algo. Si yo -un uruguayo perdido en Escocia- puedo, cualquiera puede, ¡métanle a Ruby! :P

Con Cristian nos encontramos después del almuerzo, a la hora pico cuando había más gente. El lugar era The Hub, en un edificio construido en 1800 y pico por la Iglesia de Escocia como parroquia y sala de asamblea general. Hoy funciona como la sede del Festival Internacional de Edimburgo, y como fuente de información de otros festivales en la ciudad. Es un edificio con una arquitectura genial como podrán imaginar.

Silicon Milkroundabout

Al llegar nos daban una bolsa de tela con una botella de agua y un mapa del lugar como para orientarnos y buscar alguna empresa específica con la que quisiéramos tener contacto. Arriba había una cafetería y un bar donde podíamos pedir bebidas varias.

También más adelante nos daban la clásica credencial para llevar colgada y escribir nuestro nombre. Algo particular de este evento es que nos daban también una hoja con pegotines de distintas especialidades o tecnologías. La idea era pegarlos en la credencial misma con el objetivo de mostrar qué estábamos buscando o qué sabíamos hacer como potenciales cazadores de trabajo. En general no sería muy adepto a este tipo de etiquetas, pero para ser pragmáticos en una feria de trabajo tiene sentido.

Silicon Milkroundabout

Trabajo en Escocia

Creo que el primer lugar al que nos acercamos fue el puesto de TalentScotland. Se trata de una agencia que promueve Escocia como país donde mudarse a trabajar. Su misión es ayudar a las empresas de la industria con los trámites relacionados a personas que vengan a trabajar al país. Si estuvieran interesados en trabajar en Escocia, el sitio de TalentScotland sería un buen punto de partida.

También nos acercamos al puesto de Amazon que tiene oficinas en el centro de Edimburgo, y está buscando gente para unirse a su equipo. Nos contaron un poco de lo que se hace desde acá, y parece que hay para todos los gustos. Desde bien abajo en el backend con el tema de sistemas distribuídos, machine learning hasta diseño de interfaces visuales y animación en 3D. Además de la variedad de cosas para hacer, parentemente no es difícil moverse entre equipos dentro de la empresa.

Otra empresa a la que nos acercamos por pura curiosidad fue Soluis. Se especializan en trabajo con realidad aumentada, visualizaciones fotorealísticas, animación 3D y todo ese mundillo tan interesante. Además de conocer un poco con qué trabajaban, nos intrigó el casco Oculus Rift que tenían en su puesto. Tuvimos la oportunidad de probarlo y la verdad que está bastante bueno. Por alguna razón esperaba algo mucho más espectacular y sorprendente, pero fue más o menos lo que imaginaba. Había un ambiente 3D que podíamos recorrer con un joystick de XBox y mirar para todos lados girando la cabeza. El potencial es infinito, aunque el programador cínico que llevo adentro me dice que su uso principal va a terminar siendo reuniones con Project Managers en equipos remotos, jeje.

Recorrimos bastante, tomamos café y ya por el final una cerveza. Como siempre está genial ir a estos eventos para conocer gente del ambiente, conversar mucho, y todos los contactos pueden ser potencialmente útiles en el futuro.

Parece que el mercado está creciendo en el Reino Unido, y al igual que en tantos otros lugares, el desafío es conseguir gente. Si les interesa vean las compañías que se presentaron para conocer más sobre el mercado por acá, puede haber algo que les interese.

Silicon Milkroundabout

No estábamos buscando trabajo específicamente, pero estuvo bastante bueno ir hasta ahí y conocer más del mercado laboral. Si alguien considera ir a trabajar a Escocia, espero que esto le sirva por lo menos para tener una mínima idea de lo que tiene para ofrecer. ¡Suerte!

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

Arragonán

Semanas 352 y 353

March 16, 2015 03:03 PM

Sí, de nuevo me he saltado una semana sin hacer la retro, cuando me quise dar cuenta ya casi había pasado la anterior y preferí dejarlo para hacer la de las dos semanas juntas.

Estuve con temas relacionados con el próximo Startup Open Space, gestionando el tema de patrocinios y haciendo un mailing para los asistentes del año anterior. Este año hemos repartido mejor el trabajo, aunque vuelvo a tener la sensación de que a nada que me pongo con esto se me pasan las horas volando.

En Senpai Devs tuvimos visita de Teresa Oliver, a quien le pedimos que introdujera cuestiones de metodologías ágiles. En este par de semanas también aprovechamos para hablar de REST, dimos a conocer sinatra e introdujimos algo sobre bases de datos; también presentamos un proyecto en grupo e hicimos algo de trabajo de conceptualización.

Sobre futuros proyectos, mucho ruido y pocas nueces estas semanas: Uno en el que estaba en conversaciones para hacer programación en frontend se ha caído por el momento, ya que hemos decidido retomar conversaciones en un par de semanas para que les ayude en la parte de backend, que es donde más cómodo me siento. Y aparte salieron un par de leads más, aunque con mucho que concretar todavía.

Sobre proyectos actuales:

  • En BeSEPA mergeamos con la rama ‘master’ el trabajo de separación de aplicaciones que veníamos haciendo. Lo último que estuve haciendo fue la implementar la importación de datos para la migración.
  • Conseguí acabar al fin con el trabajo para Brazil, el segundo scraper para ellos.
  • One-stop sigue atascado por lado del cliente y no pude avanzar apenas, pero ya tenemos sobre la mesa una más que posible ampliación del alcance del proyecto.
  • Estuve trabajando en mosica (sideproject bautizado hoy mismo), tanto mejorando el API como en la aplicación móvil híbrida que la consumirá. Estoy aprovechando este proyecto para trastear con Ionic.
  • Además estuve recapitulando y haciendo alguna gestión relacionada con proyectoSinNombre. Sí, aún colean algunos flecos…

Buena semana.

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

Picando Código

Todos los caminos llevan a Escocia

March 16, 2015 01:00 PM

Nuevamente me encuentro visitando unos de los mejores países del mundo: Escocia.

Arthur's Seat

Desde mi cambio de rumbo en junio, he pasado de todo. Han habido montón de cambios en un tiempo relativamente acotado, aunque ahora que lo pienso ya hace casi un año…

Una de las situaciones por las que pasé fue el querer abandonar la programación y dedicarme a algo completamente distinto. Fue algo así como una “crisis Seldoniana” en la que no sabía cómo iba a terminar. En otros momentos me lo había planteado pero esta vez fue bastante en serio. Después hablando con mucha gente del rubro sobre la situación, me di cuenta que no estoy solo en eso y a muchos les pasa. Síntoma de que lo que hacemos no debe ser tan sano en ciertas condiciones. De a poco me recuperé, y como con toda crisis superada, crecí un poco cuando terminó.

Este tiempo he venido trabajando en modo contratista para distintos proyectos, incluido ATuServicio que comenté en la entrada anterior. Desde fin de año se venía perfilando la posibilidad y finalmente se cumplió y empecé a trabajar con la gente de Cultivate en Edimburgo. La empresa nació como EdgeCase en 2007, fue adquirida por Neo en 2012 y ahí fue que inicié la relación con esta oficina. Cuando se separó de Neo, nació Cultivate.

Si bien es mi tercera vez acá, las veces anteriores fueron por un tiempo menor (2 semanas) y no vine específicamente a trabajar. Tenía mis proyectos pero con otros clientes de Neo. Ahora estoy trabajando con clientes de Cultivate y haciendo Pair Programming prácticamente todo el tiempo. Es una experiencia muy buena en todo sentido y estoy muy contento de estar acá.

En principio voy a estar durante un mes, pero según cómo vaya la cosa (ya saben cómo es el tema con los contratos y plazos) se puede extender. Espero estar acá para ScotlandJS, la conferencia de JavaScript a la que tuve el agrado de asistir los últimos dos años. Viendo las charlas que se anunciaron este año, va a estar genial de nuevo.

Algo que se repite respecto a aquel post cambio de rumbo es la incertidumbre. Tengo pasaje de vuelta pero hay posibilidades de que tenga que cambiarlo. Y así continúa la aventura: no sé qué sigue después de eso, y es genial.

¡Gracias por leer!

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

Poesía Binaria

Cómo encontrar la ruta de un elemento dentro de una jerarquía en PHP

March 16, 2015 09:31 AM

Hace poco, hablábamos de cómo generar un array que contuviera una jerarquía de elementos, vimos cómo podíamos generarlo de forma muy rápida una vez tenemos todos los elementos en una lista en la que previamente indicamos el padre de cada uno de ellos.

El problema hoy es algo distinto, tenemos esa jerarquía o árbol, y queremos encontrar la ruta hacia arriba, es decir, todos los elementos por los que tenemos que pasar para llegar hasta un elemento dado. Puede ser útil cuando estén visitando nuestra página y accedan a algún elemento interior tras el cual queremos mostrar unas migas de pan o breadcrumbs (para ello tenemos que saber todo lo que hay por encima de la categoría actual).

Como ejemplo, vamos a hacer una jerarquía con paises / comunidades autónomas / provincias / ciudades y vamos a buscar una ciudad dentro de ese array (está simplificado en el ejemplo).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
<?php

$ex = array(
        array('id' => 1,
          'nombre' => 'España',
          'parentId' => 0),
        array('id' => 2,
          'nombre' => 'Portugal',
          'parentId' => 0),
        array('id' => 3,
          'nombre' => 'Francia',
          'parentId' => 0),
        array('id' => 4,
          'nombre' => 'Andalucía',
          'parentId' => 1),
        array('id' => 5,
          'nombre' => 'Galicia',
          'parentId' => 1),
        array('id' => 6,
          'nombre' => 'Madrid',
          'parentId' => 1),
        array('id' => 7,
          'nombre' => 'Castilla la Mancha',
          'parentId' => 1),
        array('id' => 8,
          'nombre' => 'Castilla y León',
          'parentId' => 1),
        array('id' => 9,
          'nombre' => 'Jaén',
          'parentId' => 4),
        array('id' => 10,
          'nombre' => 'Córdoba',
          'parentId' => 4),
        array('id' => 11,
          'nombre' => 'Sevilla',
          'parentId' => 4),
        array('id' => 12,
          'nombre' => 'Huelva',
          'parentId' => 4),
        array('id' => 13,
          'nombre' => 'Cádiz',
          'parentId' => 4),
        array('id' => 14,
          'nombre' => 'Málaga',
          'parentId' => 4),
        array('id' => 15,
          'nombre' => 'Granada',
          'parentId' => 4),
        array('id' => 16,
          'nombre' => 'Almería',
          'parentId' => 4),
        array('id' => 17,
          'nombre' => 'Linares',
          'parentId' => 9),
        array('id' => 18,
          'nombre' => 'Úbeda',
          'parentId' => 9),
        array('id' => 19,
          'nombre' => 'Baeza',
          'parentId' => 9),
        array('id' => 20,
          'nombre' => 'Baños de la Encina',
          'parentId' => 9),
        );

function buildTree($data, $rootId=0)
{
  $tree = array('children' => array(),
        'root' => array()
        );
  foreach ($data as $ndx=>$node)
    {
      $id = $node['id'];
      /* Puede que exista el children creado si los hijos entran antes que el padre */
      $node['children'] = (isset($tree['children'][$id]))?$tree['children'][$id]['children']:array();
      $tree['children'][$id] = $node;

      if ($node['parentId'] == $rootId)
    $tree['root'][$id] = &$tree['children'][$id];
      else
    {
      $tree['children'][$node['parentId']]['children'][$id] = &$tree['children'][$id];
    }

    }
  return $tree;
}

function findPath($tree, $childId, $reverse=true)
{
  $path = array($childId);
  $current = $childId;

  while (isset($tree['children'][$current]))
    {
      $current = $tree['children'][$current]['parentId'];
      $path[] = $current;
    }

  if (!$reverse)
    return array_reverse($path);
  else
    return $path;
}

$arbol = buildTree($ex);

print_r(findPath($arbol, 17));

Ahora buscamos el elemento con id 17 (Linares), tras ello, el script debe devolver:

Array
(
[0] => 17
[1] => 9
[2] => 4
[3] => 1
[4] => 0
)

lo cual es la ruta desde Linares hacia atrás: Linares (17), Jaén (9), Andalucía (4), España (1), root (0).

Esta función findPath() ahora mismo, funciona sólo con colecciones creadas con buildTree(), por lo que es muy importante que se respeten los nombres de los elementos (id de elemento, parentId para el id del elemento superior). De todas formas, podéis modificar estos algoritmos y os agradecería que comentaseis vuestros resultados.

Foto: Roberto Verzo (Flickr CC-by)

The post Cómo encontrar la ruta de un elemento dentro de una jerarquía en PHP appeared first on Poesía Binaria.

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

Koalite

La nube es para pobres

March 16, 2015 05:06 AM

Llevamos unos días en mi trabajo del Mundo Real™ evaluando opciones para renovar nuestras añejas herramientas de desarrollo, basadas en Subversion como VCS y Cruise Control.NET como servidor de integración, y tratar de mejorar algunos procesos.

Quiza lo más razonable dados los tiempos que corren sería plantearse una solución basada en servicios en la nube, pero después de analizarlo, podríamos resumir nuestras conclusiones en el título de este post: la nube es para pobres.

(El mérito de esa frase tan link baitter es de Luis Sánchez a quien le agradezco que me haya dejado usarla en este post.)

Si te lo puedes permitir, no usas la nube

¿Os imaginais a un jeque de esos que van quemando billetes en Marbella alquilando un coche (Platform as a Service) o cogiendo un taxi (Software as a Service)? No. Ellos tienen su propio Bentley con chófer on premises, siempre disponibles para ellos, sin necesidad de depender de nada externo.

Vale, seguramente este tipo de personas no sean el mejor ejemplo de racionalización de costes, pero lo cierto es que disponer de recursos propios te garantiza una independencia y control que no son fáciles de conseguir con soluciones en la nube.

Precisamente en las últimas dos semanas se ha anunciado el cierre de dos servicios de almacenamiento de código fuente, Codehaus y Google Code. En el caso de Google Code hay herramientas para migrar los proyectos a otras plataformas, pero no deja de ser un inconveniente tener que hacerlo y adaptar todos los procesos que puedas tener integrados con Google Code para que ahora se integren con GitHub o BitBucket.

Para mi éste es el mayor hándicap a la hora de optar por una solución en la nube: adquieres una dependencia sobre un proveedor externo.

Esta dependencia es mayor cuando se trata de una solución basada en Softare as a Service o incluso en Platform as a Service. Cuando utilizas Infrastructure as a Service tienes algo más de flexibilidad porque al final estás usando máquinas virtuales que, teóricamente son más fáciles de mover entre distintos proveedores.

Depender de un proveedor de servicios implica que cuando su modelo de negocio deje de ser rentable, tendrás problemas. Que cuando decidan descontinuar la versión que estás utilizando, tendrás que adaptarte. Que las paradas técnicas de manteniento (que no debería haberlas, pero las hay) las elegirá el proveedor y no tú.

Todo esto se puede mitigar si eres el dueño de tu infraestructura. En ese caso, tú decides hasta cuándo mantenerla, cuándo actualizarla o en qué momentos programar las operaciones de mantenimiento que implican una caída de servicio.

La parte mala, claro, es que esto no es barato. Necesitas comprar hardware, licencias de software y contratar (ya sea interna o externamente) recursos para la instalación y mantenimiento del sistema. Pero si te lo puedes permitir, es la opción más segura.

Entonces, ¿la nube es un timo?

No, ni mucho menos. Igual que un taxi nos permite a los que no tenemos chófer o vehículo propio disponer de un servicio para desplazarnos, la nube permite que los que no podemos afrontar tener datacenters por todo el mundo obtengamos redundancia geográfica y podamos servir el contenido más rápido a usuarios dispersos por el mundo.

Gracias a la nube podemos utilizar un sistema de facturación sin necesidad de saber instalarlo, podemos desplegar aplicaciones en servidores web perfectamente configurados sin que sepamos casi ni su nombre o disponer de potencia de computación en momentos puntuales que no amortizaríamos ni en una década si tuviésemos que comprar el hardware físicamente.

La nube hace accesibles tecnologías que antes estaban sólo al alcance de los más grandes.

Si en lugar de trabajar en una empresa que ya tiene una infraestructura de servidores estuviese trabajando por mi cuenta, gracias a servicios en la nube podría montar mi servidor de control de versiones y mi servidor de integración, cosa que no podría permitirme hacerlo por mi cuenta.

Por supuesto, tendría los riesgos de los que hablaba antes, pero al menos tendría una opción más sólida que montar un PC debajo de la mesa del salón y rezar porque no le pasase nada.

Además, aprovechar las posibilidades de la nube permite tener una mayor flexibilidad a la hora de dimensionar y cambiar los servicios y recursos que empleamos. Si tienes que invertir 3 semanas en desplegar una nueva aplicación en tus servidores locales es probable que dejes pasar oportunidades de mejora por ahorrarte ese esfuerzo, pero si hacerlo a través de proveedores de servicios resulta simple, podrás evolucionar tus soluciones de forma mucho más ágil.

Cuando la nube sí que se convierte un poco en un timo una opción discutible es cuando nos intentan vender soluciones ultracomplejas basadas en un cluster con cuatro redis, un ESB, dos SQL Servers y un MongoDB para montar un sistema de facturación para 30 usuarios.

Antes era impensable hacer eso por los costes que implicaba, pero ahora, como la nube los “abarata”, se oferta. La realidad es que en esos casos, si comparas el coste de la nube con el coste de una solución sensata, instalada en un servidor local (o en un hosting/housing si quieres más seguridad), al final sale más rentable huir de la nube. Pero esa es otra historia y la culpa no es de la nube.

Conclusiones

Aprovechando los servicios en la nube podemos tener acceso a tecnologías y arquitecturas muy interesantes, pero estamos introduciendo una dependencia más o menos fuerte sobre proveedores externos.

La gravedad de esta dependencia depende de la importancia que tenga para tu negocio lo que estés poniendo en la nube. No es lo mismo poner una web con información corporativa, que un sitio de comercio electrónico, que un sistema de producción como es, en nuestro caso, el control de código o el servidor de integración.

Si te lo puedes permitir, optar por soluciones on premises te da el máximo control sobre tus aplicaciones y a medio plazo puede incluso suponer un ahorro de costes (de forma similar a lo que ocurre al comprar una casa en lugar de alquilarla), pero los costes iniciales, y no sólo me refiero al aspecto puramente monetario, son indudablemente mayores.

Al final, igual que pasa con el código, la plataforma, nube o no, importa, pero el contexto más.

No hay posts relacionados.

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

Blog Bitix

Introducción a Ansible

March 14, 2015 11:45 PM

Ansible

Siempre que nos sea posible deberíamos automatizar las tareas que realizamos. Automatizar tareas hará sencillo realizarlas permitiéndonos hacer todos los despliegues de una aplicación que queramos con menos posibilidades de cometer errores manuales o nos permitirá disponer de una nueva máquina aprovisionada exactamente como pueda estar la máquina de producción en mucho menos tiempo. En el ámbito del aprovisionamiento de máquinas y administración de la infraestructura IT hay varias opciones, entre ellas están Chef, Puppet y Ansible.

En este artículo haré una breve introducción de Ansible que principalmente se emplea en servidores pero también podríamos emplearlo para nuestra propia máquina. Con Ansible podremos:

  • Automatizar el aprovisionamiento de máquinas.
  • Gestionar la configuración de los servicios de esas máquinas.
  • Realizar despliegues y orquestar los servicios.

Hay que destacar que Ansible no necesita instalar un agente (al contrario de Chef o Puppet) en cada una de las máquinas del inventario que queramos administrar lo que lo hace fácil de emplear, usa un modelo «push» en el que la máquina donde se ejecuta Ansible envía por ssh los comandos necesarios a aplicar en vez de ser cada una de las máquinas las que obtienen de un repositorio las recetas a usar. En los artículos Application Deployment, Configuration Management y Continous Delivery nos describen algunos casos de uso y sus ventajas empleando Ansible.

Para trabajar con Ansible necesitaremos inventariar las máquinas y probablemente definir algunas variables. Podría ser de la siguiente forma en el caso de una máquina para desarrollar.

<noscript><pre><code>devbox ansible_connection=local ansible_python_interpreter=python2 [devbox] devbox [devbox:vars] path = &quot;projects&quot; subversion_user = &quot;picodotdev&quot; subversion_password = &quot;***&quot;</code></pre></noscript>

En el inventario descrito como un archivo en formato INI se asignan los nombres del host o sus direcciones IP, también se pueden hacer agrupaciones de máquinas por ejemplo en base al rol (base de datos, servidor web, …). Una vez que disponemos del inventario podemos empezar a usar Ansible, por ejemplo haciendo un ping a todas las máquinas o instalando un determinado paquete:

<noscript><pre><code>$ ansible devbox -i hosts -m ping $ ansible devbox -i hosts -m pacman -a &quot;name=docker state=installed&quot;</code></pre></noscript>

El parámetro -m indica el módulo de Ansible que usamos y a continuación indicamos los parámetros. Ansible dispone de una amplia colección de módulos que nos permiten hacer cantidad de tareas.

Pero en vez de usar Ansible mediante comandos podemos emplear recetas contenidas en playbooks descritos en formato YAML en las que definimos varias tareas y podemos usar las variables del inventario. Con el siguiente playbook instalamos varios paquetes en una máquina Arch Linux y hacemos un checkout de dos proyectos de subversion, para ello usamos en la primera tarea el módulo para gestionar paquetes con pacman, hay módulos para los gestores de paquetes de otras distribuciones (apt, yum, …) y en la segunda tarea hacemos un checkout de dos proyectos usando el módulo del sistema de control de versiones subversion. Los módulos son idempotentes de forma que una vez que el sistema está en el estado deseado no se realiza la operación, esto hace que el mismo playbook pueda ser ejecutado tantas veces como se desee evitando efectos colaterales por reejecuciones, lo importante es el estado que se quiere conseguir, Ansible se encarga de realizar las acciones necesarias para llegar a él desde el estado actual del sistema.

<noscript><pre><code>- hosts: devbox tasks: - name: install packages (Arch Linux) pacman: name={{ item }} state=present sudo: true when: ansible_distribution == &quot;Archlinux&quot; with_items: - vim - subversion - mariadb - redis - docker - ansible - python2-pip - python2-virtualenv - name: checkout projects environment: LANG: es_ES.UTF-8 LC_CTYPE: es_ES.UTF-8 subversion: repo={{ item.url }} dest={{ projects }}/{{ item.path }} username={{ subversion_user }} password={{ subversion_password }} with_items: - { url: &quot;http://server.com/svn/repos/project1/trunk&quot;, path: &quot;project1&quot; } - { url: &quot;http://server.com/svn/repos/project2/trunk&quot;, path: &quot;project2&quot; }</code></pre></noscript>

Para ejecutar un playbook usamos el comando ansible-playbook en vez de simplemente el comando ansible.

<noscript><pre><code>$ ansible-playbook ansible/install.yml -i hosts</code></pre></noscript>

En los playbooks podemos usar tareas, grupos de máquinas, variables, variables de grupos, asignar valores a variables, usar condicionales, bucles, hechos (facts, información obtenida por ansible), notificaciones y realizar acciones en base a ellas, aplicar etiquetas a tareas, hacer includes, plantillas (para los archivos de configuración de los servicios, por ejemplo de apache o mysql), esperar a condiciones, cifrar archivos que contengan información sensible y que podamos incluir en una herramienta de control de versiones sin riesgo a comprometer la información, usar roles que aplican todas estas cosas según la función que queramos que tenga una máquina.

El libro Ansible Up & Running es un buen punto de partida que explica los aspectos básicos ya en su versión de vista previa, por supuesto la propia documentación del proyecto y los recursos de evangelización están bastante bien. En la siguiente buena y completa presentación se explican con un poco más detalle más cosas:

También, en el siguiente repositorio de GitHub hay varios ejemplos que podemos revisar para ver como se organizan los archivos y carpetas y las convenciones en su estructura que se usan implícitamente.

Como me ha ocurrido con la herramienta Elasticsearch la documentación de Ansible no me ha resultado que esté escrita de forma didáctica para dominarla empezando desde ningún conocimiento por ello un libro como Ansible: Up and Running es una opción interesate para aprender.

Habiendo hecho una introducción a Docker y esta a Ansible en el siguiente artículo comentaré como usar Docker con Ansible.

Referencia:
Artículo en la wikipedia
Presentación sobre Ansible
Ansible examples
Introducción a Docker
Guía de inicio básico de Docker
Cómo crear una imagen para Docker usando un Dockerfile
Integración entre Ansible y Docker
Introducción a Bitnami

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

Picando Código

AtuServicio – Proyecto de Datos Abiertos con DATA

March 13, 2015 01:00 PM

A principios de año estuve trabajando en un proyecto de Datos Abiertos con DATA. La historia de cómo llegué al proyecto y cómo se desarrolló fue bastante divertida. Un día me llamó Lu Escanellas, amiga programadora con la que fuimos compañeros de trabajo en Cubox y Neo.

AtuServicio

El proyecto hace uso de datos abiertos liberados por el Ministerio de Salud Pública del Uruguay, para poder comparar proveedores de salud. En febrero se “abría el corralito mutual” en Uruguay, lo que se traduce en “los usuarios pueden aprovechar para cambiarse de proveedor”. Por lo que el proyecto tenía que estar pronto para principio del mes.

Si no me equivoco la llamada de Lu fue el viernes 23 de enero, por lo que quedaba una semana de desarrollo. Este factor fue determinante y agregó mucha diversión y adrenalina al proyecto (los demás involucrados posiblemente no lo vean así). La idea era que faltaba escribir el frontend y necesitaban a alguien que se encargara de eso. Lu venía armando el backend (no solo modelado de la aplicación, también proceso y desarrollo de los datos liberados), y habían imágenes de cómo se quería mostrar el sitio. Por lo tanto mi misión, si decidía aceptarla, era trabajar durante esa semana en terminar el frontend. Trabajaría además con Fabrizio Scrollini y Daniel Carranza, viejos y queridos conocidos de DATA.

Qué es el proyecto

El sitio en cuestión es ATuServicio.uy. Procesa y modela de forma lógica los datos liberados por el M.S.P. y tiene una página inicial que lista todos los prestadores de salud del país, mostrando sus características por sede. Además, la característica principal es el comparador de prestadores. Podemos elegir hasta tres prestadores y comparar todos sus datos en columnas.

ATuServicio.uy

Desarrollo

El trabajo arrancó bastante tranquilo para mí. Lucía, Fabrizio y Daniel ya se habían encargado de las partes más tediosas y complejas: idas y vueltas con el Ministerio, procesado de datos, formatos de los datos, etc. Así que mi misión era simplemente ir integrando el diseño y maquetado a la aplicación Angular JS que Lu había desarrollado. A mitad de camino hubo algún cambio conceptual medio grande lo que llevó a que el miércoles antes de la entrega Lu tuviera que reescribir el backend en Rails. Para mí fue un cambio positivo por mi familiaridad con Rails y mi ignorancia de Angular (y un poquito de desprecio a JavaScript en general [ ni tanto { pero un poco sí } ] ).

En esto Lu tenía otras responsabilidades por las que tenía que abandonar el proyecto. Esto ya estaba pactado desde antes con DATA así que de golpe quedé con el proyecto a cargo. Esto llevó a unas jornadas de desarrollo bastante intensas.

El viernes antes de la entrega me fui a trabajar al Coworking UY, el renacido espacio de coworking que supe llamar “mi casa” hace un buen tiempo. Me acompañaron Daniel y Fabrizio y pasamos la primera de varias jornadas maratónicas trabajando en la aplicación. En Coworking nos recibieron con brazos abiertos como siempre, y creo que es posible que termine alquilando un lugar ahí eventualmente. De a poco se ha ido poblando con más gente y hay mucha buena onda.

Ese día -al día siguiente mas bien- nos fuimos a acostar a las 8 de la mañana. Y al mediodía estábamos trabajando de nuevo. Esto me hizo acordar a los viejos y queridos hackatones de Desarrollando América Latina (2011 y 2012), casualmente compartidos con la misma gente de DATA :)

Por el apuro de la entrega el lunes, hubieron momentos de mucho cansancio y soluciones poco ideales en el código, pero el resultado final lo ameritaba y se alcanzó el objetivo. Me di cuenta que trabajar bajo presión no solo te hace sacar soluciones que de otra forma seguro ni pensarías, sino que cuando la motivación está, lo hago hasta feliz. No es algo para hacer todo el tiempo, pero cada tanto me gusta volver a programar en modo hackatón.

Durante el tiempo que programé en atuservicio, trabajé en coworkings, casas de gente, cafeterías, terminales de ómnibus y más. La experiencia en general estuvo genial, y lo repetiría.

Prensa

Por la importancia del proyecto en el momento en que se lanzó y los actores involucrados (DATA, M.S.P. y AGESIC), tuvo una repercusión de prensa importante. Salió en televisión, radio y medios de prensa digitales y escritos de todo el país. Con Fabrizio y Daniel estuvimos en dos radios AM hablando sobre ATuServicio.

Era algo un poco loco ver que en tantos lugares hablaban de algo en lo que había estado involucrado. Esto también te expone a la peor escoria de la humanidad que habita Internet a través de las redes sociales y comentarios en sitios de noticias. Pero por suerte fueron los menos, y no hay nada mejor que ignorarlos. Recibimos algunas críticas constructivas y sugerencias que ya teníamos en la lista de cosas a hacer. Pero por lo rápido que salió el proyecto algunas no las habíamos implementado al momento de que saliera al público. Creo que el balance de recepción fue positivo.

La “gira” de prensa se cerró con una conferencia de prensa en la Torre Ejecutiva con representantes de DATA, el M.S.P. y AGESIC. De ahí nos fuimos a tomar una cerveza los integrantes de DATA que habían y celebramos el cierre de una exitosa primera etapa de ATuServicio. Hay más por venir…

Conclusión

Como programador trabajando para una empresa en general el fruto de mi trabajo ayuda a que un hombre blanco con mucha plata tenga más plata. Por eso siempre estoy con la dicotomía de que no quiero vivir para ser un engranaje más en el sistema, y pensando cómo puedo aportar mi granito de arena para hacer del mundo un lugar mejor. Porque si puedo hacerlo, ¿por qué no hacerlo? Hay que dejar las cosas en lo posible mejor que como las encontramos.

Difundir ciertos valores, ideales y proyectos, código, charlas, etc. ayudan. Pero en este caso se trató de un trabajo pago donde no solo estaba haciendo lo que me gusta (programar) sino también aportando un mínimo granito de arena para ayudar en algo (y con gente amiga y buena onda). La aplicación genera un poco de balance en el poder que tienen los proveedores de salud sobre sus usuarios. Da transparencia al tema, permitiendo directamente a la ciudadanía juzgar y medir valores reales que los proveedores entregan mediante declaración jurada al Ministerio. No sé si se haya mejorado la calidad de vida de la gente, pero por lo menos empujamos un poquito en la dirección correcta para ir mejorando el sistema.

No es la primera vez que hice algo con DATA, y seguramente no va a ser la última, pero fue un proyecto distinto :)

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

Juanjo Navarro

Google Code echa el cierre

March 12, 2015 08:07 PM

Google anuncia que en un plazo de 10 meses cerrará el servicio de alojamiento de proyectos open source Google Code.

Google code

Según indica el anuncio, con las alternativas existentes actualmente (Github, Bitbucket) el servicio había caído en desuso y cada vez más los proyectos que quedaban eran spam. La propia Google había movido algunos de sus proyectos a Github.

El calendario que se plantea es el siguiente:

  • 12 Marzo 2015 – Se deshabilita la creación de nuevos proyectos
  • 24 Agosto 2015 – Los proyectos se pondrán en modo solo lectura. No se podrán hacer commits a los proyectos, solo checkouts
  • 25 Enero 2016 – Sólo se podrá acceder a un archivo tar de los proyectos. Estos archivos estarán disponibles durante todo el 2016.

Otro servicio para la comunidad que Google cierra. Una pena. Seguro que muchos proyectos se perderán. Evidentemente no los más conocidos y mejor mantenidos, pero existen miles de pequeñas utilidades y librerías en Google code, algo desactualizadas pero todavía útiles. Muchas de ellas desaparecerán.

» 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