Noticias Weblogs Código

Jesús Perales

Primeros pasos con Spring #6, Base de datos por JNDI

February 13, 2016 12:24 AM

Primeros pasos con Spring #6, Base de datos por JNDI

Al articulo anterior de esta serie, dejamos una versión funcional de una aplicación de lista de pendientes, con una API REST que es consumida desde una interfaz web basada en Angular y Bootstrap.

Recordemos que la base de datos que utilizamos es H2 y advertimos era solo de prueba, en esta ocasión configuraremos la conexión a nuestra base de datos mediante JNDI y veremos que es muy sencillo cambiar de una base de datos a otra.

Debemos saber que configurar una base de datos por JNDI es dependiente de cada contenedor de aplicaciones(Tomcat, Wildfly, etc) y el motor de base de datos a utilizar.

El contenedor que usamos es Apache Tomcat y la base de datos MySQL, por lo que es necesario tenerlos instalados previamente.

Algo muy bueno en estos momentos es que podemos utilizar Docker para correr cualquier base de datos en cuestión de segundos, mas información aquí

Dividiremos esto en pasos:

Paso 1:

Agregar el controlador JDBC de MySQL, desde el repositorio de Maven a el pom.xml, podemos eliminar la referencia a la dependencia de H2.

Primeros pasos con Spring #6, Base de datos por JNDI

Paso 2:

Crear la carpeta META-INF dentro de la ruta src/main/webapp/ y dentro de ella un archivo llamado context.xml con el siguiente contenido.

<Context path="todo" docBase="todo"
    reloadable="true" crossContext="true">
    <Resource name="jdbc/Todo" auth="Container"
        type="javax.sql.DataSource" maxActive="100" maxIdle="30" maxWait="10000"
        username="root" password="root" driverClassName="com.mysql.jdbc.Driver"
        url="jdbc:mysql://localhost:3306/todo?createDatabaseIfNotExist=true" />
</Context>

Primeros pasos con Spring #6, Base de datos por JNDI

La funcionalidad de este archivo consiste en registrar un recurso por JNDI el cual nombramos jdbc/Todo y agregamos las credenciales ( username="root" password="root" ) así como la URL a nuestra base de datos MySQL (url="jdbc:mysql://localhost:3306/todo?createDatabaseIfNotExist=true") y el nombre de la clase del controlador JDBC (driverClassName="com.mysql.jdbc.Driver").

Paso 3:

Abriremos el archivo mvc-dispatcher-servlet.xml y modificaremos el bean dataSource por la siguiente linea de código.

<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/Todo"/>

Con esta linea especificamos que el dataSource esta dado por un recurso JNDI llamado jdbc/Todo, se antepone java:comp/env/ debido a la forma en que Apache Tomcat nombra los recursos registrados, mas información aquí.

Primeros pasos con Spring #6, Base de datos por JNDI

Paso 4:

Debemos cambiar el dialecto de Hibernate de H2 a MySQL, para eso en el archivo mvc-dispatcher-servlet.xml sustituiremos org.hibernate.dialect.H2Dialect por org.hibernate.dialect.MySQLDialect,

Primeros pasos con Spring #6, Base de datos por JNDI

De forma opcional podemos cambiar la propiedad que destruye y crea las tablas de Hibernate para que simplemente actualice o valide el esquema de nuestra base de datos, en mi caso utilice update, podemos ver que hace cada uno de estos valores aquí.

Conclusión:

Por ultimo comprobamos que nuestra base de datos se creo correctamente y que la información esta guardada en ella.

Primeros pasos con Spring #6, Base de datos por JNDI

Las modificaciones que se hicieron al código están aquí, el código fuente y las imágenes de el proyecto hasta el final de este articulo se encuentran aquí.

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

Arragonán

Alchups.com: Los aljibes de San Esteban

February 12, 2016 08:25 PM

En Octubre anunciábamos el lanzamiento de la web Alchups.com, y hace meses que tenía un borrador de post sobre ello que fue cayendo en el olvido, hasta hoy. Sin ser la tipología de proyecto que haga, ni a la que quiera dedicarme a hacer habitualmente, era algo que venía de lejos y es un proyecto que me hizo mucha ilusión que viera la luz.

Hace mucho que empezábamos a hablar con Javier Viudas sobre la necesidad de documentar y dar a conocer todos los alchups (o aljibes) que hay en nuestro pueblo en una web, y al final han tenido que pasar algunos años para que esta idea que teníamos en nuestras cabezas haya podido hacerse realidad. Tenía bocetos en papel hechos y re-hechos desde hacía la tira, y antes de verano pudimos plantearnos desarrollarlo por fin.

Como se explicó a algunos medios comarcales, pudimos ejecutarlo enmarcado dentro del proyecto de limpieza y señalización de las rutas de alchups, gracias a una ayuda de la Diputación Provincial de Huesca al Ayuntamiento de San Esteban de Litera.

Como es de suponer, en este proyecto me tocó hacer un poco de todo: conceptualizar, coordinar y desarrollar.

Para conceptualización estuvimos trabajando con José Luis Lizano. Al tener mucha libertad por parte del cliente, una visión clara del proyecto y una idea bastante aproximada de los contenidos con los que íbamos a poder trabajar; pudimos avanzar bastante rápido directamente sobre bocetos en papel.

Así que partir de estos bocetos él ya pudo hacer el trabajo de diseño visual.

Una foto publicada por Dani Latorre (@danilat) el

En paralelo con Razvan Puscas empezamos a programar, principalmente la parte de gestión de contenidos. El backend lo implementamos con Ruby on Rails ayudándonos de algunas de las gemas habituales como devise, rspec, factory_girl o paperclip. Mientras para maquetar el gestor de contenidos utilizamos Twitter Bootstrap y añadimos mapas de Google Maps.

Cuando ya tuvimos cerrados los primeros PSDs, entró a echarnos una mano para hacer la maquetación de la web pública Guillermo Latorre (antes de ser nombrado CEO :)). Sass, Bourbon, Restive…

Una foto publicada por Dani Latorre (@danilat) el

Con eso quedaba unir el trabajo de todos y finiquitar detalles para empezar con la carga y gestión de contenidos.

La mayor parte de la aplicación encajaba bien con la rails way y resultó muy rápido de implementar, pero además tuvimos que hacer algunas cosillas un poco raras. Para la carga inicial de datos tuvimos que importar ficheros CSV y transformar las coordenadas desde UTM a latitud/longitud. Mientras que para pintar las rutas y el término municipal en el mapa escribimos un parser de KML para luego pasarlo a JSON/JS y poder usarlo con Google Maps.

En cuanto al despliegue, lo tenemos hospedado en heroku y para le almacenamiento de imágenes utilizamos Amazon S3.

Al ser un proyecto financiado con fondos públicos es norma de la casa el hacerlo en abierto desde el principio. Así que el código está disponible para el que quiera utilizarlo o para quien quiera conocer nuestras vergüenzas en más detalle en un repositorio de github. La licencia elegida fue la Affero GPL.

Así que nada, si un día os pasáis cerca de San Esteban de Litera, os propongo ese plan :)

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

Jesús Perales

Bootstrap Grid System para novatos

February 12, 2016 07:24 PM

Bootstrap Grid System para novatos

Creo que a estas alturas en el desarrollo web todos escuchamos o usamos Bootstrap y aunque no sea el único framework de este tipo su uso se a extendido muy rápido.

Pero en mi opinión un punto muy importante que le a dado fuerza es la forma en que posiciona las cosas con su sistema de rejillas(Grid system), ya que ayuda a crear sitios web adaptables.

El sistema de rejilla de Bootstrap se basa principalmente en 3 conceptos que se agrupan en diferentes clases que definiremos como :

  • Contenedores
  • Filas
  • Columnas

Tratare de explicar cada uno en lo que sigue del articulo.

Contenedores (container, container-fluid):

Los contenedores en Bootstrap es el elemento base de su sistema de rejilla, es decir, es el elemento principal que tiene esta clase y dentro de el deberán ir las filas(row), Ejemplo:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title></title>
  </head>
  <body>
    <div class="container">

    </div>
  </body>
</html>

La clase puede ser container o container-fluid, la diferencia es que uno es un contenedor de tamaño definido y el otro es un contenedor de tamaño variable dependiendo del ancho del navegador.

Aquí podemos ver las diferencias a nivel código entre container y container-fluid, podemos encontrar mas información y diferencias de estas clases en este enlace.

Filas (row):

Es el segundo concepto en la jerarquía y su función es simple, definir la división de los elementos de forma horizontal, obviamente esta división esta dada por espacios no visibles y se utiliza de la siguiente manera:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title></title>
  </head>
  <body>
    <div class="container">
      <div class="row" >

      </div>
      <div class="row" >

      </div>
    </div>
  </body>
</html>

Podemos ver en el código que la clase row, inicialmente solo agregar margenes negativos a los lados y posteriormente utiliza la propiedad clear con el atributo both.

Estos margenes negativos se complementan directamente con el siguiente concepto y no debería preocuparnos sumar o restar pixeles.

Como bien su nombre lo dice, simplemente es una fila.

Columnas (col-xs-, col-sm-, col-md-, col-lg-).

El ultimo concepto básico del sistema de rejillas de Bootstrap, también puede ser el mas complejo, es el encargado de hacer la mayoría del trabajo para permitir interfaces web adaptables (Responsive Design para los que dicen HTML5, CSS3 y ES6 en su CV).

Su función es dividir el espacio de una fila (row) en un numero limitado de 12 columnas dependientes del tamaño del dispositivo.

Para lograr esto, la gente detrás de Bootstrap ideo un conjunto de clases muy simples con los siguientes prefijos para definir el comportamiento de ese elemento dependiendo del tamaño de la pantalla basándose en los media queries:

  • .col-xs-*: Pantallas de nomas de 768px.

  • .col-sm-*: Pantallas de 768px en adelante.

  • .col-md-*: Pantallas de 992px en adelante.

  • .col-lg-*: Pantallas de 1200px en adelante.

Y al final solo se agrega el numero de los 12 espacios que le corresponden en cada fila a un elemento, por ejemplo:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title></title>
  </head>
  <body>
    <div class="container">
      <div class="row">
        <div class="col-xs-12 col-sm-3">

        </div>
      </div>
    </div>
  </body>
</html>

El ejemplo anterior se traduce en que el elemento al estar en un dispositivo con una pantalla extra pequeña ocupara las 12 columnas que se encuentran disponibles por fila y si se encuentra en una pantalla pequeña solo ocupara 3 posiciones de las 12 columnas que le corresponden.

También tenemos 2 tipos de columnas mas, las de compensación (.col-md-offset-*) y de ordenamiento (.col-md-push-* y .col-md-pull-*).

Las columnas de compensación tienen su principal utilidad en crear espacios en blanco entre los elementos sin la necesidad de agregar un elemento vació, ¿como lo hacen? , simplemente estas clases aumentan el margen izquierdo de una columna por ejemplo .col-md-offset-4 mueve .col-md-4 cuatro columnas.:

<div class="row">
  <div class="col-md-4">.col-md-4</div>
  <div class="col-md-4 col-md-offset-4">.col-md-4 .col-md-offset-4</div>
</div>
<div class="row">
  <div class="col-md-3 col-md-offset-3">.col-md-3 .col-md-offset-3</div>
  <div class="col-md-3 col-md-offset-3">.col-md-3 .col-md-offset-3</div>
</div>
<div class="row">
  <div class="col-md-6 col-md-offset-3">.col-md-6 .col-md-offset-3</div>
</div>

Bootstrap Grid System para novatos

Las columnas de ordenamiento sirven para cambiar el orden por defecto de las columnas en el Grid System, veamos el siguiente código de ejemplo:

<div class="row">
  <div class="col-md-9 col-md-push-3">.col-md-9 .col-md-push-3</div>
  <div class="col-md-3 col-md-pull-9">.col-md-3 .col-md-pull-9</div>
</div>

Bootstrap Grid System para novatos

Como vemos en el ejemplo el primer div toma la segunda posición y el segundo la primera, respetando siempre el limite de 12 espacios.

Estas clases las podemos leer de la siguiente forma, la primera columna (.col-*-*-*) en una pantalla mediana (.col-md-*-*) empujara (.col-md-push-*) en tres posiciones (.col-md-push-3) al elemento y la segunda columna (.col-*-*-*) en una pantalla mediana (.col-md-*-*) jalara (.col-md-pull-*) en 9 posiciones (.col-md-pull-9) al elemento, esto hace que cambien de posición.

Conclusión

Como vemos Bootstrap deja muy sencillo el trabajar con diferentes tamaños de pantallas y lo podemos ver en acción aquí, para activar la vista de diseño adaptable en Firefox simplemente presionamos ctrl + shift + m, siempre podemos ver la documentación en la pagina oficial de Bootstrap.

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

PHP Senior

GIT: colorear el diff en consola

February 12, 2016 06:39 PM


 

Estás trabajando remoto desde la consola de comandos, quieres ver la diferencia de tus fuentes con respecto a los cambios que estás haciendo, y los cambios se presentan en blanco y negro.

$ git diff

Para poder ver el diff con colores y clarificar mejor las diferencias en el código modificado, debes cambiar la configuración de GIT:

La preferencia es

color.ui


Y todas las funciones se pueden habilitar o deshabilitar con TRUE o FALSE

ejemplo:

$ git config --global color.ui true

Aquí dejamos habilitado para que el diff al ejecutar git diff se muestre en distintos colores para facilitar su lectura.

Existen muchas variaciones que se pueden aplicar, para más info, ver manual de referencia.

color.branch
color.diff
color.interactive
color.status

Saludos!


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

PHP Senior

Netbeans: cómo agregar soporte para editar bash / scripts

February 12, 2016 06:31 PM




Probado en Netbeans 8 en adelante:
  1. Instalar plugin C/C++
  2. Instalar plugin nb-noext-mime-resolver
  3. Cambiar configuración en tools/options/editor/spellchecker y desmarcar 'Script and make comments'

Fuente: stackoverflow.com

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

Blog Bitix

Cómo recuperar archivos eliminados o de una unidad corrupta en GNU/Linux

February 12, 2016 06:30 PM

Muchos archivos y su contenido son recuperables aún después de eliminados y liberado su espacio ocupado del sistema de archivos. Herramientas como Foremost son capaces de recuperar el contenido de un archivo si no ha sido sobreescrito accediendo a bajo nivel a los datos de la unidad, ya esté corrupta y de algún error al montarla o funcione perfectamente. Pudiendo extraer una imagen de la unidad seremos capaces de recuperar gran cantidad de archivos motivo por el cual al deshacernos de una unidad de almacenamiento es recomendable hacer un borrado seguro para evitar que información personal o confidencial sea obtenida con cualquier propósito.

GNU
Linux

Hace un tiempo me ocurrió que una memoria USB por algún motivo no se montaba bien ni en un sistema Windows ni tampoco en un sistema Linux. El pendrive estaba formateado con el sistema de archivos NTFS y quizá por haberlo desmontado mal se corrompió.

Por suerte en GNU/Linux disponemos de cantidad de herramientas y programas que nos ayudan en prácticamente cualquier cosa. En este caso de una memoria que no se dejaba montar buscando algún enlace en la wiki de Arch Linux relacionado con la recuperación de datos encontré en poco tiempo el artículo sobre Foremost, siendo un programa que puede ayudarnos a recuperar al menos parte de los datos que tuviésemos guardados. Si la unidad no está dañada físicamente y podemos extraer una imagen de la misma Foremost puede acceder a bajo nivel a las estructuras de datos de la imagen de la unidad. Foremost puede trabajar directamente sobre la unidad dañada o con una imagen, esto último es lo recomendable para evitar corromper los datos y dañar más gravemente la unidad al usarla. Si podemos extraer una imagen de la unidad es buen síntoma de que la unidad no ha dejado de funcionar completamente y tal vez se trate solo de un error lógico en la unidad y no físico pudiendo tal vez recuperarla con un formateo.

El error en concreto al montarlo en mi sistema Arch Linux era el siguiente:

<noscript><pre><a href="https://gist.githubusercontent.com/picodotdev/d8197f30979b1d2fb75e/raw/error.log">error.log</pre></a></noscript>

Podemos extraer una imagen de una unidad con el comando dd, el parámetro if será la unidad de entrada y el parámetro of la imagen que se creará en un archivo:

<noscript><pre><a href="https://gist.githubusercontent.com/picodotdev/d8197f30979b1d2fb75e/raw/dd.sh">dd.sh</pre></a></noscript>

Una vez que disponemos de la imagen en un archivo en un sistema libre de fallos usaremos Foremost para que intente recuperar los archivos que no han sido completamente corrompidos por el error. Indicamos la imagen del archivo extraída con el parámetro -i y la carpeta donde dejará los archivos recuperados con el parámetro -o agrupados en directorios por tipo, con el parámetro -t indicamos los tipos de archivos que queremos recuperar de entre todos los que pueda (doc, docx, pdf, jpg, png, txt, …).

<noscript><pre><a href="https://gist.githubusercontent.com/picodotdev/d8197f30979b1d2fb75e/raw/foremost.sh">foremost.sh</pre></a></noscript>

Si tenemos suerte con Foremost recuperaremos gran parte de ellos y evitaremos perderlos para siempre. En la wiki de Arch Linux hay una guía con consejos y explicaciones de como proceder en la recuperación de archivos.

Una vez que recuperé los archivos y viendo que pude extraer una imagen de la unidad probablemente en mi caso el error sería que se corrompió la unidad quizá por no extraerla de forma segura desde Windows. Por lo tanto la volví a formatear e hice algunas pruebas copiando varios archivos, se copiaron sin dar ningún error así que al final conseguí recuperar incluso la unidad.

Esto mismo es aplicable a una unidad que funcione correctamente, un archivo y su contenido es recuperable aún después de ser eliminado incluido de la papelera, con más probabilidad si la unidad no tiene muchas escrituras o una buena cantidad de espacio libre ya que por defecto únicamente se elimina de las estructuras del sistema de archivos y el contenido sigue estando presente simplemente no referenciado.

Para hacer irrecuperable el contenido de un archivo hay que hacer un borrado seguro de un archivo con el comando wipe o de una unidad con dd que consiste en sobreescribir el contenido del archivo con datos aleatorios, otra alternativa es cifrar el sistema de archivos. Es recomendable hacerlo cuando sustituyamos y nos deshagamos un disco duro o unidad de almacenamiento porque muchos de los archivos son recuperables incluido después de un formateo rápido de la unidad.

Te sorprenderá la cantidad de archivos que son recuperados por Foremost: documentos, imágenes, archivos de texto, …

Referencia:
Foremost
File recovery
Wipe
Securely Wipe Disk
Disk encryption

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

Poesía Binaria

Conocer el tipo MIME de un archivo gracias a GIO en lenguaje C

February 12, 2016 09:41 AM

3210986710_d3cbbdac55_b

La biblioteca GIO nos proporciona una capa más sobre el manejo de archivos. Nos aíslan un poco de las llamadas al sistema operativo para hacer muchas operaciones sobre archivos facilitando el uso de la biblioteca en aplicaciones multiplataforma. Así como dándonos algunas funciones interesantes que ya vienen hechas.

La gran ventaja de GIO frente al acceso normal a un archivo es que hace transparente al programador el acceso a sistemas de archivos compartidos (en Windows, Mac o Linux) sin tener que gastar mucho tiempo en todo eso.

Saber el tipo MIME de un archivo

Si tu aplicación trabaja con archivos, en muchas ocasiones es necesario conocer el tipo MIME, sobre todo cuando vamos a transferir datos a través de la red. Nos ayudan a conocer el tipo de contenido de un archivo (saber si es una imagen, un xml, un archivo comprimido, etc), mucho más allá de la extensión del mismo. Es más, podemos utilizar esta técnica para saber de verdad qué contiene un archivo porque, en muchas ocasiones, no podemos confiar en que el usuario final haya puesto bien la extensión.

El uso más común que se me ocurre para esto es cuando se sube una foto de perfil a una web. Muchos usuarios suben un jpg, otros un png, algunos un gif, bueno, hasta ahora bien, pero encontramos un primer problema, las mayúsculas, lo podemos salvar, lo pasamos todo a minúscula y comparamos, ahora otro problema, algunos jpg, podrán tener la extensión jpeg, aunque también lo podemos salvar. El problema complicado viene cuando un usuario tiene un png y lo renombra a jpg, o incluso tiene un bmp que lo renombra a gif, y claro, el renombrar los archivos no implica que se conviertan los datos que tiene dentro. Deberíamos tener una forma de poder analizar el archivo por dentro y en función de eso determinar de qué tipo es.

Tipo MIME asociado a partir del nombre de archivo

En principio, vamos a intentarlo por el nombre de archivo. Normalmente, para esto, necesitaríamos una base de datos con asociaciones entre nombre de archivo y tipo MIME, pero GIO ya hace eso por nosotros, podemos hacerlo de la siguiente forma:

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
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <gio/gio.h>

int main (int argc, char *argv[])
{
  if (argc<1)
    {
      /* Caso de error */
      fprintf (stderr, "No se ha especificado el archivo\n");
      fprintf (stderr, " Uso : %s nombre_de_archivo\n", argv[0]);
      exit(1);
    }

  const char *file_name = argv[1];
  gboolean result_uncertain = TRUE;

  char *content_type = g_content_type_guess (file_name, NULL, 0, &result_uncertain);

  if (content_type != NULL)
    {
      char *mime_type = g_content_type_get_mime_type (content_type);
      if (mime_type)
    printf ("MIME Type: '%s'\n", mime_type);
      else
    printf ("MIME Type no disponible\n");

      printf ("Content Type: '%s' (en Linux coincide con el MIME Type)\n", content_type);

      if (mime_type)
    g_free (mime_type);
    }
  else
    fprintf (stderr, "No se pudo obtener el tipo de contenido\n");
  g_free (content_type);

  return EXIT_SUCCESS;
}

Para compilar:

$ gcc -o mime mime.c $(pkg-config –libs –cflags gio-2.0)

La clave está en la función g_content_type_guess() a la que le pasamos:

  1. El nombre de archivo (o NULL si no queremos basarnos en el nombre)
  2. Cotenido del archivo (o NULL si no queremos analizar el contenido)
  3. Bytes del contenido (o 0 si no queremos analizar el contenido)
  4. Un boolean por referencia donde dirá si el resultado es incierto o no. Es decir, cómo de seguros podemos estar con el dato. Por ejemplo, un archivo sin extensión, a priori no podemos saber qué es, por lo que nos devolverá un tipo genérico y nos dirá que result_uncertain es TRUE.

La función nos devolverá una cadena de caracteres con el tipo de contenido del archivo. Este tipo de contenido variará entre diferentes sistemas operativos, ya que cada uno llama a los archivos de una forma distinta, por ejemplo en Linux este content_type será igual que el tipo MIME (no sé si habrá alguna excepción), en Windows se usan nombres de aplicación o extensiones.

El caso es que, si content_type ha devuelto una cadena (vamos, que no es NULL), preguntamos el tipo MIME con g_content_type_get_mime_type() que nos devolverá directamente el valor que queremos.

Analizando el contenido del archivo

Vamos a completar un poco más el ejemplo anterior. Por un lado, vamos a devolver más datos acerca del tipo de archivo. Por ejemplo, una descripción, decir si el archivo es ejecutable o no, comprobar si el archivo existe (tal y como está el ejemplo anterior, como sólo mira el nombre del archivo, si éste no existe, no pasa nada, nos devuelve un tipo.

Para analizar el contenido, lo que vamos a hacer es leer el archivo, almacenarlo en una variable, y pasárselo a g_content_type_guess(). En el ejemplo he utilizado g_file_load_contents() para cargar los contenidos del archivo. Aunque si prefieres ser más clásico puedes hacer:

1
2
3
4
5
6
7
8
9
10
11
int readData(const char* file_name, char* buffer, unsigned bufferSize)
{
  FILE* fd = fopen(file_name, "r");
  if (!fd)
    return -1;

  int nread = fread(buffer, 1, bufferSize, fd);
  fclose(fd);

  return nread;
}

De esta forma, podemos ver que no es necesario leer el archivo completo para averiguar el MIME type, es más, si visualizáis el contenido bruto de un archivo jpeg, png, un ejecutable, un vídeo, etc, veréis cómo se identifican fácilmente con los primeros bytes (para una parte legible que tienen…)

Allá va el ejemplo completo:

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
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <gio/gio.h>

int main (int argc, char *argv[])
{
  if (argc<1)
    {
      /* Caso de error */
      fprintf (stderr, "No se ha especificado el archivo\n");
      fprintf (stderr, " Uso : %s nombre_de_archivo\n", argv[0]);
      exit(1);
    }

  const char *file_name = argv[1];
  gboolean result_uncertain = TRUE;
  char* buffer;
  gsize bufferSize;
  if (!g_file_load_contents(g_file_new_for_path(file_name), NULL, &buffer, &bufferSize, NULL, NULL))
    {
      fprintf(stderr, "Hubo un problema leyendo el archivo.\n");
      exit(2);
    }

  char *content_type = g_content_type_guess (NULL, buffer, bufferSize, &result_uncertain);
  g_free(buffer);

  if (content_type != NULL)
    {
      char *mime_type = g_content_type_get_mime_type (content_type);
      char *description = g_content_type_get_description(content_type);
      printf ("Archivo: '%s'\n", file_name);

      if (g_content_type_is_unknown(content_type))
    fprintf (stderr, "El tipo de archivo es desconocido\n");
      else
    {
      if (mime_type)
        printf ("MIME Type: '%s'\n", mime_type);
      else
        printf ("MIME Type no disponible\n");

      printf ("Content Type: '%s' (en Linux coincide con el MIME Type)\n", content_type);

      if (description)
        printf ("Descripcion: %s\n", description);
      else
        printf ("Descripcion no disponible\n");

      printf ("¿Resultado certero? %s\n", (result_uncertain)?"no":"si");

      printf ("¿Se puede ejecutar? %s\n", (g_content_type_can_be_executable(content_type))?"si":"no");
    }
      if (description)
    g_free (description);
      if (mime_type)
    g_free (mime_type);
    }
  else
    fprintf (stderr, "No se pudo obtener el tipo de contenido\n");
  g_free (content_type);

  return EXIT_SUCCESS;
}

Vemos, en la llamada a g_content_type_guess() que he eliminado el nombre del archivo, no es que no podamos ponerlo, pero quiero darle preferencia al contenido del mismo ya que, en el nombre del archivo, si el tipo es evidente dada su extensión, no analizará el contenido.

Podemos ver este pequeño ejemplo para que veáis que el programa no se deja timar:
Screenshot 12-02-2016-020216

Una cosa más

Estamos con una biblioteca parte de glib, por lo que siempre que reservemos algo, debemos utilizar g_free() para liberar. Y tenemos funciones que empiezan por “g_” o tipos como gboolean o gsize.
¡ Y otra más ! Podemos encontrar GIO disponible para otros lenguajes como C++, Python, Ruby, Haskell y más.

Foto: Jan Lewandowski

The post Conocer el tipo MIME de un archivo gracias a GIO en lenguaje C appeared first on Poesía Binaria.

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

Jesús Perales

Primeros pasos con Spring #5, interfaz web

February 11, 2016 05:57 PM

Primeros pasos con Spring #5, interfaz web

Siguiendo el proyecto donde lo dejamos, vemos como todo va tomando forma y lo único que nos queda para terminar nuestra aplicación es una interfaz de usuario.

Nuestra API REST, puede ser consumida por una aplicación para Android , IOS, aplicación web, etc.

En nuestro caso la aplicación web que usaremos esta basada en Bootstrap y Angular, es un fork de un ejemplo del MEAN Stack(Mongodb, Express, Angular y Node), nosotros sustituimos toda la parte del backend por Java, esta aplicación web fue desarrollada por Carlos Azaustre.

Primero que nada tenemos que ir a descargar el código fuente de la aplicación que se encuentra aquí, descomprimimos el archivo ZIP y buscamos la carpeta public que contendrá dos archivos index.html y main.js.

Primeros pasos con Spring #5, interfaz web

Copiamos estos dos archivos a la carpeta webapp de nuestra aplicación.

Primeros pasos con Spring #5, interfaz web

Nos aparecerá una alerta y seleccionaremos Copy files y presionaremos el botón OK.

Primeros pasos con Spring #5, interfaz web

Una vez que tengamos los archivos en nuestro workspace, abriremos el archivo main.js y dentro de la función mainController declararemos una variable llamada apiUrl que contendrá la siguiente cadena "/todo/api/todos/".

Primeros pasos con Spring #5, interfaz web

Ahora sera necesario reemplazar todas las cadenas '/api/todos' por nuestra variable apiUrl, estas se encuentran en las lineas 10, 21 y 34.

Primeros pasos con Spring #5, interfaz web

En la linea 24 observamos que al ejecutarse la petición por post a nuestra API correctamente la información que nos devuelve reemplaza la lista de Todos almacenada en la variable $scope, pero nuestra API solo devuelve el Todo agregado, a diferencia de la API original, así que es necesario cambiar $scope.todos = data; por $scope.todos.push(data);, la documentación del método push la podemos encontrar aquí.

Primeros pasos con Spring #5, interfaz web

Para terminar de adaptar el script a nuestra API modificaremos la función anónima contenida en la variable $scope.deleteTodo.

Recordemos que nuestra API al borrar un elemento retorna el elemento eliminado, entonces debemos actualizar la lista de Todos que tenemos en la variable $scope.todos quitando el Todo devuelto al ejecutarse correctamente la petición.

Para buscar un elemento dentro de un arreglo en Javascript utilizaremos una función auxiliar llamada findAndRemove, puedes copiarla y pegarla de aquí.

function findAndRemove(array, property, value) {
      array.forEach(function(result, index) {
        if(result[property] === value) {
          array.splice(index, 1);
        }    
      });
}

Primeros pasos con Spring #5, interfaz web

Esta función recibe como parámetros, un arreglo, el nombre de la propiedad del elemento a buscar y el elemento a buscar en el arreglo, así que la utilizaremos para borrar de la vista el elemento eliminado en la base de datos, reemplazando la linea 36 $scope.todos = data; por findAndRemove($scope.todos, 'id', data.id);.

Primeros pasos con Spring #5, interfaz web

Como podemos ver pasamos el arreglo de Todos, el nombre de la propiedad a buscar en el arreglo y el Todo eliminado.

También debemos abrir el archivo index.html y cambiar la linea 21 ng-click="deleteTodo(todo._id)"> por ng-click="deleteTodo(todo.id)">.

Primeros pasos con Spring #5, interfaz web

Después tenemos que cambiar la linea 22 {{ todo.text }} por {{ todo.name }}.

Primeros pasos con Spring #5, interfaz web

Y por ultimo la linea 35 ng-model="formData.text"> por ng-model="formData.name">

Con esto solo toca levantar nuestro servidor y visitar la url http://localhost:8080/todo/ y probar nuestra aplicación.

Primeros pasos con Spring #5, interfaz web

Podemos observar el log de nuestro servidor que esta ejecutando las consultas necesarias.

Primeros pasos con Spring #5, interfaz web

En este punto nuestra aplicación ya es funcional y es posible verla en ejecución, vimos como adaptar una interfaz web ya hecha reutilizando el código que encontramos en Github y así no partimos desde cero.

Quizá no se explico que es Angular ni sus principios para no desviarnos del tema (Spring) pero es un framework con el que estoy trabajando y me gustaría dedicarle algunos artículos posteriormente, pueden revisar que hace el código en el post original aquí y aprender un poco sobre Angular.

Parce que nuestra aplicación ya está terminada, pero aun falta cambiar la base de datos a una real, aplicar un cache, integrar validaciones JSR 303 y un largo etc, podemos decir que ya es funcional, pero no esta terminada.

El código hasta el final de este articulo lo podemos descargar aquí.

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

Poesía Binaria

Cómo extraer duración, fotogramas, bitrate y fps de un vídeo para nuestros scripts

February 10, 2016 09:15 AM

1200px-Film_strip

Seguramente en nuestros scripts, si tratamos con archivos de vídeo, nos interese conocer información sobre el mismo. Tal vez podamos extraer más información de un archivo de vídeo con un software especializado como VLC; pero para hacer unos cálculos rápidos o una conversión de formato nos va a venir muy bien.

Programas como avconv o ffmpeg son capaces de extraer información básica del vídeo. Es más, programas como identify (de imagemagick) entre otros, en realidad llaman por detrás a uno de los dos primeros para realizar la identificación del archivo.

Pero claro, imaginemos que queremos automatizar algunos procesos y hay archivos de vídeo involucrados. ¿Cómo extraemos la información de los mismos? Con herramientas como sed o awk. Al final del post tendréis el código fuente completo del script.

¿avconv o ffmpeg?

avconv es un fork de ffmpeg. Por eso, tienen algunas cosas en común. El caso es que algunas distribuciones incluyen ffmpeg y otras avconv. Por lo que lo primero que tenemos que tener claro es qué programa tenemos nosotros instalado.
Ambos programas sólo tienen que especificar el archivo de entrada para tener como salida información del archivo de vídeo. En este caso el argumento -i nos devuelve algo como:

avconv version 9.18-6:9.18-0ubuntu0.14.04.1, Copyright (c) 2000-2014 the Libav developers
built on Mar 16 2015 13:19:10 with gcc 4.8 (Ubuntu 4.8.2-19ubuntu1)
Guessed Channel Layout for Input Stream #0.1 : stereo
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from ‘/tmp/video.mp4′:
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2avc1mp41
encoder : Lavf54.20.4
Duration: 00:26:25.36, start: 0.000000, bitrate: 1105 kb/s
Stream #0.0(und): Video: h264 (High), yuv420p, 720×576 [PAR 16:15 DAR 4:3], 974 kb/s, 25 fps, 25 tbr, 25 tbn, 50 tbc
Stream #0.1(und): Audio: mp3, 48000 Hz, stereo, s16p, 128 kb/s
At least one output file must be specified

Nos da más información de la que vamos a extraer, por lo que podríamos sacar más cosas en claro, por otro lado, no nos enseña los frames totales del vídeo, y eso puede resultarnos de ayuda ya que esta herramienta expresa el progreso en número de frames procesados, por lo que si sabemos cuántos frames hay en total, podemos saber qué porcentaje de progreso llevamos. Pero lo veremos más adelante.

Desgranando el script

Para eso he utilizado una función que busca los dos ejecutables en el sistema, y coge el que exista.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function checkSoftware()
{
    SW="`which avconv; which ffmpeg`"

    if [ -n "`echo $SW | grep avconv`" ]
    then
    CONVERTSOFTWARE="avconv"
    elif [ -n "`echo $SW | grep ffmpeg`" ]
    then
    CONVERTSOFTWARE="ffmpeg"
    else
    echo "No hay ningún conversor disponible" >&2
        exit 1
    fi
}

Con esto, sólo tendremos que llamar a checkSoftware y a partir de ahí se establecerá la variable CONVERTSOFTWARE con el nombre de nuestro ejecutable.

Tras esto, sólo tendremos que llamar a:

1
$CONVERTSOFTWARE -i "$ARCHIVO"

Esto, yo prefiero meterlo en una función que he llamado getVideoInfo(), por si algún día decido cambiar algo o introducir algo más:

1
2
3
4
function getVideoInfo()
{
    $CONVERTSOFTWARE -i "$1" 2>&1
}

Al final, como avconv devuelve los mensajes por la salida de error, redirijo ésta a la salida estándar (2>&1) para no tener problemas.

Obtención de datos

Los datos los obtendremos a partir de la salida de avconv (o ffmpeg), filtrando las zonas de texto. Puede que haya archivos que por su naturaleza no nos devuelvan bien la información (porque tengan varios streams, o porque no tengan los streams bien formados, o no tengan stream de vídeo…), aunque por lo general sí que funcionará bien:

1
2
3
4
5
6
7
8
9
10
11
12
13
function getAllData()
{
    FILE="$1"
    if [ ! -r "$FILE" ]
    then
    panic "File $FILE not found"
    fi
    FILEINFO="`getVideoInfo "$FILE"`"
    DURATION=$(echo "$FILEINFO" | sed -n "s/.* Duration: \([^,]*\), start: .*/\1/p")
    BITRATE=$(echo "$FILEINFO" | sed -n "s/.* bitrate: \([^,]*\) kb\/s/\1/p")
    FPS=$(echo "$FILEINFO" | sed -n "s/.*, \(.*\) fps.*/\1/p")
    FRAMES=$(echo $DURATION | awk -F':' "{ FRAMES=(\$1*3600+\$2*60+\$3)*$FPS; print FRAMES }")
}

Esto creará algunas variables:

  • FILEINFO: Contendrá la información en bruto devuelta por avconv
  • DURATION: Busco la palabra Duration y start y extraigo el valor que hay entre ellos, que es la duración en horas:minutos:segundos (segundos puede tener decimales)
  • BITRATE: Busco la palabra bitrate y kb/s y extraigo el número entre ellos, así consigo los kilobits por segundo.
  • FPS: Los fotogramas por segundo son un número seguido de la palabra fps
  • FRAMES: Los fotogramas, ya que no vienen en esta salida los calculo extrayendo el número total de segundos de duración y multiplicándolos por los FPS. Es decir: (horas*3600 + minutos*60 + segundos)*fps. En este caso, awk tomará como separador ‘:’ y cogerá el elemento $1, como hora; $2, como minutos; $3, como segundos. La barra delante del $ es un escapado, ya que hemos de diferenciar entre una variable de awk (\$1) y una variable de bash ($FPS).

El script completo

Aquí lo tenéis para copiar y pegar:

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
#!/bin/bash
#
# Author: Gaspar Fernandez (blakeyed@totaki.com)
# Extracted from http://totaki.com/poesiabinaria
#
# Do whatever you want with this code.

function panic()
{
    echo "$1" 1>&2
    exit 1
}

function checkSoftware()
{
    SW="`which avconv; which ffmpeg`"

    if [ -n "`echo $SW | grep avconv`" ]
    then
    CONVERTSOFTWARE="avconv"
    elif [ -n "`echo $SW | grep ffmpeg`" ]
    then
    CONVERTSOFTWARE="ffmpeg"
    else
    panic "Failed to search conversion software in remote server"
    fi
}

function getVideoInfo()
{
    $CONVERTSOFTWARE -i "$1" 2>&1
}

function getAllData()
{
    FILE="$1"
    if [ ! -r "$FILE" ]
    then
    panic "File $FILE not found"
    fi
    FILEINFO="`getVideoInfo "$FILE"`"
    DURATION=$(echo "$FILEINFO" | sed -n "s/.* Duration: \([^,]*\), start: .*/\1/p")
    BITRATE=$(echo "$FILEINFO" | sed -n "s/.* bitrate: \([^,]*\) kb\/s/\1/p")
    FPS=$(echo "$FILEINFO" | sed -n "s/.*, \(.*\) fps.*/\1/p")
    FRAMES=$(echo $DURATION | awk -F':' "{ FRAMES=(\$1*3600+\$2*60+\$3)*$FPS; print FRAMES }")
}

checkSoftware

if [ -z "$1" ]
then
    panic "No input file"
fi

if [ -z "$2" ]
then
    FILE="$1"
    DATA="all"
else
    FILE="$2"
    DATA="$1"
fi

case "$DATA" in
    "all")
    getAllData "$FILE"
    echo "Duration: $DURATION"
    echo "FPS: $FPS"
    echo "Bitrate: $BITRATE kb/s"
    echo "Total frames: $FRAMES"
    ;;
    "duration")
    getAllData "$FILE"
    echo "$DURATION"
    ;;
    "fps")
    getAllData "$FILE"
    echo "$FPS"
    ;;
    "bitrate")
    getAllData "$FILE"
    echo "$BITRATE"
    ;;
    "frames")
    getAllData "$FILE"
    echo "$FRAMES"
    ;;
    *)
    panic "Element to extract not recognized: $DATA"
esac

El ejecutable tiene algunas opciones, si lo llamamos vinfo y ejecutamos:

$ vinfo mivideo.avi

Nos devolverá información general sobre el vídeo.

Duration: 00:26:25.36
FPS: 25
Bitrate: 1105 kb/s
Total frames: 39634

En cambio si especificamos una opción de las posibles (duration, fps, bitrate, frames) sólo devolverá el contenido del dato, sin ningún texto introductorio, para que podamos usar ese dato directamente en scripts.

$ vinfo frames mivideo.avi
39634

Foto: “Film strip” by Bart from New Orleans, Louisiana, USA – Strip. Licensed under CC BY 2.0 via Commons

The post Cómo extraer duración, fotogramas, bitrate y fps de un vídeo para nuestros scripts appeared first on Poesía Binaria.

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

Variable not found

Enlaces interesantes 227

February 09, 2016 03:34 PM

Enlaces interesantesAhí van los enlaces recopilados durante la semana pasada. Espero que os resulten interesantes. :-)

.Net

ASP.NET

ASP.NET Core

Azure / Cloud

Conceptos/Patrones/Buenas prácticas

Data

Html/Css/Javascript

Visual Studio/Complementos/Herramientas


Publicado en Variable not found

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

Picando Código

Meld – Herramienta visual para diffs y merges

February 09, 2016 02:30 PM

MeldMeld es una aplicación para comparar archivos y directorios y mergearlos (“¿unirlos?” a falta de mejor palabra). Es parte del proyecto GNOME y sus aplicaciones centrales. En mis épocas de usuario de KDE usé una herramienta similar: Kompare.

Hace tiempo que no usaba herramientas de este tipo, generalmente con los comandos de Git resuelvo todos los problemas de diff y merge. Pero hay casos en los que tenemos que trabajar con archivos fuera del repositorio, o fuera del control de versiones, y éstas herramientas vienen a mano. Recientemente me vino muy útil a la hora de procesar datos abiertos en CSV 😀

Meld nos permite comparar hasta 3 archivos a la vez, mostrando los cambios y permitiéndonos mandar los cambios de un archivo a otro. También tiene la posibilidad de comparar directorios, pero la verdad no he usado esta característica todavía. También soporta sistemas de control de versiones incluyendo Git, Mercurial, Bazaar y SVN y comandos básicos como commit/update/agregar/eliminar archivos.

Meld

Una característica que necesité en su momento era ignorar los cambios en espacios. Tener cientos de cambios y que sean todos de indentación o saltos de línea distintos es poco cómodo, así que buscaba cómo ver algo como git diff -w.

En la ayuda del sitio oficial hablaba de un menú de Preferencias para configurar lo de los espacios, entre otras cosas. Estuve buscándolo rato largo hasta sentirme muy idiota :mrgreen: . El tema es que en las directrices actuales de GNOME, algunos menúes (Ayuda, Salir, etc) se movieron al menú de aplicaciones, y ahí pude activar lo de ignorar espacios en blanco desde los filtros de texto:

Meld preferences
Meld está disponible en la mayoría de las distribuciones GNU/Linux, por lo que podemos instalarlo desde nuestro gestor de paquetes. También hay versiones disponibles para Windows y OS X. Obviamente es software libre, con licencia GPL v2.

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

Variable not found

La directiva @helper no existe en MVC Core 1.0

February 09, 2016 08:19 AM

ASP.NET CorePues a estas alturas, no son pocos los posts que hemos ido publicado por aquí sobre características que cambian o desaparecen en el salto a las nuevas tecnologías "core", pero no se vayan todavía, aún hay más ;)

En esta ocasión, comentaremos muy brevemente una gran ausencia en las vistas Razor: la directiva @helper, que no estará disponible en la primera versión de ASP.NET Core MVC.

<Disclaimer>Aunque en una Release Candidate las cosas no deberían cambiar, ya han demostrado bastantes veces que sí que cambian, por lo que algunas de las cosas que digamos aquí podrían no ser ciertas para la versión final del producto</Disclaimer>

Esto no es un tema reciente: hace ya bastantes meses, la implementación que había era bastante incompleta y no encajaba bien en la nueva arquitectura, por lo que tras algunos debates y reuniones de diseño, se decidió que lo mejor era eliminar esta directiva.

Personalmente me parece una gran ausencia, porque las usaba bastante para evitar porciones de código repetido en el interior de mis vistas, y también para facilitar el mantenimiento o legibilidad cuando éstas eran relativamente complejas, pero bueno, es lo que hay.

Pero este nuevo escenario, aún tenemos posibilidades al alcance para conseguir un resultado parecido al que lográbamos con la directiva @helper , como:
  • Usar la directiva @functions para crear funciones helper en las vistas, quizás combinado con interpolación de cadenas de C#  y heredoc en caso de haber mucho HTML:
    @Input("Name", "name", "José")

    @functions {

    HtmlString Input(string label, string field, string value)
    {
    var result = $@"
    <div class='input-group'>
    <label for='{field}'>{label}</label>
    <input type='text' name='{field}' id='{field}'
    value='{value}'>
    </div>
    ";
    return new HtmlString(result);
    }
    }
  • Por supuesto, si el helper lo vamos a utilizar más allá de una única vista, ese mismo código podríamos desplazarlo a una clase estática y crearlo como extensor de IHtmlHelper para poder usarlo en la forma @Html.MyHelper()  desde las vistas. 
    public static class MyHtmlExtensions
    {
    public static HtmlString Input(
    this IHtmlHelper helper, string label,
    string field, string value)
    {
    var result = $@"
    <div class='input-group'>
    <label for='{field}'>{label}</label>
    <input type='text' name='{field}' id='{field}'
    value='{value}'>
    </div>
    ";
    return new HtmlString(result);
    }
    }
  • Otra posibilidad, que ya teníamos en versiones anteriores de MVC, es llevar el código del helper a una vista parcial independiente e invocarla desde la vista principal con @Html.Partial(). La pega de este enfoque es que es algo farragoso, pues a la hora de pasar parámetros a esta parcial hay que crear un view model, o bien jugar con dynamics.
    @Html.Partial("_Input", new InputViewModel() { 
    Field="name",
    Label="Name",
    Value="José" })
  • Por último, podríamos utilizar también los nuevos View Components, de los que hablaremos un día de estos por aquí. Aunque a priori parecen los sustitutos naturales de los helpers HTML, es igual o incluso más farragoso que el enfoque anterior, pues es necesario crear el componente, la vista (ubicada en el lugar indicado por la convención) y realizar la llamada desde la vista mediante un objeto anónimo:

    @Component.InvokeAsync("Input",
    new { label="Name", field="name", value="José" }
    )

The helper directive is not supportedPero bueno, no todo son malas noticias: muy probablemente traigan la directiva @helper de vuelta en un  futuro no demasiado lejano, por lo que "helper" sigue siendo una palabra reservada y reconocida por el parser.

Esto se ve claramente al utilizarla Visual Studio, que nos indica que está reconociendo la directiva pero no está soportada, a diferencia del error que aparecería si usamos cualquier otra directiva inexistente.

Publicado en Variable not found.

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

Picando Código

Crucigramas de Expresiones Regulares

February 08, 2016 10:00 AM

Regex CrosswordHace unos días descubrí Regex Cross­word, un sitio web de crucigramas interactivos que tenemos que completar interpretando expresiones regulares. Hay varios niveles, empezando por un tutorial bien básico para entender cómo es la cosa.

Lo interesante es que tenemos una pista mínima en el título de cada ejercicio, como para determinar lo que deberíamos leer una vez resuelto el crucigrama. Si bien son bastante ambiguas, en muchos casos ayudan si nos trancamos. O sea que mezcla esa gracia que tienen los crucigramas con resolver expresiones regulares.

No sólo es un ejercicio muy divertido (la razón #1 para usar aprender y usar Expresiones Regulares) sino que nos ayuda a repasar un poco y podemos aprender algo nuevo en el camino.

Nos podemos loguear con varias cuentas de redes sociales para crearnos un usuario e ir guardando nuestro progreso. Me loguié con mi cuenta de GitHub y el único permiso que requirió fue la información pública de mi cuenta así que eso no fue un problema.

Al progresar, la aplicación nos da “medallas” o “badges” para motivarnos a avanzar con los siguientes desafíos. La noche en que descubrí el programa logré terminé el tutorial y un par de desafíos más y llegué a un crucigrama de 5×5 con muchos signos de pregunta (escapados y no), que no era viable de terminar a las altas horas de la madrugada en que me colgué a jugar con esto…

Algo que hizo que me gustara más todavía fueron las referencias al libro La Guía del Autoestopista Intergaláctico de Douglas Adams. En estos días estoy leyendo esta trilogía de cinco libros así que esa casualidad de probabilidad 8.767.128 a 1 me resultó divertida y enriqueció mi experiencia con Regex Crossword.

El sitio provee 10 grupos de crucigramas: Tutorial, Beginner, Intermediate, Experienced, Palindromeda, Double Cross, Cities, Volapük, Hamlet y Hexagonal. Además de esto, nos da la posibilidad de jugar crucigramas subidos por otros usuarios y crear crucigramas propios con un constructor de puzzles integrado.

Por ahora llevo terminados 4 de los 10 desafíos que provee el sitio, pero sigo jugando. Es muy divertido y sacia un poco la sed de trabajar con expresiones regulares que me surgen a veces, no siempre escribimos tantas expresiones regulares como quisiéramos. Les invito a visitar Regex Crossowrds y divertirse un buen rato con expresiones regulares.

 

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

Poesía Binaria

Cómo listar archivos de forma recursiva en C, y un mundo de posibilidades en nuestros programas

February 08, 2016 09:59 AM

3042638653_f14c62a4f7_bPuede que seas un hacha con el comando find o incluso con locate; pero hay veces que nuestro software tiene que ser capaz de realizar la búsqueda de un archivo en el árbol de directorios, puede que queramos hacer inclusiones, exclusiones, o analizar las características del archivo.
Podemos tener varias misiones, por ejemplo calcular el tamaño que ocupan todos los archivos a partir de una ruta dada (como hace du -s), copiarlos a otra ruta como haría un gestor de archivos o, incluso buscar archivos repetidos. Lo que todos estos programas tienen en común es que exploran los archivos contenidos dentro de un directorio de manera recursiva, y esto es que si encontramos un directorio entramos en él y repetimos la operación.
Llegará un punto en el que no encontraremos más directorios, sólo archivos. En ese caso, hacemos lo que tengamos que hacer con ellos, y salimos de la función. El caso es que, como en todos los algoritmos recursivos, si entramos 15 veces dentro de la función, tendremos que salir otras 15 veces hasta que nuestro caso base se cumpla en todas las ejecuciones de la función.

En el caso de los archivos, siempre se va a cumplir, el número de archivos en el sistema es limitado, a no ser que encontremos un directorio que se contenga a sí mismo, haya alguna inconsistencia en disco o estemos realizando una operación no válida. De todas formas, podemos limitar la recursividad (el número de ejecuciones que podrá haber de una función, o el número de directorios dentro del directorio base en el que podremos entrar).

Aunque hace años ya publiqué un código parecido voy a extender un poco los ejemplos por petición de varios usuarios por Facebook.

Buscando un archivo

Con este programa sólo tendremos que hacer:

./listar . archivo.jpg

  • Donde . es la ruta actual, a partir de la que empezamos a leer, podría ser /home/portatil/ si queremos; y archivo.jpg es el archivo que estamos buscando.
    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
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    #include <stdlib.h>
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <dirent.h>
    #include <string.h>
    #include <errno.h>

    /* Podemos usar malloc() para reservar sólo lo necesario, como en
       http://totaki.com/poesiabinaria/2011/09/listar-archivos-dentro-de-un-directorio-o-carpeta-en-c/
    */

    #define MAXSTR 1000

    /* Función para devolver un error en caso de que ocurra */
    void panic(const char *s);

    /* Sacamos el tipo de archivo haciendo un stat(), es como el stat de la línea de comandos */
    unsigned char statFileType(char *fname);

    /* Intenta sacar el tipo de archivo del ent */
    unsigned char getFileType(char *ruta, struct dirent *ent);

    /* Obtiene el nombre del fichero con la ruta completa */
    void getFullName(char* dest, char *ruta, struct dirent *ent);

    /* Genera una cadena de espacios, para dibujar el árbol */
    void generaPosStr(char* dest, int niv);

    /* Función principal, que cuenta archivos */
    int buscaArchivo(char *ruta, char* busca, int maxNiv, int niv);

    int main(int argc, char *argv[])
    {
      int veces;

      if (argc != 3)
        {
          panic("Uso: ./listar <ruta> <archivo>\n");
        }

      printf("Entrando en: %s\n", argv[1]);
      veces = buscaArchivo(argv[1], argv[2], 10, 1);

      if (veces)
        printf ("Encontrados %d archivos\n", veces);
      else
        printf ("Archivo no encontrado\n");

      return EXIT_SUCCESS;
    }

    void panic(const char *s)
    {
      /* perror() devuelve la cadena S y el error (en cadena de caracteres) que tenga errno */
      perror (s);
      exit(EXIT_FAILURE);
    }

    void getFullName(char *dest, char *ruta, struct dirent *ent)
    {
      int tmp = strlen(ruta);

      tmp=strlen(ruta);

      if (ruta[tmp-1]=='/')
        snprintf(dest, MAXSTR ,"%s%s", ruta, ent->d_name);
      else
        snprintf(dest, MAXSTR ,"%s/%s", ruta, ent->d_name);
    }

    void generaPosStr(char* dest, int niv)
    {
      int i;

      for (i=0; i<niv*2; ++i)
        dest[i]=' ';

      dest[niv*2]='\0';
    }

    int buscaArchivo(char *ruta, char* busca, int maxNiv, int niv)
    {
      /* Con un puntero a DIR abriremos el directorio */
      DIR *dir;
      /* en *ent habrá información sobre el archivo que se está "sacando" a cada momento */
      struct dirent *ent;
      int veces = 0;
      unsigned char tipo;       /* Tipo: fichero /directorio/enlace/etc */
      char nombrecompleto[MAXSTR];     /* Nombre completo del fichero */
      char posstr[MAXSTR];         /* Cadena usada para posicionarnos horizontalmente */

      if (niv == maxNiv)
        return 0;

      dir = opendir (ruta);

      /* Miramos que no haya error */
      if (dir == NULL)
        return ;            /* No entramos, puede que no tengamos permiso */
     
      while ((ent = readdir (dir)) != NULL)
        {
          if ( (strcmp(ent->d_name, ".")!=0) && (strcmp(ent->d_name, "..")!=0) )
        {
          getFullName(nombrecompleto, ruta, ent);
          generaPosStr(posstr,niv); /* Espacios para dibujar mejor esto */
          tipo=getFileType(nombrecompleto, ent);
          if (strcmp (ent->d_name, busca) == 0)
            {
              veces++;
              printf ("%sArchivo encontrado: %s\n", posstr, nombrecompleto);
            }

          if (tipo==DT_DIR)
            {
              /* Si es un directorio, entramos, ya que es la condición
             que hace de nuestro algoritmo algo recursivo. */

              printf("%sEntrando en: %s\n", posstr, nombrecompleto);          
              veces+=buscaArchivo(nombrecompleto, busca, maxNiv, niv+1);
            }
        }
        }
      closedir (dir);
     
      return veces;
    }

    unsigned char getFileType(char *nombre, struct dirent *ent)
    {
      unsigned char tipo;

      tipo=ent->d_type;
      /* Puede que hayamos visto un enlace, pero queremos saber
         si ese enlace es de un archivo o de un directorio, para
         seguirlo o no.
      */

      if ( (tipo==DT_UNKNOWN) || (tipo == DT_LNK) )
        {
          tipo=statFileType(nombre);
        }

      return tipo;
    }

    /* stat() vale para mucho más, pero sólo queremos el tipo ahora */
    unsigned char statFileType(char *fname)
    {
      struct stat sdata;

      /* Intentamos el stat() si no funciona, devolvemos tipo desconocido */
      if (stat(fname, &sdata)==-1)
        {
          return DT_UNKNOWN;
        }

      switch (sdata.st_mode & S_IFMT)
        {
        case S_IFBLK:  return DT_BLK;
        case S_IFCHR:  return DT_CHR;
        case S_IFDIR:  return DT_DIR;
        case S_IFIFO:  return DT_FIFO;
        case S_IFLNK:  return DT_LNK;
        case S_IFREG:  return DT_REG;
        case S_IFSOCK: return DT_SOCK;
        default:       return DT_UNKNOWN;
        }
    }

    Podemos cambiar el número de niveles máximos a entrar cambiando el argumento maxNiv de la función buscaArchivo().

    También hay un pequeño añadido, es que, cuando encontramos un enlace entre los archivos del directorio, analizamos con stat() qué es dicho enlace, así, si es un directorio, podemos entrar dentro del mismo (a veces no nos interesará esto, pero es bueno conocerlo).

    Cuenta tamaño de los archivos

    Podemos utilizar dos métodos para calcular el tamaño de los archivos. Ahora, siempre haremos stat() para ver información sobre cada archivos. En este caso, veremos el tamaño de cada archivo y lo sumaremos. Copio el código completo para que podamos probarlo con tranquilidad:

    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
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    #include <stdlib.h>
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <dirent.h>
    #include <string.h>
    #include <errno.h>

    struct DirStats
    {
      unsigned totalArchivos;
      unsigned totalDirectorios;
      unsigned long tamTotal;
      unsigned totalEnlaces;
    };

    typedef struct DirStats DirStats;

    /* Podemos usar malloc() para reservar sólo lo necesario, como en
       http://totaki.com/poesiabinaria/2011/09/listar-archivos-dentro-de-un-directorio-o-carpeta-en-c/
    */

    #define MAXSTR 1000

    /* Función para devolver un error en caso de que ocurra */
    void panic(const char *s);

    /* Sacamos el tipo de archivo haciendo un stat(), es como el stat de la línea de comandos */
    unsigned char statFileType(char *fname, long unsigned* size);

    /* Obtiene el nombre del fichero con la ruta completa */
    void getFullName(char* dest, char *ruta, struct dirent *ent);

    /* Genera una cadena de espacios, para dibujar el árbol */
    void generaPosStr(char* dest, int niv);

    /* Función principal, que cuenta archivos */
    void generaStats(char *ruta, DirStats* ds, int maxNiv, int niv);

    /* Inicializa DirStats (lo pone todo a 0, por si acaso) */
    void inicializaDirStats(DirStats* ds);

    /* Tamaño en formato humano http://totaki.com/poesiabinaria/2010/03/tamano-de-archivo-para-seres-humanos-phpc-y-c/  */
    char *human_size(char *store, long double size);

    int main(int argc, char *argv[])
    {
      int veces;
      DirStats ds;
      char tamhumano[MAXSTR];

      if (argc != 2)
        {
          panic("Uso: ./contar <ruta> \n");
        }

      inicializaDirStats(&ds);
      printf("Entrando en: %s\n", argv[1]);
      generaStats(argv[1], &ds, 10, 1);

      printf ("Total de archivos: %d\n", ds.totalArchivos);
      printf ("Total de directorios: %d\n", ds.totalDirectorios);
      printf ("Total de enlaces: %d\n", ds.totalEnlaces);
      printf ("Tamaño total: %s (%lu bytes)\n", human_size(tamhumano, ds.tamTotal), ds.tamTotal);

      return EXIT_SUCCESS;
    }

    void panic(const char *s)
    {
      /* perror() devuelve la cadena S y el error (en cadena de caracteres) que tenga errno */
      perror (s);
      exit(EXIT_FAILURE);
    }

    void inicializaDirStats(DirStats* ds)
    {
      ds->totalArchivos = 0;
      ds->totalDirectorios = 0;
      ds->totalEnlaces = 0;
      ds->tamTotal = 0;
    }

    void getFullName(char *dest, char *ruta, struct dirent *ent)
    {
      int tmp = strlen(ruta);

      tmp=strlen(ruta);

      if (ruta[tmp-1]=='/')
        snprintf(dest, MAXSTR ,"%s%s", ruta, ent->d_name);
      else
        snprintf(dest, MAXSTR ,"%s/%s", ruta, ent->d_name);
    }

    void generaPosStr(char* dest, int niv)
    {
      int i;

      for (i=0; i<niv*2; ++i)
        dest[i]=' ';

      dest[niv*2]='\0';
    }

    void generaStats(char *ruta, DirStats* ds, int maxNiv, int niv)
    {
      /* Con un puntero a DIR abriremos el directorio */
      DIR *dir;
      /* en *ent habrá información sobre el archivo que se está "sacando" a cada momento */
      struct dirent *ent;
      int veces = 0;
      unsigned char tipo;       /* Tipo: fichero /directorio/enlace/etc */
      char nombrecompleto[MAXSTR];     /* Nombre completo del fichero */
      char posstr[MAXSTR];         /* Cadena usada para posicionarnos horizontalmente */
      long unsigned filesize;

      if (niv == maxNiv)
        return;

      dir = opendir (ruta);

      /* Miramos que no haya error */
      if (dir == NULL)
        return;         /* No entramos, puede que no tengamos permiso */

      while ((ent = readdir (dir)) != NULL)
        {
          if ( (strcmp(ent->d_name, ".")!=0) && (strcmp(ent->d_name, "..")!=0) )
        {
          getFullName(nombrecompleto, ruta, ent);
          generaPosStr(posstr,niv); /* Espacios para dibujar mejor esto */

          /* Cuenta un enlace */
          if (tipo == DT_LNK)
            ds->totalEnlaces++;
          tipo=statFileType(nombrecompleto, &filesize);
          if (tipo == DT_REG)
            {
              ds->totalArchivos++;
              ds->tamTotal+=filesize;
            }
          if (tipo==DT_DIR)
            {
              ds->totalDirectorios++;
              /* Si es un directorio, entramos, ya que es la condición
             que hace de nuestro algoritmo algo recursivo. */

              printf("%sEntrando en: %s\n", posstr, nombrecompleto);          
              generaStats(nombrecompleto, ds, maxNiv, niv+1);
            }
        }
        }
      closedir (dir);
    }


    /* stat() vale para mucho más, pero sólo queremos el tipo ahora */
    unsigned char statFileType(char *fname, long unsigned* size)
    {
      struct stat sdata;

      /* Intentamos el stat() si no funciona, devolvemos tipo desconocido */
      if (stat(fname, &sdata)==-1)
        {
          return DT_UNKNOWN;
        }
      *size = sdata.st_size;

      switch (sdata.st_mode & S_IFMT)
        {
        case S_IFBLK:  return DT_BLK;
        case S_IFCHR:  return DT_CHR;
        case S_IFDIR:  return DT_DIR;
        case S_IFIFO:  return DT_FIFO;
        case S_IFLNK:  return DT_LNK;
        case S_IFREG:  return DT_REG;
        case S_IFSOCK: return DT_SOCK;
        default:       return DT_UNKNOWN;
        }
    }

    char *human_size(char *store, long double size)
    {
      static char units[10][6]={"bytes","Kb","Mb","Gb","Tb","Pb","Eb","Zb","Yb","Bb"};  
      int i= 0;

      while (size>1024) {
        size = size /1024;
        i++;
      }

      sprintf(store,"%.2Lf%s",size, units[i]);

      return store;
    }

    Ahora, la función principal es generaStats() ya que se generarán algunas estadísticas de archivos encontrados, y se almacenará todo en una estructura de tipo DirStats. Se contarán directorios, archivos y enlaces y, además, se calculará el tamaño de todos los archivos, generándose un resumen de todo al final.

    Como véis, es distinto al método utilizado por du -s (o du -sh para verlo en formato humano). Y eso es porque du en lugar de contar el tamaño de los archivos, tal cual, cuenta el número de bloques y lo multiplica por el tamaño de bloque de nuestro sistema de archivos actual. Como norma general (puede haber variaciones según SO), la función stat() nos devolverá un struct donde los elementos:

    • st_size : de tipo unsigned es el tamaño total en bytes del archivo
    • st_blksize : es el tamaño de bloque del sistema de archivos actual.
    • st_blocks : es el número de bloques de 512 bytes para el archivo actual.

    Por lo que, aunque internamente se use st_blksize, nosotros usaremos st_blocks y multiplicamos por 512. Aunque normalmente si exploramos los archivos, el número st_blocks*512 sea múltiplo de st_blksize.

    De hecho, la función statFileType, la cambiamos por:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    unsigned char statFileType(char *fname, long unsigned* size)
    {
      struct stat sdata;

      /* Intentamos el stat() si no funciona, devolvemos tipo desconocido */
      if (stat(fname, &sdata)==-1)
        {
          return DT_UNKNOWN;
        }

      *size = 512 * sdata.st_blocks;
      switch (sdata.st_mode & S_IFMT)
        {
        case S_IFBLK:  return DT_BLK;
        case S_IFCHR:  return DT_CHR;
        case S_IFDIR:  return DT_DIR;
        case S_IFIFO:  return DT_FIFO;
        case S_IFLNK:  return DT_LNK;
        case S_IFREG:  return DT_REG;
        case S_IFSOCK: return DT_SOCK;
        default:       return DT_UNKNOWN;
        }
    }

    Y nos tiene que dar lo mismo que el comando du -sh.

    Foto principal: Dani

  • The post Cómo listar archivos de forma recursiva en C, y un mundo de posibilidades en nuestros programas appeared first on Poesía Binaria.

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

    Koalite

    Sé consciente de lo que sabes (y de lo que no)

    February 08, 2016 05:06 AM

    He dicho muchas veces que uno de los motivos por lo que escribo este blog es para aprender cosas nuevas. Me gusta aprender. Y me gusta enseñar. Por eso a veces, en un alarde autorreferente, me gusta aprender sobre aprender y, aunque no soy ningún experto en la materia (no soy un experto en casi nada), creo que en una profesión como la nuestra, donde la necesidad de aprender es constante, merece la pena pensar un poco sobre cómo aprendemos. Concretamente, en este post vamos a ver distintas fases por las que pasamos al aprender algo, lo que en ocasiones se denominan fases de la competencia.

    Puede que sea éste un post raro para un blog sobre desarrollo, pero considerando lo importante que es el aprendizaje en esta profesión, podemos dedicarle un poco de tiempo. A fin de cuentas, le dedicamos mucho tiempo a escribir sobre herramientas para desarrollar software y, la capacidad de aprender, junto con la de resolver problemas, son seguramente las dos herramientas más importantes que tenemos, muy por encima de IDEs, editores o librerías.

    Incompetencia inconsciente

    Es la primera fase. No sabemos hacer algo, pero es que ni siquiera sabemos que es posible hacerlo. Por ejemplo, en esta fase se encontraría alguien que no sabe que existe la programación funcional: no sabe utilizarla y ni siquiera se lo plantea porque no la conoce.

    Pero no hace falta irse a un caso tan extremo. ¿Habéis visto alguna vez una clase con métodos de 800 líneas, con nombres de variables poco descriptivos, fragmentos de código comentados porque ya no se usan y responsabilidades entrelazadas? Seguro que sí. Posiblemente sea otro caso de incompetencia inconsciente, en el que el desarrollador no sabe que hay formas mejores de estructurar y escribir el código, por lo que dentro de su marco de conocimiento, está haciendo lo correcto.

    Desgraciadamente, me atrevería a decir, y ésta es una estimación sacada de la nada, que más el 75% del código que hay en producción ha sido escrito por alguien que se encuentra en esta fase de conocimiento.

    Incompetencia consciente

    Un paso natural. En algún momento, ya sea por curiosidad, por necesidad o por el sufrimiento que nos causa nuestra propia incompetencia, empezamos a ser conscientes de que hay cosas que no van todo lo bien que deberían. Que debe haber algo más. Y comienza nuestra búsqueda.

    Nuestra clase con métodos de 800 líneas se había vuelto inmanejable y no podíamos ser los únicos con estos problemas. ¿Qué hacen los demás? Y ahí empiezas a investigar sobre clean code, patrones de diseño o principios SOLID, aunque todavía no sabes muy bien cómo aplicar eso a tu código.

    Es un paso importante, porque es el primer paso hacia la mejora, pero también es un paso que nos puede producir cierta ansiedad. Ser consciente de la cantidad de cosas que no sabemos puede resultar abrumador. Es lo que algunos llaman developaralysis y hace que nos pongamos nerviosos en lugar de dedicar el tiempo necesario para aprender bien las cosas realmente importantes.

    No siempre es necesario superar esta fase. Hay muchas ocasiones en las que nos basta saber que existe algo, tener unas ideas generales sobre lo que nos puede aportar, y dejarlo estar hasta que sea necesario. Un aprendizaje más Just In Time y menos especulativo. Por supuesto, no quiero decir que no merezca la pena aprender cosas “porque sí”, sino que no debemos agobiarnos intentando aprender los 8 frameworks de Javascript que han salido en las últimas 2 horas o los cambios del último SDK de Azure.

    Competencia consciente

    Por fin. Después de darnos cuenta de que necesitábamos aprender algo y dedicar tiempo a ello, ya somos competentes y nuestra situación mejora. Lo malo de esta fase es que aplicar los conocimientos adquiridos requiere un esfuerzo consciente por nuestra parte, y eso hace que resulte más tedioso y cansado.

    Ya no diseñamos clases con métodos de 800 líneas. Nos ha costado, pero ahora somos capaces de pensar detenidamente en las responsabilidades de cada método y, con paciencia, acabamos generando diseños con los que resulta más cómodo trabajar.

    Tenemos que estar constantemente pendientes de no volver a caer en los métodos de 800 líneas, y a veces supone tanto esfuerzo separarlos en distintas clases, gestionar las dependencias entre ellos, o buscar nombres apropiados para las variables, que acabamos obteniendo el mismo resultado que en la primera fase, ya no por incompetencia, sino por dejadez o hastío.

    Aun así, esta fase supone un avance importante porque por fin podemos aplicar mejoras, aunque nos cueste.

    Normalmente habrá un montón de cosas que sepas en las que no hayas pasado de esta fase, sobre todo cosas que no utilizas a diario. Seguro que muchos sabéis usar expresiones regulares, pero en cuanto se complican un poco, tenéis que dedicar un rato a pensar sobre los grupos de reemplazo o el greedy matching, y eso hace que a veces acabéis por emplear soluciones menos eficientes (volviendo a la primera fase) sólo por no tener que pensar.

    Competencia inconsciente

    Es el nirvana. El maestro Jedi de los estados de consciencia. Es conseguir que te salgan las cosas sin pensar, tenerlas tan interiorizadas que te salgan automáticamente.

    Puede parecer imposible, pero en realidad ya hay un montón de cosas que haces así. Andar, sin ir más lejos. No creo que seas muy consciente de los movimientos que haces para desplazarte de un sitio a otro, te sale natural. Lo mismo pasa con el lenguaje, o si quieres ejemplos menos básicos, con conducir. Cuando me estaba sacando el carnet de conducir me parecía muy complicado tener que estar pendiente de tantas cosas a la vez (la marcha, la velocidad, las señales, el tráfico, …), sin embargo, 20 años después, prestar atención a todo ello me resulta natural y no me supone ningún trabajo adicional.

    Para dar el salto desde la fase anterior a ésta, el único camino que conozco es la práctica y la repetición. En serio. Me gustaría decirte que a base de leer libros sobre clean code vas a hacerte un experto en el tema, pero si no ejercitas lo que estás aprendido, ya sea con programas reales, con katas, con proyectos para jugar, o con lo que sea, como mucho conseguirás quedarte en la fase de competencia consciente (y eso siendo bastante optimista).

    Como te puedes imaginar, llegar a esta fase requiere tiempo y esfuerzo. Mantenerse también, aunque hay ocasiones en que si llegas a interiorizar algo lo suficiente, luego es difícil olvidarlo, o al menos es fácil recuperar el nivel después de haber estado un tiempo sin practicarlo. Todos hemos oído la expresión montar en bici nunca se olvida.

    Es imposible alcanzar un grado de competencia inconsciente en todas nuestras habilidades, por lo que es necesario seleccionar en cuales queremos invertir el tiempo necesario para lograrlo. Cuando son habilidades que utilizamos en nuestro día a día, llegar a un punto de competencia inconsciente es una evolución lógica, pero si son cosas más esporádicas, puede ser muy complicado.

    Si realmente quieres llegar a este nivel con algo, mi consejo es que intentes involucrarlo en tus actividades cotidianas. Si, por ejemplo, has decidido hacerte un experto en vim o en shortcuts de Visual Studio, haz el esfuerzo consciente de usarlos siempre que puedas en lugar de recurrir a lo que fuese que hicieras antes. Es la mejor forma de ir interiorizando esa forma de trabajar y conseguir que te salga sóla.

    Conclusiones

    Conocer los distintos estados de consciencia y competencia nos puede ayudar a enfocar mejor la manera en que aprendemos y a ser conscientes, valga la redundancia, de nuestro nivel en una materia determinada.

    Para mejorar en algo, el primer paso es ser consciente de los problemas que tenemos.

    Por ello es importante estar siempre replanteándonos lo que sabemos y asumir que, tal vez, estemos en esa fase de incompetencia inconsciente y haya cosas que nos estamos perdiendo. A partir de ahí, paciencia y trabajo. A base de aprendizaje y práctica podrás mejorar hasta dominar esa materia, pero el camino es largo y hay que estar preparado para ello.

    Tampoco debes obsesionarte con todo lo que no sabes. El mero hecho de ser consciente de que no sabes algo ya te coloca en una buena posición para aprenderlo.

    No hay posts relacionados.

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

    Poesía Binaria

    BITes: SSH para WordPress, recuerdos para los veteranos, fail2ban, Human File Project, millonarios y más

    February 07, 2016 07:06 PM

    photo-1443110189928-4448af4a2bc5_r

    Una semana más, continuamos con un resumen de lo que me ha parecido interesante en la red.

    Ayúdame a seguir online

    Como la semana pasada, al lado de un par de enlaces de este post he colocado algunos enlaces que dicen [Ayúdame]. En realidad el enlace directo está al lado, pero si pincháis en ese veréis un poco de publi y me llevaré algún euro perdido de vez en cuando. El servicio que utilizo para esto es adf.ly, que de paso acorta los enlaces, aunque eso aquí da igual.

    Recomendaciones de la semana

    Allá va mi resumen:

    • Soporte SSH y SFTP para WordPress. En la vida he instalado muchos WordPress, y desde hace mucho tiempo, no instalo un servidor FTP en los servidores. Más que nada, porque no lo uso y, cuantos menos puertos abiertos, mejor. Además, he casi todos los ataques graves que he tenido en servidores han sido por el FTP. Pero si queremos que los usuarios puedan administrar plugins y temas de WordPress, actualizarlo y aprovechas muchas de sus características, necesitamos tener un FTP instalado y es algo que, hasta ahora, me fastidiaba.
    • Servicios o webs que ya no están con nosotros: El caso de Fotolog y Tuenti han sido los últimos. Pero, hay muchas webs y muchos servicios que ya no están disponibles y que nos hacen sentir algo viejunos en esto de Internet. [Ayúdame]
    • Protegiendo con fail2ban los servicios http, https y ssh de ataques DDOS: El título lo dice todo, ¿no?
    • Human File Project: Este proyecto pretende almacenar información de todas las personas que han pisado la Tierra, de manera colaborativa. Hay mucho trabajo por hacer, y la cosa acaba de empezar.
    • From Millionaires to Billionairess: Una pequeña web donde podemos ver a qué edades se hicieron millonarios y milmillonarios algunas de las personas más ricas del mundo.

    El reto 2016

    Un total de 10 posts en 2016, por lo que ya llevo más de un 6% cumplido. Me quedan 328 días para publicar 140 posts. Por lo que tengo que ir a unos 3 posts por semana.

    Foto principal: Didier Weemaels

    The post BITes: SSH para WordPress, recuerdos para los veteranos, fail2ban, Human File Project, millonarios y más appeared first on Poesía Binaria.

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

    Blog Bitix

    6º aniversario del blog

    February 07, 2016 09:00 AM

    Muchas bitácoras perecen y son abandonadas al cabo de un par de años, quizá sus autores desmotivados por las pocas visitas que reciben o por el tiempo que se dan cuenta que les requiere dedicar a escribir en una bitácora y más aún editar los artículos. En mi caso aún no he perdido las ganas por seguir escribiendo y son incluso mayores. La experiencia acumulada durante este tiempo también se nota en la redacción de los artículos.

    Hugo

    Seis años ya desde que que empecé a escribir en El blog de pico.dev y Blog Bitix aquello de lo que me interesa, investigo, aprendo, descubro o un poco más tarde analizando algunos productos que voy comprando. Viendo los primeros artículos que escribía en El blog de pico.dev y comparándolos con los que escribo ahora en Blog Bitix hay una notable diferencia, ahora me cuesta menos escribir y los artículos tienen algo de más extensión siendo más completos. También pasar de usar Blogger a Octopress en el inicio de Blog Bitix junto con GitHub Pages me ha permitido dedicar mucho menos tiempo a la edición de los artículos y hacerlo de forma más simple. Posteriormente usando Hugo como generador estático he podido personalizar mucho más el diseño. Diseño que tengo pendiente de mejorar sobre todo para la versión móvil.

    Seis años publicando un artículo semanalmente y algunas semanas incluso dos requiere mucha constancia además de dedicación y ganas, muchas bitácoras son abandonadas en un periodo de tiempo mucho menor. Por el momento dispongo del tiempo, la motivación, ideas y artículos ya escritos pero pendientes de publicar así que por el momento esta bitácora tiene la misma o incluso mejor salud que cuando empecé en el año 2010. Voy apuntando más ideas de las que incluso puedo ir escribiendo, el factor más limitante es el tiempo sobre todo por tener que compaginarlo con la jornada laboral que es lo que realmente me permite tener un sueldo a final de mes motivo por el cual suelo publicar durante el fin de semana y normalmente un solo artículo. Dado que ahora en algunos artículos incluyo vídeos de Asciinema y los que llevan código los subo a mi reposiotorio de ejemplos de GitHub me lleva algo de ese tiempo que me ahorro en la edición.

    Lentamente cada mes recibo alguna visita más y aumenta algo los ingresos por la publicidad AdSense y con los enlaces de afiliado de Amazon, mensualmente unas 9000 páginas vistas y entre 7€ y 15€ por la publicidad. Aún después de algo más de dos años de crear Blog Bitix no he llegado al mismo nivel de páginas vistas que en los mejores momentos de El blog de pico.dev, 16000 frente a 9000, lo que es una muestra de lo difícil que resulta, por lo menos a mí, atraer visitas. Quizá Google posicione mejor cuando una página tiene ya dos o tres años de vida, quizá considere mejor un dominio propio que el que tengo de https://picodotdev.github.io/blog-bitix/.

    Aún así cuando escribo pienso poco en las visitas, únicamente comparto aquello que me interesa y voy aprendiendo. Pero me satisface que por ejemplo el último artículo sobre la Conferencia BilboStack 2016 celebrada el último fin de semana de enero lo pueda considerar ya un éxito, ha sido el más veces marcado como favorito y retuiteado con diferencia de los que he escrito, muy visitado en estos pocos días y a raíz de él varias personas de mi entorno cercano me han empezado a seguir. Uno de mis objetivos de este blog como comentaba en Mi experiencia y consejos para un blog.

    Con el último ingreso de Google AdSense he podido comprar una par de camisetas a la FSFE para contribuir y cuando me las ponga difundiendo la palabra y la importancia del software libre. En futuros ingresos donaré a otros proyectos de software libre que uso o me parecen útiles, así que como decía visitando este blog considera que tú también colaboras con el software libre.

    Revisando algunos artículos antiguos me parece que no fue hace tanto tiempo que los escribí, veo la fecha y pienso que está mal pero no después de un rato me doy cuenta que para algunos artículos de los que guardo cierto cariño ya ha pasado más de un año casi dos.

    Cada año y en cada hemeroteca repito un poco lo mismo pero esto me permite reflexionar un poco y una semana de descanso sin la presión de tener que investigar algo para publicar que me llevaría más tiempo.

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

    Poesía Binaria

    Creando un cliente para un servicio de red con pocas líneas en C++

    February 05, 2016 09:16 AM

    neon_open_splitshire_r

    En la era actual, es muy importante que múltiples aplicaciones accedan a servicios online para obtener la información que desean (o incluso enviarla). Es decir, las aplicaciones han perdido su simplicidad de ejecutarse en una sola máquina, y han pasado a ejecutarse en múltiples máquinas conectadas a través de Internet.

    Y, aunque muchos piensan que C++ no es un lenguaje muy indicado para ello, y que no se pueden hacer estas cosas. Yo creo que C++ está en buena forma, tienes que cocinar un poco más las cosas (o si no te las cocinas, utilizar bibliotecas de otras personas que lo hayan hecho antes).

    Desde hace meses mantengo un proyecto en Github, llamado Glove (para compilar el ejemplo necesitaremos la biblioteca) cuyo objetivo es hacer facilitar un poco el acceso y creación de servicios de red (por TCP/IP), abstrayendo al programador un poco del uso de sockets, incluso con la posibilidad de correr tareas en segundo plano, dividirse en threads, monitorizar conexiones, etc. Reconozco que al principio, en mi cabeza era mucho más sencillo, pero vi interesante su utilización en algunos de mis programas y fui introduciendo nuevas características como SSL, envoltorio para HTTP, filtros, etc.

    De todas formas, haré una serie de posts dedicados a esta biblioteca que servirán como documentación de la misma, y comentar algunas de sus características, así como una forma de tener ejemplos para copiar y pegar; y, por qué no, encontrar bugs y solucionarlos.

    ¿Otra biblioteca para Internet? ¿Por qué? ¿Por qué?

    Porque me apetecía, quería aprender algunas cosas nuevas, y porque con alguna biblioteca que he probado me ha sido imposible cambiar algunas cosas, por ejemplo configurar timeouts de manera más flexible, interceptar las lecturas (algún día traeré un ejemplo), implementar seguridad, etc.
    Falta mucho trabajo por hacer, y mucho soporte por dar, hay algunas cosas que no funcionan o no están probadas con IPv6, tal vez en el futuro deba distinguir mejor clientes de servidores…
    Aunque, de todos modos, lo considero un buen punto de partida para hacer posibles algunos accesos sencillos a diferentes servicios (o incluso montar un servicio en modo de servidor).

    Cliente de comunicación bidireccional

    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
    #include "glove.hpp"
    #include <iostream>

    using namespace std;

    const std::string WHITESPACE = " \n\r\t";

    std::string TrimLeft(const std::string& s)
    {
        size_t startpos = s.find_first_not_of(WHITESPACE);
        return (startpos == std::string::npos) ? "" : s.substr(startpos);
    }

    std::string TrimRight(const std::string& s)
    {
        size_t endpos = s.find_last_not_of(WHITESPACE);
        return (endpos == std::string::npos) ? "" : s.substr(0, endpos+1);
    }

    std::string Trim(const std::string& s)
    {
        return TrimRight(TrimLeft(s));
    }

    int main(int argc, char *argv[])
    {
      Glove g;
      try
        {
          cout << "Buffer: "<< g.buffer_size(10)<<endl;
          g.timeout(0.01);
          g.remove_exceptions(Glove::EXCEPTION_TIMEOUT);
          g.connect(argv[1], 50000);
          g<<"Hello, tell me something..."<<endl;
          std::string data = g.receive(-1, true);

          while (1)
        {
          if (!data.empty())
            {
              if (data.substr(0,3)=="FIN")
            break;
              else
            {
              data = Trim (data);
              cout << "Server sent: "<<data<<endl;
              g<< "Hi: "<<data<<" for you."<<endl;
            }
            }

          if (GloveBase::select(0, 0, GloveBase::SELECT_READ) == 0)
            {
              std::string input;
              getline(cin, input);
              g<<input<<endl;
            }

          data = g.receive(-1, true);
        }
          g.disconnect();
        }
      catch (GloveException &e)
        {
          cout << "Exception: "<<e.what() << endl;
        }

      return 0;
    }

    Para compilar, podemos hacerlo así:

    $ g++ -o example5 example5.cc glove.cpp -DENABLE_OPENSSL=0 -std=c++11

    Es tan largo porque, por un lado, debemos desactivar el compilado con soporte O

    No hay comprobación de errores de entrada. Para probarlo, podemos montar un servidor netcat escuchando en el puerto TCP 50000 con el siguiente comando:

    $ netcat -l -p50000

    Luego, ejecutamos example5 de la siguiente manera:

    $ ./example5 localhost

    Haremos una conexión local en la que podremos enviar mensajes desde cliente a servidor y viceversa casi sin retardo, hasta que el servidor envía FIN, momento en el cual el cliente desconecta.

    ¿Qué podemos hacer con Glove?

    Con g, un objeto de clase Glove:

    • g.buffer_size(10) : hace que el buffer de entrada sea de 10bytes. Por ejemplo, si enviamos un mensaje desde el servidor al cliente de más de 10 letras, éste se dividirá en dos mensajes automáticamente. Podemos hacer que el buffer sea grande, un buen valor puede ser 4096 o 16384, aunque depende de nuestra aplicación.
    • g.timeout(0.01) : establecemos el tiempo tras el cual pensaremos que no ha llegado nada (timeout). Este tiempo vienen dado en segundos, y podemos utilizar un double para expresarlo. En este ejemplo, todo el tiempo que no estemos recibiendo mensajes desde el servidor, estaremos dando timeouts, para verificar la entrada de teclado.
    • g.remove_exceptions(Glove::EXCEPTION_TIMEOUT): Normalmente cuando ocurre un timeout devolvemos una excepción. Por ejemplo, si estamos conectando con algún tipo de servicio de Internet, el hecho de recibir un timeout, puede causar que los datos no se hayan recibido bien, o que el servidor no nos está haciendo caso, por lo que deberemos dejar lo que estamos haciendo. Debido a la naturaleza del ejemplo, esto no es así en este caso. Podemos eliminar las excepciones Glove::EXCEPTION_DISCONNECTED, éstas ocurren cuando el servidor nos ha desconectado.
    • g.connect(argv[1], 50000): Conecta con el servidor argv[1] en el puerto 50000
    • g<<“Hello, tell me something…”<<endl : Envía con la conexión actual el mensaje “Hello, tell me something…” y un salto de línea. Igual que cuando hacemos cout, podemos enviar contenidos por la red así. También vale el operador >>para recibir desde el socket y escribir en una variable local tipo string
    • string data = g.receive(-1, true): Otra forma de recibir información, de esta manera tenemos dos argumentos extra, el primero es el timeout que, si lo dejamos a -1 cogerá el timeout por defecto (que definimos con g.timeout(double), el segundo vale para realizar una sola lectura de datos y devolver lo que se ha leído (si es que se ha leído algo). Veremos más abajo que hay otra forma de hacerlo usando operadores.
    • GloveBase::select(0, 0, GloveBase::SELECT_READ) : Esta línea detecta cuándo viene información por la entrada estándar, en este caso, el teclado, el primer argumento designa el descriptor de fichero que queremos controlar, el segundo el timeout (el tiempo que vamos a esperar mientras vienen datos o no vienen, y por último GloveBase::SELECT_READ define que estamos esperando una lectura. Si todo va bien, devolverá 0, -1 significa que ha habido un fallo del sistema, y -2 significa que ha transcurrido el timeout

    Lectura de datos con operadores

    Como antes, dije que la lectura la podemos preparar también con operadores, al más puro estilo C++, es más podemos definir parámetros que afectarán a la lectura tal y como podemos cambiar la precisión de un double con iomanip.
    Por ejemplo podemos hacer:

    1
    2
       std::string data;
       g >> GloveBase::set_read_once(true) >> data;

    Y estamos definiendo el argumento deseado de g.receive() directamente. Esmás, podemos utilizar los siguientes manipuladores de entrada:

    • set_read_once(bool): Como antes, definimos que sólo queremos leer una vez y devolver el dato.
    • set_timeout_when_data(bool): Definimos si queremos devolver un timeout cuando ya hemos leído datos (sólo válido si set_read_once(false) antes. Es decir, el hecho de devolver un timeout puede depender de una lectura anterior.
    • set_enable_input_filters(bool): Definimos que queremos activar los filtros a los datos de entrada. Veremos más adelante que podemos pasar todos los datos entrantes por un callback automáticamente.
    • set_timeout(double): Sobreescribe el valor de timeout de la lectura actual.
    • set_exception_on_timeout(bool): Define si queremos lanzar una excepción o no cuando se produzca un timeout
    • set_exception_on_disconnection(bool): Define si queremos lanzar una excepción o no cuando se produzca desconexión del host de destino.

    Filtros de entrada

    Podemos hacer que todos los mensajes entrantes pasen por un callback que los transforme, de la siguiente manera:

    1
    2
    3
    4
    std::string FiltroDeEntrada(const std::string& s)
    {
      return "ENTRADA FILTRADA -- "+s+"---";
    }

    y luego con nuestro objeto (g):

    1
          g.add_filter(Glove::FILTER_INPUT, "myFilter", FiltroDeEntrada);

    Donde myFilter es el nombre del filtro (para poder gestionarlo luego), y FiltroDeEntrada nuestra función que gestiona el filtro. Es más podemos añadir varios filtros, si hacemos:

    1
    2
          g.add_filter(Glove::FILTER_INPUT, "trim", Trim);
          g.add_filter(Glove::FILTER_INPUT, "myFilter", FiltroDeEntrada);

    haremos primero el Trim y luego el nuevo filtro que hemos creado.

    Filtros de salida

    De la misma manera que los filtros de entrada, podemos configurar filtros de salida, así todo mensaje que enviemos pasará por alguna(s) función(es) antes de salir:

    1
    2
    3
    4
    std::string FiltroDeSalida(const std::string& s)
    {
      return "VA A SALIR ESTO -- "+s+"---";
    }

    y desde nuestro main():

    1
          g.add_filter(Glove::FILTER_OUTPUT, "outFilter", FiltroDeSalida);

    Seguridad

    Vamos a correr el ejemplo sobre un protocolo seguro, y ya podemos crear un chat simple entre dos ordenadores a través de Internet sin miedo a que nos lean los mensajes. Lo primero es crear el certificado:

    $ openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes

    Rellenamos los datos del certificado (no pide nada raro, en el Common Name, podemos escribir simplemente “server” o lo que queramos. Ahora sí, tenemos que hacer que nuestro programa acepte conexiones seguras (es más, copio el programa completo, con filtros y todo):

    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
    #include "glove.hpp"
    #include <iostream>

    using namespace std;

    const std::string WHITESPACE = " \n\r\t";

    std::string TrimLeft(const std::string& s)
    {
        size_t startpos = s.find_first_not_of(WHITESPACE);
        return (startpos == std::string::npos) ? "" : s.substr(startpos);
    }

    std::string TrimRight(const std::string& s)
    {
        size_t endpos = s.find_last_not_of(WHITESPACE);
        return (endpos == std::string::npos) ? "" : s.substr(0, endpos+1);
    }

    std::string Trim(const std::string& s)
    {
        return TrimRight(TrimLeft(s));
    }

    std::string FiltroDeSalida(const std::string& s)
    {
      return "VA A SALIR ESTO -- "+s+"---\n";
    }

    std::string FiltroDeEntrada(const std::string& s)
    {
      return "ENTRADA FILTRADA -- "+s+"---\n";
    }

    int main(int argc, char *argv[])
    {
      Glove g;
      try
        {
          cout << "Buffer: "<< g.buffer_size(16386)<<endl;
          //      g.timeout_when_data(false);
          g.timeout(0.01);
          g.remove_exceptions(Glove::EXCEPTION_TIMEOUT);
          g.add_filter(Glove::FILTER_OUTPUT, "outFilter", FiltroDeSalida);
          g.add_filter(Glove::FILTER_INPUT, "trim", Trim);
          g.add_filter(Glove::FILTER_INPUT, "myFilter", FiltroDeEntrada);
          g.connect(argv[1], 50000, -1, GLOVE_DEFAULT_DOMAIN, true);
          g<<"Hello, tell me something..."<<endl;
          /* std::string data = g.receive(-1, true); */
          std::string data;
          g >> GloveBase::set_read_once(true) >> data;
          while (1)
        {
          if (!data.empty())
            {
              if (data.substr(0,3)=="FIN")
            break;
              else
            {
              data = Trim (data);
              cout << "Server sent: "<<data<<endl;
              g<< "Hi: "<<data<<" for you.";
            }
            }

          if (GloveBase::select(0, 0, GloveBase::SELECT_READ) == 0)
            {
              std::string input;
              getline(cin, input);
              g<<input;
            }

          g >> GloveBase::set_read_once(true) >> data;
        }
          g.disconnect();
        }
      catch (GloveException &e)
        {
          cout << "Exception: "<<e.what() << endl;
        }

      return 0;
    }

    Ahora, compilaremos example5 de la siguiente manera:

    $ g++ -o example5 example5.cc glove.cpp -lssl -lcrypto -std=c++11

    Cargaremos el servidor, en lugar de netcat usaremos openssl para aprovechar el certificado anterior.

    $ openssl s_server -key key.pem -cert cert.pem -accept 5000

    Como véis para activar SSL sobre la conexión sólo ha hecho falta activar poner algunos argumentos a connect. El formato general es:
    connect(const std :: string & host, const int port, double timeout, int domain, int secure)
    donde,

    • timeout lo podemos poner a -1 para dejar el timeout por defecto
    • domain apuntar a GLOVE_DEFAULT_DOMAIN que por defecto es AF_INET (AF_INET6 también vale)
    • secure, podemos ponerlo a true para establecer una conexión segura

    Las conexiones seguras sólo serán posibles si Glove se ha configurado con soporte para ello. Si lo compilamos con dicho soporte podremos hacer conexiones seguras e inseguras, pero si lo configuramos sin él, sólo podremos hacer conexiones inseguras.

    Foto: Splitshire

    The post Creando un cliente para un servicio de red con pocas líneas en C++ appeared first on Poesía Binaria.

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

    Picando Código

    ATuServicio 2016

    February 04, 2016 12:15 PM

    En Uruguay el Sistema Nacional Integrado de Salud permite que una vez al año durante el mes de febrero las personas (que cumplen con ciertas características de afiliación) puedan cambiarse de proveedor de salud. Es así que varios proveedores inundan la ciudad y los medios con publicidades donde afirman un montón de cosas positivas sobre sus servicios.

    ATuServicio es un proyecto en el que trabajamos el año pasado con DATA que utiliza Datos Abiertos liberados por el Ministerio de Salud con información de distintos valores de los prestadores de servicios de salud. La aplicación muestra los datos de una manera amigable al usuario y permite comparar la información frente a frente de hasta 3 proveedores a la vez. Mediante la democratización de esta información, las personas pueden tomar decisiones más informadas a la hora de considerar un cambio de proveedor.
    ATuServicio.uy

    Para 2016 sacamos una versión con diseño renovado, datos actualizados, mejor rendimiento, más liviana y con alguna funcionalidad distinta. La metodología de desarrollo fue bastante caótica el año pasado, por lo que el resultado final no fue lo mejor que podía ser. Pero cumplió su objetivo en el momento y tuvo bastante éxito:

    La aplicación fue seleccionada en cuatro ocasiones como caso de alto impacto por la Open Knowledge Foundation (Reino Unido), Sunlight Foundation y GovLab de la New York University (EE.UU.). También fue seleccionado como “Lo Mejor del Infoactivismo LATAM 2015” por SocialTIC (México, finalista de los Open Data Institute Awards (Reino Unido) y primer premio del Open Government Partnership Open Government Awards (global)

    En tecnología, en esta versión dependemos menos de bibliotecas JavaScript, eliminamos CoffeScript, y se disminuyó mucho la cantidad de consultas SQL. La visualización de datos es más clara y en general creo que quedó más limpio y sencillo el diseño.

    Al igual que el año pasado, durante esta primera semana de febrero la aplicación tuvo bastante prensa. Una parte interesante fue que algunos periodistas hicieron periodismo de datos basado en lo que se publicó en atuservicio.uy. El periodismo de datos es una pata importante en la movida de Datos Abiertos, facilitan la investigación con hechos, y está faltando más de eso en nuestro país. Pero de a poco estoy viendo un crecimiento en esa área tanto en lo mediático como en la educación lo cual es muy positivo.

    La Diaria publicó un artículo Comparación Saludable, donde hace un análisis de los datos publicados. El programa de radio En Perspectiva invitó a Daniel “Chino” Carranza de DATA a hablar de ATuServicio y el martes estuvimos en el programa de 8 a 10 de Radio Uruguay:

    Varios medios más han ido publicando sobre el tema y probablemente en el correr del mes veamos más.

    Tenemos muchas ideas nuevas para ATuServicio que desde su puesta en producción el lunes pasado ha ido teniendo pequeñas mejoras incrementales, en funcionalidad, visualización y rendimiento. Si todo sale bien probablemente este año podamos trabajar en estas ideas y expandir su alcance.

    En lo personal ver lo que genera la aplicación, tanto a nivel de calidad de los datos como en los cambios de datos en sí, alimenta mi confianza en el tema de los Datos Abiertos. Han habido cambios que probablemente no son influencia directa de que estos datos estén públicos, pero yo sospecho que otros tantos sí. El simple hecho de poder dar utilidad a esta información justifica la existencia de la aplicación. Y las repercusiones que tiene le dan todavía más valor. Estoy muy contento de haber trabajado otro año con DATA en este proyecto y espero que siga creciendo durante 2016.

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

    Poesía Binaria

    Cómo subir tipos de archivo que WordPress no nos deja subir “por seguridad”

    February 03, 2016 09:53 AM

    peak_r

    Por motivos de seguridad, WordPress, en su configuración por defecto no nos deja subir determinados tipos de archivo. Podemos subir imágenes, sin problema, pero si queremos ofrecer algún tipo de descarga adicional como archivos comprimidos, no nos va a dejar, presentándonos el siguiente mensaje:
    Screenshot 31-01-2016-210132

    Digo motivos de seguridad, y en el título lo pongo entre comillas, porque es la excusa que pone WordPress. Puede que sea porque no son tipos de archivo comunes, y porque perfectamente pueden alojar archivos maliciosos (por ejemplo si ofreces una descarga puedes incluir un binario malicioso sin problema), pero en ocasiones sí que nos puede interesar ofrecerlo.

    Podemos buscar un plugin que nos deje subir estos tipos de archivo (si hemos creado un plugin para meter todo el código adicional que usamos en nuestro sitio (es una buena práctica tener un plugin donde podamos echarlo todo (algún día prepararé un post sobre esto) y puede quedar todo muy limpio).

    O, si nos corre mucha prisa, y no queremos que quede muy limpio (y olvida que te he dicho esto), puedes entrar en el directorio de tu tema, editar functions.php e insertar el código. Eso sí, si cambias de tema algún día recuerda que tienes que copiar ciertas cosas.

    El código en sí es el siguiente:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <?php
    add_filter('upload_mimes', 'incluir_extensiones');

    function incluir_extensiones ( $mimeTypes=array() ) {
            $mimeTypes['gz'] = 'application/x-bzip2';
            $mimeTypes['bz2'] = 'application/x-gzip';

            return $mimeTypes;
    }
    ?>

    Por supuesto, si php está abierto previamente, no tenemos que abrirlo (>?php). Y también podemos añadir tantas extensiones como queramos, siempre que respetemos el formato:

    1
    $mimeTypes['LAEXTENSIONDESEADA'] = 'MIME DE LA EXTENSION';

    Si queremos mirar el MIME de la extensión, podemos mirarlo aquí.
    Foto principal: Paul E.

    The post Cómo subir tipos de archivo que WordPress no nos deja subir “por seguridad” appeared first on Poesía Binaria.

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

    Variable not found

    ¿Cuál es el "Bus factor" de tu proyecto?

    February 02, 2016 12:05 PM

    Ojo a los autobusesMe encanta encontrar definiciones más o menos conocidas de conceptos o ideas que se nos han pasado por la cabeza en algún momento sin saber que otros ya les habían dado forma, porque demuestra que todos los que nos dedicamos a esto tenemos poco más o menos las mismas inquietudes.

    En este caso, me ha entusiasmado dar por casualidad con el "bus factor", un término que seguro muchos conocíais pero para mi ha sido un descubrimiento reciente, que básicamente da forma a la pregunta que seguro que todos nos hemos hecho sobre la continuidad de algunos proyectos ante determinados acontecimientos inesperados.

    Imaginemos que todo el equipo de desarrollo de un proyecto va caminando por la calle, y un autobús que circulaba cerca pierde el control y les arrolla de forma violenta. ¿A cuántos miembros del equipo tendría que afectar fatalmente este accidente para poner en gran peligro la continuidad del proyecto?

    "The bus factor connotes the number of team members that can be unexpectedly lost from a project ('by a bus', as it were) before the project collapses due to lack of knowledgeable or competent personnel"

    -- Bus Factor (Wikipedia)
    Pues bien, ese número es el "Bus factor", también es conocido por variantes como "Truck factor" o "Lorry factor" o similares, atendiendo al tipo de objeto que provocaría el estropicio. Básicamente, es una medida de la concentración de información y conocimiento en determinadas personas del equipo, y es peor cuanto más pequeño resulta.

    Un valor de uno es el mínimo, y seguro que lo reconocéis en muchos proyectos que habréis visto a lo largo de los años: indica que toda la información y conocimientos vitales del proyecto están depositados en una única persona, por lo que bastaría con que el autobús se cebara con ella para que el proyecto fuera al traste. Como muestra, hace años se decía que Linux tenía un Bus factor 1, lo cual se entendía como un gran riesgo para el proyecto por la gran dependencia que existía hacia su creador.

    Por aportar algo a la causa, yo casi añadiría que también existe un "Bus factor 0": proyectos que se ve a la legua que van a ir al traste aunque el autobús no atropelle a nadie, pero esto es harina de otro costal… ;)

    En bastantes empresas y proyectos de tamaño pequeño o mediano es curioso, a la vez que espeluznante, ver que el Bus factor suele ser uno, o acercarse peligrosamente a este valor. Esto podemos detectarlo fácilmente, por ejemplo, cuando:
    • La visión completa del proyecto está concentrada en muy pocas personas.
    • El equipo tiene uno o algunos key developers, cowboys o rockstars que llevan sobre sus hombros gran parte del desarrollo.
    • Hay "zonas delicadas" en el código, que sólo algunos pocos iluminados son capaces de entender y tocar.
    • Hay información secreta o datos que conocen pocas personas.
    • Hay demasiada especialización en los miembros del equipo, por lo que cada uno gestiona áreas o tecnologías en la que no entran los demás y gestionan en exclusividad.
    • Hay pocas personas con implicación real en el proyecto o el equipo está muy desmotivado.
    Claro, la probabilidad del accidente descrito anteriormente ocurra es bastante escasa, pero pero no hay que ser tan trágico para ver la importancia del riesgo que conlleva que el bus factor sea muy bajo. Existen muchísimos motivos que pueden llevar a que un miembro clave del equipo sea vea forzado o decida abandonarlo temporal o definitivamente (vacaciones, cambios en situación familiar, enfermedades, búsqueda de nuevos retos, traslados, etc.), y en todos estos escenarios un bus factor bajo hará que el proyecto se tambalee, o al menos se resienta notablemente.

    Para mejorar el bus factor se pueden tomar una serie de medidas, principalmente relacionadas con mejorar la comunicación y hacer que la información y conocimiento esté lo más distribuido posible entre los miembros del equipo. Algunas prácticas que podrían ayudar a ello son:
    • Mantener al equipo continuamente actualizado sobre el avance del proyecto. Por ejemplo, los stand-up meetings de Scrum o Kanban son una buena forma de poner en común los progresos, lo que cada uno hace, lo que piensa hacer o los problemas que está encontrando.
    • Evitar secretismos o información restringida, facilitando la colaboración y el libre intercambio de información entre los miembros del equipo.
    • Tener todos los activos del proyecto siempre disponibles en repositorios compartidos o almacenamientos a los que sea fácil acceder por parte de los miembros del equipo.
    • Hacer a las personas partícipes y responsables del proyecto. Aumentar la sensación de "este es mi proyecto" en lugar de que cada uno se centre en una tecnología, funcionalidad o área. Favorecer la compartición colectiva del código, evitando propietarios en exclusiva de partes específicas.
    • Aumentar la redundancia en habilidades y conocimientos. Esto puede conseguirse promoviendo la formación interna para romper la tendencia natural a la especialización, o llevando a cabo prácticas como pair programming con mucha rotación, pair o code reviews frecuentes.
    • Seguir convenciones y estándares comunes que hagan sencillo el movimiento de responsabilidades de un miembro del equipo a otro.
    • Evitar complejidad excesiva y grandes ideas geniales que sólo unos pocos serán capaz de entender en el futuro. Si estamos seguros de que hay partes del código que no entenderemos ni nosotros mismos algo más adelante, ya estamos tardando en replantearla. El código mantenible es fundamental, así como un cierto nivel de documentación, especialmente los procesos clave.
    • Por último, si el equipo de desarrollo es excesivamente pequeño, tiene mala solución; llevándolo al extremo, imaginad un proyecto que tira adelante con un único desarrollador. Aumentar el bus factor pasa irremediablemente por aumentar el tamaño del equipo. Si esto no es posible, la solución es aún más compleja: la forma de minimizar el riesgo sería aumentando el volumen y calidad de la documentación, de forma que ésta pudiera servir como base para retomar el proyecto si algo sucediera.
    Y por vuestra parte, estimados amigos, ¿cuál es el Bus factor de vuestros proyectos? ¿hacéis o hace vuestra empresa algo para mejorarlo? (Admitimos comentarios anónimos XD)

    Publicado en Variable not found.

    _______________________________________
    Referencias y más información en:

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

    Arragonán

    Semanas 398 y 399

    February 01, 2016 11:37 PM

    Este par se semanas hemos tenido festivo local y por eso no tenía demasiado lío para poder escaparme a Bilbao un año más para asistir a la Bilbostack. Y como siempre me lo he pasado muy bien, es un evento muy social, pero aún así me quedé con las ganas de hablar con alguna gente.

    Pude acercarme al fin a unas Geeks Talks, que he perdido totalmente la rutina de ir y dudo mucho de ser capaz de recuperarla en el corto plazo.

    Además esta semanas eché una mano en organizar el FridayDojo de Refactoring de Angular y el Zaragozarb con los amigos de Spines. En ambas charlas no se habló tanto de lo geniales que son los respectivos frameworks elegidos en cuestión (por mucho que lo puedan ser o no), si no que se ponía sobre la mesa el tema de cómo “protegerse” de ellos. Y cómo cada uno intentaba conseguir el equilibrio entre tratar de abstraerse y desacoplarse de estos frameworks, con el sacarle el mayor partido a la productividad que prometen a la hora de escribir código.

    Hemos seguido trabajando algunos leads pero sin cerrar nada de cierta entidad. Aunque sí cerré una pequeña colaboración para tratar de terminar de lanzar un proyecto que ya está muy avanzado y que lleva ya mucho tiempo atascado, un proyecto basado en Refinery.

    En cuanto a los proyectos y colaboraciones en marcha:

    • En Outreach Tool estuve refactorizando algunas cosas, limpiando código, actualizando algunas gemas y modificando parte de la lógica del proceso de compra de la tienda. También aprovechamos mi visita a Bilbao para hacer una pequeña reunión y vernos las caras.
    • Hice algún pequeño cambio en Bichomanía y tuvimos una comida de trabajo para ver cómo nos organizamos en los próximos meses para seguir haciendo pequeñas mejoras.
    • Con Maubic estuvimos, entre otras cosas, de despliegues a staging. Este par de semanas estuve trabajando en tratar de dejar lo más finos posibles los servicios en los que venía trabajando; además de empezar a trabajar en uno nuevo. También estuvimos revisando la cobertura de los tests, viendo algún analizador de código estático y poniéndonos ya serios con el logging en los servicios con vistas a los próximos meses.
    • Nos dieron la aceptación de One-step, así que desplegamos a producción y ya lo dejé facturado.

    Buena semana.

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

    Variable not found

    Enlaces interesantes 226

    February 01, 2016 06:46 AM

    Enlaces interesantesAhí van los enlaces recopilados durante la semana pasada. Espero que os resulten interesantes. :-)

    .Net

    ASP.NET

    ASP.NET Core

    Estrenamos hoy esta categoría para separar los contenidos de ASP.NET Core de los de ASP.NET "clásico".

    Azure / Cloud

    Conceptos/Patrones/Buenas prácticas

    Data access

    Html/Css/Javascript

    Visual Studio/Complementos/Herramientas

    Cross-platform

    Otros


    Publicado en Variable not found

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

    Koalite

    Por qué es importante Flux y por qué (probablemente) no debes usarlo

    February 01, 2016 05:06 AM

    Flux es el patrón (o la arquitectura, según se mire) de moda. Normal, si tienes en cuenta que funciona muy bien con React, React es la librería de moda en Javascript, y Javascript es el lenguaje de moda (no, Elixir está bien pero no está tan de moda como Javascript).

    flux-logo

    Como siempre que algo se pone de moda, hay mucha gente escribiendo sobre Flux y mucho incauto usando Flux en situaciones donde, posiblemente, no sea la mejor opción. En este post vamos a intentar conocer un poco mejor lo que hay detrás de Flux, por qué es importante conocer este patrón, y, lo fundamental: por qué no debemos lanzarnos a implementarlo en nuestra próxima aplicación.

    OJO: con la cantidad de buzzwords de este post es probable que acabe bien posicionado en Google y llegues desde allí. Si esperas encontrar un tutorial paso a paso de cómo usar Flux, lo siento pero este no es tu post. Luego no te quejes de que no hay código, que ya te estoy avisando. Por el contrario, si quieres entender cómo funciona Flux, tal vez merezca la pena que sigas leyendo.

    De dónde viene Flux

    Para entender mejor de dónde sale Flux es bueno partir de la filosofía que hay detrás de React. Si no la recuerdas, échale un vistazo a esta introducción a ReactJS para poder seguir el resto de este post. A fin de cuentas, Flux es un patrón ideado por Facebook para usar en aplicaciones con React y la influencia entre uno y otro es clara.

    En React podemos llegar a entender la vista como una función pura, es decir, sin efectos colaterales, que sólo depende del modelo o estado de la aplicación. Aunque el estado se separe en información mutable propia del componente (state), e inmutable que fluye entre componentes (props), cada componente podríamos simplificarlo como la siguiente función:

    component :: Model -> VirtualDOM
    

    De hecho, a eso en React se le llama componentes funcionales sin estado (por aquello de que no usan su state mutable) y desde la versión 0.14 son una realidad.

    Para que esto funcione un punto clave es el DOM Virtual que usa React, que hace que podamos modelar los componentes como funciones puras que devuelven un DOM Virtual pero que no modifican el DOM real. La vista es sólo una función que transforma el modelo en Virtual DOM.

    Esto está muy bien para la parte de pintar cosas en pantalla pero, ¿qué hacemos con las interacciones del usuario o de sistemas externos (timers, websockets, etc.)?

    Inspirándonos en la programación funcional y, concretamente, en la programación reactiva funcional, podemos intentar modelar los cambios de estado de forma parecida a como hemos hecho con las vistas.

    Cada acción que se genera cuando se produce una interacción desencadena un cambio en el estado. Podemos ver entonces cada acción como una función que a partir del estado anterior y los parámetros de la acción genera un nuevo estado:

    action:: (Model, ActionData) -> Model
    

    En un lenguaje como ClojureScript mundo ideal, nuestro modelo estaría formado por estructuras de datos inmutables y al ejecutarse acciones sobre él, tendríamos nuevas versiones del modelo, no mutaciones sobre el modelo anterior. Llevando esto al extremo, partiendo del modelo inicial y las acciones que han ocurrido podemos reconstruir cualquier punto de la historia. Si os recuerda a Event Sourcing es porque la idea es similar.

    Bien, con esto tenemos dos partes: un modelo que podemos convertir en vista a través de funciones puras y unas acciones que podemos aplicar sobre el modelo para dar lugar a un nuevo estado. Sólo nos falta una parte: actualizar la vista cuando el modelo cambia. Para conseguir eso, lo más fácil es hacer que el modelo dispare un evento cada vez que cambia y hacer que la vista se repinte en respuesta a ese evento.

    ¿Un evento? ¿En React? Sí, puede sonar extraño porque los eventos se asocian más a patrones como MVVM o MVC y React huye de eso, pero en este caso se trata de un evento que capturarán los componentes de más alto nivel de la aplicación (en el caso ideal, sólo el componente raíz) y serán estos componentes los que propagarán el estado hacia abajo, haciéndolo llegar a todos sus hijos a través de los props de cada hijo.

    Un dato importante: sólo existe un evento. No hay un evento por cada posible modificación, sino que el modelo lanza un evento changed cuando se produce cualquier cambio, y se renderiza toda la vista entera. Esto, que puede parecer ineficiente, suele ser bastante rápido porque toda la regeneración de las vista se hace contra el DOM Virtual, sin involucrar al DOM real que es lo que más penaliza el rendimiento.

    ¿Qué ventajas nos aporta esto?

    La ventaja más importante, y sólo por ella ya merece la pena plantearse usar este diseño, es que es mucho más fácil razonar sobre la aplicación porque la información sólo fluye en una dirección. Además, proporciona una forma fácil y homogénea de organizar toda la funcionalidad de la aplicación, aunque para mi eso queda en un segundo plano. Tener funciones puras también facilita la creación de test, si es que eso es lo tuyo.

    Qué es Flux en realidad

    Lo que hemos visto ahora son (parte de) las bases en las que se asienta Flux, pero en realidad Flux no es exactamente como lo que he descrito. Aunque parte de esas ideas, Flux se estructura alrededor de varios componentes.

    Views

    Son componentes de React que transforman el modelo en DOM Virtual.

    Existen unas vistas especiales, que se suelen llamar ViewController, que se corresponden con esas vistas de alto nivel que mencionábamos antes y que son las encargadas de escuchar el evento changed del modelo, actualizar su state y forzar así el renderizado de todo su subárbol.

    Stores

    Representan el modelo de la aplicación, desde la información descargada del servidor hasta los filtros activos en una búsqueda. Puede haber más de uno para facilitar una separación lógica de la información, y cada uno de ellos dispara su propio evento changed.

    Las vistas pueden referenciar los stores para acceder a su estado interno, aunque lo recomendable es que sólo lo hagan los ViewController, pasando estos a sus hijos la información necesaria a través de los props normales de todo componente de React.

    Los stores no exponen ningún método para modificar su estado interno, para ello hay que pasar por los Actions y el Dispatcher, que veremos más adelante.

    Actions

    Cada operación que podemos realizar en la aplicación se representa mediante un action, que es un objeto literal que contiene un identificador de tipo único para cada acción, y la información asociada a esa acción.

    Por ejemplo, si tenemos la típica aplicación de TODOs, podríamos tener una acción con este aspecto:

    {
      type: Actions.ADD_TODO,
      text: 'Comprar panceta adobada'
    }
    

    Estas acciones se envían al dispatcher, que se encargará de propagarlas hasta los stores. Cualquier vista o componente (por ejemplo un WebSocket) puede enviar estas acciones al dispatcher para desencadenar un proceso.

    Para simplificar el manejo de las acciones, es frecuente introducir un módulo que consolide varias acciones relacionadas y ofrezca un API más amigable. A estos módulos se les denomina ActionCreators.

    Por ejemplo, en el caso de la aplicación de TODOs, podríamos tener en algún action creator el siguiente método que sería invocado desde las vistas adecuadas:

    TodoActions.addTodo = function(text) {
      var action = {
        type: Actions.ADD_TODO,
        text: 'Comprar panceta adobada'
      };
      Dispatcher.dispatch(action);
    }
    

    Dispatcher

    Es el encargado de mediar entre los actions y los stores.

    Básicamente lo único que hace es permitir que los stores registren una callback que será invocada siempre que se reciba un action. Siempre. Es decir, el dispatcher, a diferencia de lo que haría un EventBroker, no discrimina a quién manda las acciones, sino que es cada store el que tendrá un “precioso” switch sobre el action.type para decidir si le interesa o no la acción y qué hacer con ella.

    Visto así, parece que el dispatcher no aporta mucho a todo esto ya que podríamos pasar las acciones directamente a los stores, pero la ventaja de usarlo es que desacopla las vistas de los stores porque no hace falta saber qué stores están involucrados en procesar un action concreto, y además ofrece métodos (como waitFor) para coordinar unos stores con otros si es necesario controlar el orden en que se ejecutan.

    El resultado

    Y éste es el pack completo de Flux. El diagrama de cómo fluye la información es el siguiente:

    flux-diagram

    Un “sencillo” flujo unidireccional de información en el que el proceso es más o menos así:

    1. Los stores disparan el evento changed.
    2. Los view controllers suscritos al evento acceden a la información de los stores y actualizan su propio state.
    3. Esto fuerza el renderizado de las views que componen los view controllers, las cuales reciben el nuevo estado a través de sus props.
    4. Cuando se genera alguna interacción, las views invocan un método en un action creator con la información necesaria para procesarla.
    5. El action creator construye un objeto action con el tipo de la acción y la información asociada, y lo manda al dispatcher.
    6. El dispatcher propaga el action hasta los stores y garantiza que los stores lo procesan en el orden adecuado.
    7. Los stores reciben el action y, si les interesa, actualizan su estado interno, generando un nuevo evento changed, lo que nos lleva otra vez al punto de partida.

    Como veis, hay algunas diferencias con respecto a los conceptos básicos que hemos explicado antes, especialmente la forma en que se gestiona el estado, ya que en lugar de implementar las acciones como funciones puras que generan un nuevo estado, tenemos unos stores que encapsulan el estado y el comportamiento, al más puro estilo orientado a objetos.

    Hay otras implementaciones del patrón, como Redux, que se ajustan más a la idea inicial, pero la implementación “oficial” de Facebook funciona como lo que acabamos de ver.

    Entonces, ¿merece la pena?

    Ahora que ya tenemos una idea de cuáles son los principios detrás de Flux y conocemos mejor cómo se suele implementar, estamos en mejor posición para evaluar hasta dónde nos puede ayudar.

    Las ideas en las que se basa Flux son relativamente simples y nos aportan unos beneficios claros, especialmente a la hora de razonar sobre la aplicación y el flujo de información. Desde ese punto de vista, aplicar esas ideas para diseñar una aplicación nos puede ayudar a conseguir un diseño más fácil de entender y mantener, y eso lo convierte en un patrón más a tener en cuenta a la hora de estructurar nuestra capa de presentación junto a los clásicos MVC o MVVM.

    Es posible simplificar Flux y renunciar a algunas partes. Si la aplicación no es muy complicada, podemos llegar a unificar actions, action creators, dispatcher y stores en un único módulo que encapsule el estado y exponga un API de commands (métodos que cambian estado pero no devuelve nada) que utilicen todas las vistas, y queries (métodos que devuelven cosas pero no cambian estado) que usen sólo los componentes raíz. Implica mayor disciplina a la hora de trabajar, pero ahorra mucho código (posiblemente) innecesario y permite mantener el “espíritu” de flujo de información unidireccional de Flux.

    Si optamos por la implementación “completa” de Flux, con sus stores, su dispatcher, sus actionsy y sus action creators vamos a necesitar una cantidad de código importante para ponerlo en marcha. Antes de decidir utilizar todo esto tenemos que estar seguros de que realmente lo necesitamos, porque la complejidad que introduce en la aplicación no es despreciable.

    Entre el extremo de unificar todo en un único módulo y montar el “pack completo” de Flux, existe toda una escala de grises en la que nos podemos mover en función del escenario en que nos encontremos.

    Para finalizar, si estás dispuesto a utilizar otro lenguaje y transpilarlo a Javascript, hay algunos que facilitan bastante una implementación más ortodoxa de Flux o alguna de sus variantes, especialmente lenguajes funcionales como Elm, Purescript o ClojureScript. Éste último cuenta con construcciones muy idiomáticas (atoms y estructuras de datos persistentes) que hacen que implementar Flux resulte más natural y requiera menos código, por lo que se convierte en una opción más atractiva.

    No hay posts relacionados.

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

    Blog Bitix

    Conferencia BilboStack 2016

    January 31, 2016 09:00 PM

    BilboStack

    Esta ha sido la quinta BilboStack celebrada de forma anual en Bilbao en la localización de la Universidad de Deusto, este año la edición del 2016. Con más asistentes que en años anteriores y unas aulas dispuestas por la universidad más grandes para dar cabida a todos. Sigue manteniéndose gratuita aunque hay que reservar entrada, todas las entradas se agotaron y ha habido lista de espera para conseguir las de aquellos que finalmente tuvieron la mala suerte de no poder asistir. Si el siguiente año no quieres perdértela no hagas planes desde ya para finales de enero de 2017 cuando esperemos sea la edición del 2017.

    Al igual que en anteriores entregas las presentaciones se han dividido en dos tracks con duración de 45 minutos ofrecidas en una única mañana. Este año estando el track 1 orientado a temas de programación o código y el track 2 con una temática más diversa desde equipos a productos y servicios.

    Hora Track 1
    8:55 Presentación
    9:00 Descubriendo Angular 2 por Hugo Biarge
    9:55 React Native 101 por Carlos Fernández
    11:20 Web Functional First por Alex Casquete
    12:15 Java ha muerto! Larga vida a Java (moderno) por Jerónimo López
    13:15 El browser plataforma del presente por Ibon Tolosana
    Hora Track 2
    8:55 Presentación
    9:00 Los equipos ¿nacen o se hacen? (Y si no nacen, ¿cómo se hacen?) por Todora Bozheva
    9:55 Cómo ser maestros constructores por Zuriñe Menendez
    11:20 Your code as a crime scene por Vicenç García
    12:15 Tipografía responsive, tipografía responsable por Diego Rodriguez
    13:15 Cómo pasar de servicio a producto y no perecer en el intento por Carlos Hernández

    No podía faltar la cuota para el popular lenguaje JavaScript este año con las dos primeras presentaciones del track 1 y si el año pasado comentaba que no había habido ninguna de Java en ya cuatro entregas, este finalmente ha tenido presencia. Aún siendo solo dos tracks me ha costado algo decidirme por la presentaciones a las que asistir. En resumen, he preferido ir salvo a la de Java a las presentaciones del track 2. React ya lo conozco al escribir el artículo Ejemplo lista de tareas con Backbone y React, en Angular 2 habrán mejorado las cosas pero se me hace redundante. En general creo que me podría enriquecer más del track 2 que del 1 ya que en mayor o menor medida las del primero ya me iban ser bastante familiares siendo programador.

    Dentro de unos días se publicarán las presentaciones y no se si los vídeos con los que absorber las ideas expuestas de forma propia, aquí resumiré las presentaciones a las que asistí con las notas e ideas principales que conseguí retener al igual que hice el año pasado con la BilboStack del 2015.


    Los equipos ¿nacen o se hacen? (Y si no nacen, ¿cómo se hacen?) por Todora Bozheva

    Comenzamos viendo el caso de una empresa Armenia, Armsoft, dedicada al desarrollo de software bancario. En una zona recientemente en conflicto se empieza de poco a construir una organización sin estructuras y sin procesos, todo el mundo sabe de todo. Buscan la excelencia programando. Para contratar, hacen un curso gratuito, dan formación y a los que seleccionan los contratan pero aún así si contratado finalmente no encaja se va (o supongo lo echan). Son buenos técnicamente pero le dan más importancia al trabajo en equipo que a los conocimientos técnicos.

    Los equipos están autoorganizados tomando sus propias decisiones con un objetivo común claro y compartido, estando las personas que lo forman enganchados a lo que están haciendo que con sus diferencias se complementan. Un equipo autoorganizado no significa caos o falta de dirección. La autoorganización es algo natural para los equipos. Sustituir el controlar por confiar es un desafío en muchas organizaciones. Un despacho y repartir el trabajo no une, el control se ejerce por desconfianza.

    ¿Como hacer equipos? ¿Cómo se crean donde parece imposible? Hay equipos que emergen pero hay que cuidarlos. La empresas tienen que crear el ambiente adecuado. Los equipos nacen entre personas que confían entre ellas y se crean confiando en las personas. Se hacen uniendo personas con capacidades complementarias. En la presentación se comenta que una forma de crear equipo es realizando actividades como hacer excursiones, _karts o paintball.

    Cómo ser maestros constructores por Zuriñe Menendez

    En el ámbito frontend de una aplicación web existen frameworks para todo pero debemos saber lo que hacen.

    A la hora de organizar los archivos frontend basta con tener los directorios fonts, images, javascripts y stylesheets siendo innecesario cosas como home, custom o new para incluir en ellas elementos para ciertas partes de la aplicación. Con los diseños hechos se puede planificar la organización. Es recomendable añadir comentarios en el CSS, no usar importants (no hay !important de !important), tabular e indentar correctamente para mejor legibilidad.

    Recomendable usar un reset.css y un animate.css. Se ha de cuidar el rendmiento en el cliente no cargando muchos CSS o JavaScripts (añado yo que con HTTP/2 reducir el número de archivos será menos necesario). Hay que conocer el modelo de cajas CSS. Dependiendo de archivo sobretodo si es sencillo es mejor tener una única hoja de estilos, con Sass se pueden llegar a tener muchos archivos. En el navegador hay elementos que se procesan más rápido: (de mayor a menor) id, clase, etiqueta, selector universal. Los ids tienen mayor rendmiento siendo utilizables para elementos únicos como la barra de navegación, sin embargo, no son reutilizables y para esto se pueden utilizar las clases.

    Todavía hay quien usa versiones antiguas de Intenet Explorer (como administraciones públicas) y en algunos casos hay que soportar 5.5, 6, 7, 8, 9, 10. Para facilitarlo se pueden usar los condicionales como <!-- [if lt IE7]>. Se debe seguir una nomenclatura prefiriendo usar el idioma inglés para dar nombres, usar el guión - para separar palabras compuestas, no usar mayúsculas ni números para los estilos CSS asignando nombre semánticos (evitando col-left, col-right, blue, dark-blue, more-dark-blue), ordenar las propiedades en el mismo orden.

    Se debe usar CSS orientado a objetos (OOCCS) agrupando propiedades comunes a varios estilos y los específicos de forma individual para el estilo en concreto. Se debe validar el CSS con W3C, validar el diseño responsive y hacer minimizado para mejorar el rendimiento. En el diseño adaptable no basta con usar un display: none ya que un vídeo se seguiría cargando y consumiendo datos para evitarlo se necesita ayuda de los backenders, para estos casos se puede empezar por el desktop.

    Your code as a crime scene por Vicenç García

    Nos quejamos de los jefes pero el código que hacemos nosotros tampoco es perfecto y viéndolo algunas partes apestan. Se puede extraer información analizando el código. La presentación se basa en el libro Your Code as a Crime Scene .

    Hay herramietas para analizar el código:

    • El tamaño de los ficheros.
    • Que archivos se modifican más habitualmente y si son grandes significa mayor probabilidad de problemas.
    • Número de commits, ficheros y cuantos cambios se les ha hecho, autores.
    • Número de indentaciones al principio de las lineas.
    • Número de autores que están modificando un archivo, que solo una persona modifique un archivo tiene riesgos. O obtener el número de adiciones o eliminaciones por autor para saber quien tiene más conocimiento de él.

    Estos datos son guías algunos datos pueden tener justificación a pesar de su aparente mal indicador. Otra medida es el acoplamiento temporal (ficheros que se modifican en grupo, cada vez que se cambia uno se cambia otro) que permite descubrir acoplamientos que no deberían existir entre ficheros que no deberían tener relación.

    Hay que tener en cuenta:

    • Process loss: la suma del equipo no es la suma de los integrantes, la necesaria comunicación resta eficiencia.
    • Pluralistic ignorance: todo el mundo sabe que algo está mal pero nadie lo dice.
    • Dar por buena una opinión porque se repite mucho.
    • Aumentar una opinión porque está con la corriente.

    Es importante tener datos para evaluar pero hay que tener cuidado con las métricas ya que son maleables.

    Java ha muerto! Larga vida a Java (moderno) por Jerónimo López

    Hay gente que considera que Java es una mierda y es el nuevo Cobol. Se alegan algunos motivos como:

    • Uso Maven y me baja medio internet.
    • Es compilado y esto lleva tiempo.
    • ProblemFactory. Clases con nombres de 45 y 90 letras.
    • XML largos, EJB, XML en Spring, en Struts, también el los pom de Maven, stack traces largos.
    • Hola mundo de 7 líneas, con parámetros y verboso.
    • Hay que declarar los beans y DTO, hay inner classes.

    Con todo esto si es una mierda, pero si lo usas así tu proyecto es una mierda no Java. A partir de 1990 aparecen nuevos lenguajes, cerca de 1995: Python, Java, PHP, JavaScript, 2001: C#. Anteriormente no había un stack con lógica de negocio no acoplado a la base de datos (existía Oracle, SAP o Access, Visual FoxPro). En 1997 se añade JDBC y Servlets, en 1999 Java EE con un montón de especificaciones. Java como lenguaje ha cambiado poco, más en las versiones 5 y 8, mayoriamente ha cambiado la máquina virtual, en las primeras versiones era lenta, ya no. En Java 8 se añaden varias novedades como propiedades básicas pero muy útiles de programación funcional, lamdas, defaults methods con las que lo que antes eran 5 líneas ahora es 1.

    Aparece Gradle como herramienta de construcción alternativa a Maven y Ant, con las ventajas de ambos sin sus defectos. Si la herramienta no sirve cámbiala. Antes un servidor de aplicaciones ejecutaba varias aplicaciones, ahora la aplicación ejecuta de forma embebida su propio servidor de aplicaciones que se inicia como una aplicación tradicional con java -jar app.jar de forma que se ha simplificado el despliegue. Se han puesto de moda los microframeworks Spark, Spring Boot, Vert.x, Play, … que arrancan la aplicación en muy pocos segundos. El ecosistema Java sigue evolucionando adaptándose a las nuevas tendencias (en su momento el XML era una de ellas, ya no).

    En el índice Tiobe sigue siendo el más popular y en GitHub ha crecido mucho. Lo usan empresas como Google, LindedIn, Amazon, Netflix. Google incluso ha creado un compilador que traduce código Java a JavaScript. Se usa en proyectos como Casandra, Hadoop, Spark, Minecraft, elasticsearch o Android. Es escalable desde tarjetas inteligentes a granjas de servidores, desde una Raspberry Pi a un mainframe. Si Java como lenguaje no te es suficiente puedes usar Scala, Groovy, Ceylon o Clojure. Tiene buenos IDEs que facilitan la escritura de código como eclipse, IntelliJ IDEA o Netbeans.

    Su futuro es bueno Sun en su momento lo hizo de código abierto y se basa en comites. Están planificadas varias mejoras, Jigsaw para modularizarlo, HTTP2, REPL, soporte JSON y finalmente se eliminará el soporte de los applets.

    En realidad Java nunca dejó de molar y ha evolucionado, esto me es parecido a las bases de datos relacionales y el potente lenguaje SQL que tampoco nunca dejaron de molar y ahí está jOOQ para demostrar que los ORM no son siempre la mejor solución.

    La presentación ha expuesto que Java sigue siendo una de los mejores lenguajes, plataforma y ecosistema con el que desarrollar aplicaciones, han sido todo argumentos. Si alguien lo quiere ver en la práctica puede revisar varios de los artículos que he escrito en esta bitácora:

    Cómo pasar de servicio a producto y no perecer en el intento por Carlos Hernández

    Grandes empresas como DELL empezaron siendo un servicio de reparar ordenadores a proporcionar ordenadores personalizados o Microsoft pasando a vender licencias de su software.

    Empezaron ofreciendo servicios para otros a crear un producto que ahora es quaderno para llevar el control de facturación y control de gastos. No es fácil pasar de servicio a producto y hay que preguntarse ¿por que quieres cambiar?:

    • Ser los propietarios reales de tu trabajo. En el trabajo para otros se aprende.
    • Tomar el control de tu destino, tener tu trabajo, no que te lo den otros.

    ¿A que estás dispuesto a renunciar?:

    • Si pasas de servicio a producto deberás abandonar a los clientes de tus servicios.
    • La expectativa de crecimiento o dinero que proporcionará el producto puede ser mucho menor que la real.

    ¿Cómo lo hicieron?

    • Con muchas horas de trabajo.
    • Durante un tiempo trabajando para sus clientes de los servicios.
    • Gastando los ahorros.

    En 2005 no había muchas herramientas o se debían instalar en Windows. Empezaron con un SaaS en 2006. En 2009 estaban en varias cosas al mismo tiempo, eran 3 y uno se dedicaba por completo al producto otro al 50% y otro a finalizar con los antiguos clientes. En 2010 se desarrolla la segunda versión de endeve con buenos resultados a base de marketing. En 2012 todos trabajan en el proyecto. España es un mercado pequeño en cuanto a internet, se internacionalizan. En 2013 se renombra endeve a quaderno internacionalizándose de una isla de Canarias a nivel mundial. En 2014 se automatizan las facturas integrándolo con sistemas de pago y el producto va funcionando.

    Hacer negocios básicos no es sexy pero estos resuelven problemas reales y una buena oportunidad para crear productos. Con su experiencia empezando de nuevo daría al inglés importancia desde el principio, crearía una audiencia para tener clientes desde un inicio, elegiría la solución más sencilla evolucionándola a medida que se conoce a los usuarios escuchándoles y diciendo no o sí a sus sugerencias, tu eres el dueño no tienes por que aceptarlas todas. Mejor externalizar servicios como marketing o publicidad ya que es difícil poder con todo siendo pequeños y otros saben más que tú en sus áreas de conocimiento.

    Usan herramientas como Slack, Trello, gmail, pasarela de pago stripe que considera mejor opción que PayPal para integrarse.

    Otros ejemplos de productos son:


    Sigues pudiendo saciar tu curiosidad con la lista de reproducción de YouTube de elComite donde están disponibles casi todas las presentaciones de las ediciones 2012, 2013 y 2014, del 2015 no se hicieron aunque si algunos audios y de este 2016 si que he visto en el track 2 al menos una cámara creo que grabando, de ser así supongo que las encontrarás en el canal del YouTube anterior.

    De nuevo hay que dar las gracias a sus organizadores, ponentes, Universidad de Deusto, asistentes y a la guerrera minoría de asistentas. Hasta el 2017, no te la pierdas.

    » 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