Noticias Weblogs Código

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

Poesía Binaria

Y tú, ¿cuántas teclas eres capaz de pulsar en un día?

February 01, 2016 09:44 AM

teclado_chulo_r

Un dato inútil si no fabricas teclados, y muy friki si te gustan este tipo de cosas. Además, depende del uso que hagas de un ordenador, si eres más de teclado o de ratón. Parece que no, pero somos muchos los que preferimos escribir qué queremos hacer que hacer varios clicks en determinadas zonas de la pantalla, o desplazamientos y arrastres con las correspondientes esperas ventana a ventana hasta que el ordenador conozca cuál es nuestro objetivo.

Por otro lado, si tienes un blog, ya la hemos liado, si eres de los que se enrollan mucho, a no ser que le dictes al ordenador y que te funcione bien, vas a pulsar muchas teclas para escribir tus posts.
Si bajas un poco verás un programa con el que poder crear tu estadística.

Respuesta rápida

En una semana de trabajo normal, como media diaria he pulsado 36622 teclas (de lunes a viernes). Siendo 23699 el día que menos y 49673 el día que más.

Respuesta extendida

Y el top5 de las teclas más pulsadas:

Tecla Pulsaciones
space 32048
e 21745
BackSpace 21359
a 18868
o 16859

Que el espacio sea la tecla más común es prácticamente normal, y es que por un lado, cuando se escribe un texto, más o menos con sentido, todas las palabras están separadas, es más, una palabra concreta no va a tener todas las letras, pero lo que sí va a tener es una separación. Por otro lado, como programador me suele gustar tenerlo todo oxigenado, tanto vertical como horizontalmente. Aunque suelo usar tabuladores para anidar el código, me gusta poner espacios a los lados de casi todos los paréntesis y al lado de operadores, prefiero:

1
int variable = ( (opcion == 'a') &amp;&amp; (opcion == 'b') );

a

1
int variable = ((opcion=='a')&amp;&amp;(opcion=='b'));

Otro descubrimiento que saco de todo esto es que me equivoco mucho. Pulso demasiado backspace y casi casi creo que es por un extraño vicio que muchos tenemos, y es que cuando te equivocas en una letra al principio de la palabra, en lugar de volver atrás, corregir y seguir, borro toda la palabra y la escribo de nuevo. En ocasiones es óptimo hacerlo así, en otras no mucho, y es algo que intento corregir.

Y las letras más comunes, e, a, o.

La respuesta más larga todavía

Todo comenzó una aburrida noche de 2010 en la que me surgió esta pregunta, ¿cuántas teclas pulso en un día? Así que, lo que se me ocurrió fue monitorizarlo y pensar en hacer un post para el Poesía Binaria… pero entre un viaje pendiente y algún bug que no corregí en su momento nunca llegué a enviar al mundo este programa.
Aunque un día, ya entrado 2016, cuando ya las vacaciones llegaban a su fin, encontré esto perdido en una cuenta de Dropbox de cuyo usuario no quiero acordarme y quería recordar programas del pasado.
Hay muchas cosas que se pueden optimizar, pero llevo utilizando el programa unas tres semanas sin problemas.

Necesitamos que nuestro entorno gráfico esté basado en Xorg, como casi todos los Linux. No funcionará con Wayland, y desconozco si el programa corre bien con XQuartz en OSX o XMing en Windows. Aunque si algún usuario hace la prueba y me deja un comentario, le estaré muy agradecido. También cabe decir que si no estamos ejecutando un entorno gráfico, estamos ante una terminal no gráfica, o conectamos remotamente a la máquina el programa no hará nada (en el último caso es lo más normal del mundo, ya que no se están pulsando las teclas directamente en la máquina).

También necesitamos la extensión record y, normalmente si hemos instalado una distribución Linux y no hemos tocado nada de configuración de Xorg, esa extensión la tendremos habilitada. Esa extensión es la que me permite saber qué está haciendo el usuario a nivel de entorno gráfico y monitorizarlo.

Si lo pensamos bien, esta aplicación es un Keylogger, pero claro, lo podemos utilizar con malas intenciones, para conocer las contraseñas de un usuario o para espiar conversaciones de chat, o como en este ejemplo, con fines estadísticos. Es más, no es está guardando nada sobre el orden de las teclas, sólo la cantidad de teclas.

Al programa tal vez le faltan cosas, y después de 5 años, no me ha dado por terminarlo. Si te apetece aprovechar el código (que dejo en github, en enlace está al final), perfecto, y me gustaría verlo, así que, forkéalo. Además, seguramente haya (y hay) cosas que no están del todo bien hechas. Es más, creo que estoy tardando más en hacer este post que lo que tardé en hacer el programa. Y seguro que podemos aprovechar cosas de C++ moderno para facilitar las cosas, o meter threads, o hacer que sea un servidor y el mismo programa te haga los análisis contando lo que aún no está escrito, en fin, muchas cosas. Esto sólo es una prueba de concepto.

Funcionamiento del programa

El programa creará un directorio oculto, llamado .keyCounter en el home del usuario que lo ejecute, allí escribirá archivos .log con las teclas pulsadas en los diferentes intervalos de tiempo. Al mismo tiempo, en pantalla, escribe lo que estamos pulsando. Así que, podemos dejar ese programa corriendo en segundo plano y él irá monitorizando sin que nos demos cuenta.

Como el programa no está escribiendo en disco constantemente (así salvamos recursos de sistema), cuando tiene la suficiente información para escribir, se escribirán los datos en disco y, con esa información podemos proceder a un análisis si jugamos con los argumentos:

Análisis de pulsaciones

Si queremos contar las teclas pulsadas en cada hora:

$ ./keyCounter analyze hourly

que nos devolverá algo como esto:

1454194800;31/01/2016 00:00;770
1454216400;31/01/2016 06:00;254
1454220000;31/01/2016 07:00;1370
1454223600;31/01/2016 08:00;2999
1454227200;31/01/2016 09:00;2995
1454230800;31/01/2016 10:00;975
1454234400;31/01/2016 11:00;46
1454238000;31/01/2016 12:00;1717
1454241600;31/01/2016 13:00;7167

Son tres valores separados por ; curiosamente para poder exportar a un archivo csv si ejecutamos así:

$ ./keyCounter analyze hourly > archivo.csv

La primera columna es la marca de tiempo en formato Unix, la segunda, es la misma marca de tiempo en formato humano y la tercera, el número de teclas pulsadas en esa hora. Es decir, si la hora son las 6:00 significa que entre las 06:00 y las 07:00 se han pulsado 254 teclas. Y, ya que madrugo un día, lo pongo por aquí.

Análisis de teclas

Para saber qué teclas hemos pulsado y cuántas veces, lo ejecutamos de la siguiente manera:

$ ./keyCounter analyze keycount

Lo que nos devolverá el top de las teclas en todos los tiempos, así:


t;11579
u;7394
v;2663
w;3397
x;2736
y;2431
z;1192

De todas formas, vemos que no tiene orden ni nada, aunque, claro, pero para eso tenemos comandos que pueden ordenar esta salida:

$ ./keyCounter3 analyze keycount | sort -t';’ -n -k2 | less

Análisis de ráfaga

Esta parte tiene fallos, y no me he parado mucho en depurar…, tal vez haya algún valor no inicializado por ahí, sé que hay algún cálculo que no estoy haciendo bien, pero no lo voy a depurar… al menos hoy. Pero puedes hacer:

$ ./keyCounter3 analyze burst

y te debe decir cuántas teclas sin parar has pulsado, es decir, todas las teclas pulsadas, con un descanso de X segundos (viene dado por una constante, MAX_IDLE_TIME). Pero bueno, no debemos confiar mucho en lo que salga de este análisis todavía.

El programa

Podemos encontrar una versión pre-compilada para Ubuntu 14.04 por si queréis hacer vuestras estadísticas (y os fiáis de que en realidad el keylogger no envía contraseñas por Internet). Aunque también os podéis descargar el código fuente, o podéis clonar el repositorio de Github por si queréis curiosear:

Dependencias

Precisamente el archivo precompilado está aquí por si no te apetece descargar bibliotecas de desarrollo para probar el programa. De hecho, las bibliotecas son:

  • libx11-dev
  • x11proto-record-dev
  • libxtst-dev

Tendrás que buscarlas en tu distribución para compilar el programa de la siguiente forma:

$ g++ -o keyCounter keyCounter.cpp cfileutils.cpp -lX11 -lXtst

Si tienes algún problema compilando, copia y pega el mensaje de error en un comentario, por favor.

¿Cuántas teclas pulsas tú?

Yo te he dicho las mías, ¿ te apetece jugar y publicar tu estadística de una semana, de un día o de una hora ?

The post Y tú, ¿cuántas teclas eres capaz de pulsar en un día? appeared first on Poesía Binaria.

» 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 10: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 Teodora 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 Teodora 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...

Variable not found

Cómo cambiar el nombre a la carpeta wwwroot en ASP.NET Core

January 30, 2016 08:18 PM

ASP.NET CoreLo malo que tiene escribir sobre un producto que está en construcción es que a veces los artículos quedan desfasados muy rápidamente. Ya hemos visto por aquí otros casos en los que he tenido que actualizar contenidos cuando los chicos del equipo de ASP.NET han estornudado… y últimamente, con los fríos del invierno, parece que andan bastante resfriados ;)

Y hoy vamos a comentar muy rápidamente otro cambio que han introducido hace relativamente poco tiempo, y que afecta a lo que contábamos hace algunas semanas sobre la nueva carpeta "wwwroot", el lugar donde colocaremos todos los archivos estáticos usados por nuestra aplicación web.

Pues bien, hasta hace poco era posible modificar el nombre de la carpeta en el archivo project.json, pero al final el equipo de desarrollo cambió de idea y han modificado las convenciones que comentábamos entonces, por lo que, a partir de la RC-1, el criterio para determinar dónde se guardan los archivos estáticos de un proyecto es el descrito a continuación.

Primero, si existe el archivo "hosting.json", utilizado para configurar aspectos relativos al hosting de nuestra aplicación, y en él existe la propiedad "webroot", su valor será el nombre de la carpeta que utilizaremos como raíz de los archivos estáticos.
    Configuración de webroot en hosting.json
Si no existe este archivo o no contiene un valor para "webroot", pero existe en la carpeta principal del proyecto una subcarpeta llamada "wwwroot", se tomará ésta como raíz de los archivos estáticos. Esta sería la configuración por defecto, la que encontramos en aplicaciones recién creadas.

En cambio, si ninguna de las condiciones anteriores son satisfechas, StaticFilesMiddleware, que es el componente encargado de procesar las peticiones realizadas a archivos estáticos, retornará siempre un error 404.

Publicado en Variable not found.

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

Poesía Binaria

BITes: Passwords en RAM, routers, servicios, OpenShot 2.0, silencio y algo de humor

January 30, 2016 08:17 PM

12391903_1296607033723521_1586297891790579764_n

Esta semana he hecho muchas cosas en C++ que iré repartiendo en el tiempo, poco a poco iré redactando posts e iré organizando un poco la información para ponerla legible y un poco más humana.

Ayúdame a seguir online

Nota: en el resto de este post, veremos enlaces que pone Ayúdame. Estos enlaces son exactamente iguales a los que hay al lado, pero tienes que tragarte unos segundos de publicidad. Por esa publicidad recibo unos 10 céntimos cada 110 clicks. Si queréis ir al link directo, sin problema, está al lado, esto es sólo para los voluntarios. No es mucho, pero como tampoco es un esfuerzo sobrehumano.

El acortador que uso para esto es: adf.ly

¡Al tema!

Por lo pronto, traigo algunos links que he encontrado esta semana que me han llamado la atención (los que me seguís en Facebook o en Twitter, seguro que habéis visto algunos:

  • Proteger tu password cuando está en la RAM: Cuando un sistema informático almacena un dato en memoria, si somos capaces de extraer una foto de la memoria en ese momento, podremos extraer el dato. Pero, ¿ qué hacemos con los datos sensibles ?
  • Un error de 2002 afecta a routers actuales: Tal y como lo lees, si 10 años en el mundo de la informática es como 120 años en el mundo de los humanos, imagínate 14. Pero vamos, un día de estos vemos un fallo informático actual causado por el bug del FDIV de los Pentium [Ayúdame]
  • Buscar el puerto que pertenece a cada servicio: Lo vi por curiosidad en Twitter, gracias a @Mr_Prometheus, y no le hice mucho caso al principio porque siempre podemos hacer less /etc/services, de todas formas con este programa encontramos más puertos, más servicios y algunas formas más accesibles para buscar y presentar la información.
  • OpenShot 2.0 vive !! ¿Recordáis OpenShot? Aquel editor de vídeo para Linux que integraba scripts para Blender y era capaz de hacer cosas muy chulas, de forma fácil. Bueno, en 2013 su autor abrió una campaña en kickstarter para financiar la versión 2.0 que promete ser un editor de vídeo esta vez multiplataforma que tiene que hacer el café y encima ser libre y gratis. Bueno, aunque va tarde, pero va, y aquí nos presenta una Beta !! A ver si me animo y la pruebo pronto.
  • El día que me arrebataron el silencio: Algo de autobombo. En un blog que tengo medio abandonado, pero que a veces escribo algo… alguna de mis historias, que en total se ha compartido unas 65 por Facebook, y me ha hecho ilusión. [Ayúdame].

Algo de humor


Y una imagen que encontré en un grupo perdido de programación en Facebook:
responsive

El reto 2016

Como update para el reto 2016. Llevo 6 posts este año, bueno, las primeras semanas no publiqué nada nuevo, pero a este paso, hago 72 posts este año, pero bueno, habrá semanas y meses mejores. ¿Lo conseguiré?

Agradecimientos

La foto principal, la vi en el grupo Technostalgy en Facebook. Y un agradecimiento especial a @davidochobits por enviar uno de mis posts a Menéame. No llegó a portada, es muuy difícil, pero me hizo mucha ilusión.

The post BITes: Passwords en RAM, routers, servicios, OpenShot 2.0, silencio y algo de humor appeared first on Poesía Binaria.

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

Poesía Binaria

Cómo hacer una barra de progreso gráfica para las transferencias con rsync

January 29, 2016 09:45 AM

photo-1449973581296-e82bb57dc2ca_r

Rsync es una gran utilidad, de esas en las que al principio da miedo meterse, pero, cuando un día la descubres no quieres utilizar otra cosa y esperas el momento en que vuelves a utilizarla y encima se lo dices a tus amigos. Que luego te miran con cara de friki, y te dejarán de hablar, pero bueno… esperas pacientemente el momento en que ellos descubran Rsync.

Rsync, es una utilidad para copiar archivos. Tenemos, cp, incluso cp -r, pero rsync es capaz de mostrar el progreso (hace mucho propusieron cp -r, pero lo rechazaron por no cumplir la filosofía) y copiar tanto en la misma máquina como en un servidor externo; tenemos scp, pero rsync es capaz de hacer la copia transmitiendo la menor cantidad de datos posible, es decir además de comprimir la transferencia su copia es incremental, vamos, obtiene sólo los bytes modificados de un archivo en el origen y los copia en el destino, puedes excluir ciertos archivos de la copia, y muchas cosas más.

Eso sí, la indicación de progreso de rsync es sólo para visualizar en terminal, y no es inmediato pasarla a través de una pipe a otro lado (los linuxeros siempre queremos hacer algo con la información que tenemos en pantalla), además de incluir caracteres especiales para posicionarse en la terminal.

En estos ejemplos vamos a utilizar zenity como gestor de la salida en formato gráfico, tal y como indica este ejemplo:
Screenshot 27-01-2016-030125

Esta ventana del ejemplo, la podemos obtener fácilmente si escribimos en consola:

1
$ zenity --progress --text "Copiando archivos" --title "Progreso de Rsync" --width=300

y luego, mientras está ejecutándose la aplicación escribimos un número entre el 0 y el 100 veremos que la barra de progreso se mueve, si ponemos cualquier otra cosa, la barra de progreso no se mueve, pero si empezamos una línea con una almohadilla (#) conseguimos que ese texto se escriba en la ventana de progreso también.

Un gran GUI conlleva una garn pérdida de CPU

Siempre será mucho más rápida una copia muda que una que nos esté diciendo todo el rato lo que queda. Aún más si la copia indica su progreso en formato gráfico. Imagina que no sólo tenemos que estar copiando, sino que tenemos que actualizar un dibujo, un texto, e incluso estar calculando todo el rato lo que queda. Es cierto que ahora tenemos todos un montón de cores en nuestro ordenador y no pasa nada, pero bueno, tengo que avisar que no es lo más rápido.

Por otro lado, somos humanos, y si no nos dicen todo el tiempo lo que queda, nos desesperamos, y el tiempo parecerá mucho más lento, como decía Einstein: “Pon tu mano sobre una estufa caliente durante un minuto y te parecerá una hora. Siéntate junto a una chica bonita durante una hora y te parece un minuto. ESO es la relatividad“.

Y bueno, y un poco de sobrecarga también se produce porque tenemos que encadenar varios programas haciendo que la salida de uno vaya a la entrada de otro y la salida de éste a la entrada de otro… usaremos muchas pipes. Pero estará todo contado con detalle.

Dos tipos de salida

Aún así, rsync optimiza mucho la salida, en este caso, los volcados del buffer de salida. Es decir, siempre que escribe en pantalla no realiza un volcado por lo que si queremos extraer los mensajes producidos desde otro programa, éste último no los va a ver. (Ya veremos cómo hace esto).

El tema es que rsync sí que produce un volcado de información de pantalla cuando acaba de copiar un fichero y se dispone a copiar otro nuevo, así que podríamos aprovechar esos volcados para extraer y representar el progreso:

1
2
3
$ rsync -avh --info=progress2 ORIGEN usuario@servidor:ruta/en/servidor |
  awk '{print $3; fflush(); }' |
  zenity --progress --title "Copiando archivos" --width=300 --text "Copiando, espere por favor"

En este ejemplo estamos encadenando tres cosas:

  • rsync:
    • -a : modo de archivos (es recursivo, incluye enlaces, conserva permisos, conserva marcas temporales, grupos, dueños y algunas cosas más).
    • -v : incrementa los detalles de salida. Nos dice más datos sobre la copia.
    • -h : salida en formato humano (los tamaños de archivo, en lugar de decirlos en bytes los transforma a una unidad más legible, como por ejemplo Kb, Mb…). Esta opción no es tan necesaria para el ejemplo, porque sólo necesitamos los porcentajes, pero bueno, si algún día queremos aumentar la salida, aquí la tenemos.
    • –info=progress2 : Indica el progreso en porcentaje y tamaño. Hay dos tipos de progreso, progress a secas es el progreso de cada archivo, es decir, cada archivo irá del 0 al 100, en cambio progress2 es el progreso global. Sólo irá del 0 al 100 una vez.
    • ORIGEN: indicará el archivo o los archivos a copiar, puedes utilizar asteriscos, o llaves para seleccionar los archivos a copiar.
    • usuario@servidor:ruta/en/servidor : en caso de querer copiar a un servidor, podemos utilizar este formato para un servidor a través de SSH (el servidor de destino deberá tener instalado el paquete rsync), esto también puede ser una carpeta en nuestro ordenador. O incluso el servidor puede ser el origen y la carpeta el destino.
  • awk ‘{print $3; fflush(); }’ : para cada línea que me devuelva rsync ejecutaré un script sobre ella. awk es un lenguaje de tratamiento de cadenas de texto, en este caso, estamos escribiendo la tercera columna (las columnas por defecto vienen separadas por espacios). Tras imprimir la tercera columna, hacemos un volcado forzado del buffer de salida para que éste podamos pasarlo al siguiente comando.
  • zenity : ¡ mi preferido ! Hecho en GTK+ nos permite crear diálogos que podemos automatizar.
    • –progress : Crea un dialogo de progreso, como este que queremos. Si curioseamos el programa, podemos ver que permite crear una gran variedad de diálogos diferentes.
    • –title “Título de la ventana” : con esto lo digo todo.
    • –text “Texto sobre la barra” : también se explica solo.
    • –width=300 : la ventana tendrá un ancho de 300 pixels.

Archivos muy grandes

El problema del ejemplo está cuando queremos copiar archivos muy grandes, y por lo tanto tardarán mucho, la barra de progreso no se actualizará hasta que no se transfiera el archivo por completo. Imaginad la copia de un archivo de 1Gb y otro de 1Mb, uno tardará muchísimo menos que el otro, y la barra no se moverá durante varios minutos.

Para solucionar esto, tenemos que forzar el volcado de buffers de rsync, además, por la forma de la salida de rsync, tenemos que transformar los caracteres \r por \n y así poder interpretarlos como líneas.
Para ello, encadenaremos más comandos en el asunto, aunque antes de representar el progreso gráficamente, vamos a extraer las líneas por separado (por si queremos hacer algo con ellas: escribir un log, mandar un e-mail con el progreso, o lo que se os ocurra):

1
2
3
$ rsync -avh --info=progress2 --out-format="FILE %f" ORIGEN usuario@servidor:ruta/en/servidor |
  stdbuf -oL tr "\r" "\n" |
  while read -r logline; do  echo "RSYNC: $logline";  done

En este comando podemos ver cosas nuevas:

  • rsync:
    • –out-format=”FILE %f” : Cada vez que se copie un archivo se escribirá el nombre del archivo precedido de la palabra FILE.
  • stdbuf: Cambia el modo de volcado de buffers del comando que ejecutaremos (en este caso, tr)
    • -oL : Los buffers serán de línea, por lo que los datos se volcarán cada línea
    • tr “\r” “\n” : transforma los \r en \n como dijimos antes.
  • while read -r logline; do echo “RSYNC: $logline”; done : Mientras se puedan leer líneas de la salida del comando anterior, dichas líneas las almacenaré en la variable logline e imprimiré en pantalla “RSYNC: logline”

Lo de usar el while está muy bien en muchos casos, es más, ahora debemos analizar línea a línea la salida generada para extraer el progreso, pero lo haremos directamente con awk, como antes:

1
2
3
4
$ rsync -avhP --out-format="FILE %f"  ORIGEN usuario@servidor:ruta/en/servidor |
  stdbuf -oL tr '\r' '\n' |
  awk '{print $2; fflush(); }' |
  zenity --progress --title "Copiando archivos" --width=300 --text "Copiando, espere por favor"

Ahora, hemos juntado el ejemplo de antes con awk y zenity para escribir el progreso en la barra. Ahora, al ser -P y no –info=progress2 escribirá el progreso de cada archivo por separado. Aunque para los archivos pequeños, la barra no se mueve, ya que no pasará por el 0 para reiniciar el progreso. Vamos a hacer una modificación más.

1
2
3
4
$ rsync -avhP --out-format="FILE %f" ORIGEN usuario@servidor:ruta/en/servidor |
  stdbuf -oL tr '\r' '\n' |
  awk '{ if ($1=="FILE") { $1="#"; print $0 "\n0"; } else print $2; fflush(); }' |
  zenity --progress --title "Copiando archivos" --width=300 --text "Copiando, espere por favor"

Hemos complicado un poco el script de awk. Ahora estamos diciéndole que si la primera palabra de la línea es FILE (recordemos que el out-format empezaba por FILE y luego el nombre de archvio), sustituimos FILE por almohadilla (#) y escribimos la línea completa (la # la utilizaba zenity para cambiar el texto sobre la barra). Seguidamente escribimos un INTRO (\n) y un 0 y con esto le decimos a zenity que reinicie el progreso de la barra.

Indicador de progreso global

Lo que podemos hacer con rsync para tener una visión global del progreso, y reaccionar frente a archivos grandes, sería:

  1. Contar el número de archivos que tenemos que copiar
  2. Sabiendo el número, podemos calcular en qué parte del progreso global nos encontramos, y sumarle el progreso de cada archivo por separado.

Aquí tendríamos un problema, si tenemos muchos muchos archivos nos darían igual los archivos grandes, ya que si el progreso de un archivo grande no llega al 1% daría igual ver su progreso por separado o no, de todas formas, vamos a poner el progreso del archivo entre paréntesis.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ NUMBEROFFILES=`rsync -a --list-only ORIGEN usuario@servidor:ruta/en/servidor | wc -l`
$ rsync -avhP --out-format="FILE %f" -ORIGEN usuario@servidor:ruta/en/servidor |
  stdbuf -oL tr '\r' '\n' |
  awk "BEGIN{ CURRENT=0; CURRENTFILE=\"\" }
    { if (\$1==\"FILE\")
      {
        CURRENT=CURRENT+1;
        \$1=\"#\";
        CURRENTFILE=\$0;
        print \$0 \"\n\"100*(CURRENT-1)/$NUMBEROFFILES;
      } else {
        PROGRESS=\$2/$NUMBEROFFILES+100*(CURRENT-1)/$NUMBEROFFILES; print CURRENTFILE\" (\"PROGRESS\")\n\"PROGRESS
      }
      fflush(); }"
|
  zenity --progress --width=400

El script awk es más grande, y al cambiar la comilla simple por doble (porque como voy a meter variables de bash ($NUMBEROFFILES) me viene algo mejor, aunque las variables de awk tengo que escaparlas (por ejemplo \$0, \$1…).

Lo que hago al principio es contar cuántos archivos van a ser transferidos con –list-only. Como este parámetro escribe los archivos a transferir uno por línea, con wc -l cuento las líneas de salida y obtengo el número de archivos total.

Dentro de awk, mi script lo divido en BEGIN {…} y {…}, la primera parte la ejecutaremos antes de la entrada de datos, por lo que inicializaremos variables como el número de archivo que estamos analizando y el nombre del archivo actual.

Ahora, para conocer el progreso hasta el momento utilizo \$2/$NUMBEROFFILES+100*(CURRENT-1)/$NUMBEROFFILES, es decir, el porcentaje total del archivo actual entre el número total de archivos (con lo que obtenemos el porcentaje de progreso global de este archivo) y a eso le sumamos (el número de archivo actual-1)/número de archivos) que será el progreso global hasta el archivo actual.

Para hacer pruebas…

Para hacer pruebas con todo esto podemos utilizar el modificador –bwlimit de rsync con el que podemos limitar por ejemplo a 100K/s (–bwlimit=100K) la transferencia de ficheros, o incluso a menos, el objetivo es que se transmita lento para ver cómo se comporta el script.

Foto principal: Brandon Redfern

The post Cómo hacer una barra de progreso gráfica para las transferencias con rsync appeared first on Poesía Binaria.

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

Poesía Binaria

Obtener la IP y sólo la IP de un dispositivo de red en nuestros scripts

January 27, 2016 09:53 AM

photo-1414495984329-50d61ce35d7e_r

Cuando estamos haciendo un script, en ocasiones necesitamos la información muy masticada, para pasarla como parámetro a otro programa, para realizar operaciones con un dato en concreto, para introducirlo en una condición, etc.

Este es el caso de la IP de un dispositivo de red (la privada), o puede que la pública, depende de si tenemos conexión directa o no.

¿Par qué nos puede interesar?

Para compartir nuestra dirección con otros dispositivos de nuestra red, por ejemplo. En mi caso, tenía que crear un túnel y que éste estuviera disponible a varios ordenadores de la misma red.

El gran problema, es que mi conexión con la red local era por DHCP, es decir, cada vez que me conecto a esa red, el router me asigna una IP de forma dinámica, diferente a la actual, y a la hora de vincular el túnel a mi dispositivo de red debía decirle la IP del mismo.

Varias maneras de hacerlo

Conocemos ifconfig, que tiene una salida parecida a esta:

$ /sbin/ifconfig
eth0 Link encap:Ethernet direcciónHW c8:60:00:3d:7f:65
Direc. inet:192.168.0.24 Difus.:192.168.0.31 Másc:255.255.255.224
Dirección inet6: fe80::ca60:ff:fe3d:7f65/64 Alcance:Enlace
ACTIVO DIFUSIÓN FUNCIONANDO MULTICAST MTU:1500 Métrica:1
Paquetes RX:913229 errores:0 perdidos:287 overruns:0 frame:0
Paquetes TX:658288 errores:0 perdidos:0 overruns:0 carrier:1
colisiones:0 long.colaTX:1000
Bytes RX:959665104 (959.6 MB) TX bytes:87001781 (87.0 MB)

lo Link encap:Bucle local
Direc. inet:127.0.0.1 Másc:255.0.0.0
Dirección inet6: ::1/128 Alcance:Anfitrión
ACTIVO BUCLE FUNCIONANDO MTU:65536 Métrica:1
Paquetes RX:34631 errores:0 perdidos:0 overruns:0 frame:0
Paquetes TX:34631 errores:0 perdidos:0 overruns:0 carrier:0
colisiones:0 long.colaTX:0
Bytes RX:3286955 (3.2 MB) TX bytes:3286955 (3.2 MB)

Jugando con ifconfig

En este caso, debemos extraer el 192.168.0.24 pero sin nada más. Por ejemplo podríamos utilizar:

1
BINDIP=`LC_ALL=C /sbin/ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}'`; zenity --info --text="Tu IP es $BINDIP";

Este pequeño script almacena en BINDIP la dirección IP del dispositivo eth0 y la muestra en pantalla con zenity (podemos hacer echo “Tu IP es $BINDIP” pero con zenity queda más chulo, si lo tenemos instalado)

En principio estamos cambiando la locale del sistema a la locale por defecto, con esto obtendremos los mensajes de ifconfig en inglés y haremos que nuestro programa no dependa del idioma del sistema. Por ejemplo “inet addr:” en mi ordenador es “Direc. inet” y ya que lo utilizamos como cadena para filtrar la salida si omitimos LC_ALL=C no funcionará.

Por lo demás, estamos llamando a ifconfig, filtrando con grep (con el que sacamos una sola línea de la salida, luego recortamos la línea en los “:” y luego separamos por espacio. Esto lo podemos hacer más directamente así:

1
BINDIP=`LC_ALL=C /sbin/ifconfig eth0 | awk -F' ' '/inet addr:/ {split($2,ip,":"); print ip[2]}'`; zenity --info --text="Tu IP es $BINDIP";

En este caso sólo utilizamos ifconfig y awk para filtrar, pero podríamos filtrar con sed:

1
BINDIP=`LC_ALL=C /sbin/ifconfig eth0 | sed -En 's/127.0.0.1//;s/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p'`; zenity --info --text="Tu IP es $BINDIP";

Hasta ahora estamos haciendo lo mismo de varias formas diferentes, aunque, como siempre, dependiendo del sistema nos puede interesar más una u otra.

Con ip

¿Queremos más formas? Si podemos, o si queremos cacharrear un poco, podemos utilizar otros métodos:

1
BINDIP=`ip addr show eth0 | awk '/inet/ {print $2}'`; zenity --info --text="Tu IP es $BINDIP";

Que nos devolverá IP y los bits de la máscara de red, o

1
BINDIP=`ip route get 1 | awk '{print $NF;exit}'`; zenity --info --text="Tu IP es $BINDIP";

Con hostname

También se lo podemos pedir a hostname. Normalmente nos devuelve el nombre del host actual, pero también podemos pedirle las direcciones IP de todos los dispositivos conectados, en este caso, el primer dispositivo coincidirá con la primera palabra, el segundo con la segunta y así sucesivamente:

1
BINDIP=`hostname -I | cut -d' ' -f1`; zenity --info --text="Tu IP es $BINDIP";

¿Algún método más?

Está bien disponer de varios métodos ya que puede que nos encontremos en sistemas empotrados con versiones mínimas de Unix en las que no dispongamos de muchas utilidades, o simplemente en el sistema actual no tengamos permiso para acceder a la información.

Foto principal: Luisa Rusche

The post Obtener la IP y sólo la IP de un dispositivo de red en nuestros scripts appeared first on Poesía Binaria.

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

Variable not found

Enlaces interesantes 225

January 25, 2016 08:05 AM

Enlaces interesantesAhí van los enlaces recopilados durante la semana pasada, espero que os resulten interesantes. Ah, y recordad que donde dice ASP.NET 5, debéis leer ahora ASP.NET Core ;-)

.Net

ASP.NET

Azure / Cloud

Conceptos/Patrones/Buenas prácticas

Data access

Html/Css/Javascript

Visual Studio/Complementos/Herramientas

Otros

Publicado en Variable not found

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

Variable not found

Añadir o eliminar encabezados de respuesta en ASP.NET Core y Core MVC

January 24, 2016 05:53 PM


ASP.NET CoreA veces, en nuestras aplicaciones ASP.NET Core y MVC puede ser interesante manipular los encabezados retornados desde el servidor al cliente en todas las peticiones, ya sea añadiendo información personalizada o eliminando encabezados generados por otros componentes o middlewares que participan en el proceso de la petición.

Un ejemplo muy típico puede ser la eliminación del header "Server" para no dar pistas sobre el software usado en el servidor, que, como se comentaba en la propia RFC del protocolo, "podría hacer nuestro servidor más vulnerable a ataques".

Obviamente, el tratarse de una tarea absolutamente transversal e independiente de las aplicaciones en las que vayamos a aplicar esta técnica, es la misión ideal para un middleware personalizado. Así pues, crearemos un componente de este tipo y lo insertaremos al principio del pipeline, capturando todas las respuestas enviadas al cliente y aprovechando ese momento para manipular los encabezados a nuestro antojo.

Introducing HeaderTransformMiddleware

Comenzando por el final, nuestra intención es implementar un middleware personalizado que podamos añadir al pipeline de la siguiente forma:
var transforms = new Dictionary<string, string>()
{
    ["X-Author"] = "José M. Aguilar",
    ["Server"] = null
};
app.UseHeaderTransform(transforms);
En ese diccionario de transformaciones indicaremos los encabezados que deseamos establecer (si existen previamente se sobreescribirán), y los que queremos eliminar (estableciéndolos a null).

En principio, la cosa debería haber sido tan sencilla como crear un middleware como el que sigue:
// Ojo: no funciona bien, más abajo se explica el por qué y cómo solucionarlo
public class HeaderTransformMiddleware
{
    private readonly RequestDelegate _next;
    private readonly IDictionary<string, string> _transforms;

    public HeaderTransformMiddleware(
RequestDelegate next, IDictionary<string, string> transforms)
    {
        _next = next;
        _transforms = transforms;
    }

    public async Task Invoke(HttpContext context)
    {
        await _next(context);
        TransformHeaders(context.Response.Headers, _transforms);
    }

    private void TransformHeaders(IHeaderDictionary headers,
IDictionary<string, string> transforms)
    {
        if (transforms == null)
            return;

        foreach (var entry in transforms)
        {
            if (string.IsNullOrWhiteSpace(entry.Value))
                headers.Remove(entry.Key);
            else
                headers[entry.Key] = entry.Value;
        }
    }
}
Sin embargo, las cosas no son nunca tan sencillas, y si lo probamos veremos que en muchos casos no funcionará bien. El problema que tiene el código anterior es quedamos a la espera de que el resto de middlewares procesen la petición (en la llamada a await _next(context)) para modificar los encabezados, pero en el momento que volvemos a tomar el control es posible que éstos hayan sido enviados ya al cliente, por lo que cualquier transformación que les apliquemos sencillamente no serán tenidas en cuenta.

Afortunadamente, ASP.NET Core permite registrar delegados que se ejecutarán justo antes de que se comiencen a enviar los encabezados al cliente, por lo que el enfoque en el método Invoke() debería ser el siguiente:
public async Task Invoke(HttpContext context)
{
    context.Response.OnStarting(() =>
    {
        TransformHeaders(context.Response.Headers, _transforms);
        return Task.FromResult(0);
    });
    await _next(context);
}
Con esto ya tenemos solucionado gran parte del problema. Si ejecutamos una petición a nuestra aplicación, los encabezados que obtenemos son los siguientes (fijaos que ha desaparecido el encabezado "Server" y que aparece "X-Author":
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Vary: Accept-Encoding
X-Author: Variable not found
X-Powered-By: ASP.NET
Date: Sun, 20 Dec 2015 11:21:11 GMT
Content-Length: 8011

Añadir el middleware UseHeaderTransform al pipeline

Con la definición del middleware que hemos visto ya podríamos añadirlo al pipeline usando los métodos genéricos Use() de IApplicationBuilder, de la siguiente forma:
app.UseMiddleware<HeaderTransformMiddleware>(transforms);
Obviamente no nos íbamos a quedar ahí ;) A continuación se muestra el código que permite llamar a UseHeaderTransform()  sobre IApplicationBuilder, simplificando la inserción de nuestro middleware al pipeline de ASP.NET:
namespace Microsoft.AspNet.Builder
{
    public static class HeaderTransformMiddlewareExtensions
    {
        public static IApplicationBuilder UseHeaderTransform(
            this IApplicationBuilder app, IDictionary<string, string> headerChanges)
        {
            app.UseMiddleware<HeaderTransformMiddleware>(headerChanges);
            return app;
        }
    }
}
De esta forma, ya podemos añadir el middleware como señores:
app.UseHeaderTransform(transforms);

Publicado en Variable not found.

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

Una sinfonía en C#

AngularJs paso a paso en 10 videos cortos

January 24, 2016 02:01 PM

Alentado por la posibilidad con la que contamos ahora los MVPs, hice una serie de videos sobre AngularJs de corta duración (entre 3  y 7 minutos) sobre los temás que considero más importantes. Acá van los links:

Introducción a AngularJs

Nos leemos!

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

Variable not found

Creación de enlaces con tag helpers de MVC 6

January 24, 2016 01:33 PM

ASP.NET CoreHace unos días hablamos de los tag helpers, esa interesante novedad de Core MVC  destinada a mejorar la legibilidad de nuestras vistas. Comentamos los que traía de serie el framework, y vimos por encima algunos ejemplos para hacernos una idea.

Hoy vamos a ver en profundidad uno de estos helpers, AnchorTagHelper, cuya misión es facilitarnos la creación de enlaces a controladores/acciones de nuestra aplicación. Para hacer más sencilla su comprensión, lo haremos mediante casos prácticos, y comparando cada ejemplo con la fórmulas que hemos usado tradicionalmente y que seguro conocéis, los helpers HTML.

<warning>ASP.NET aún se encuentra en desarrollo, por lo que detalles de lo que contemos por aquí aún podrían variar en la versión final</warning>

1. Enlace especificando controlador y acción

La sintaxis del tag helper permite codificar el hipervínculo con una etiqueta <a>, pero, en lugar de utilizar el atributo href para indicar el destino, utilizaremos los parámetros de servidor asp-controller y asp-action para especificar el controlador y acción, respectivamente. El valor del atributo href es calculado en servidor, y se emite en el resultado eliminando todo rastro de los parámetros del helper.

Tag helper <a asp-controller="Home" asp-action="Index">Link</a>
Html helper @Html.ActionLink("Link", "Index", "Home")
Resultado (ruta por defecto) <a href="/home/index">Link</a>

2. Enlace mediante ruta nombrada

Como en el caso de los helpers tradicionales, también es posible generar enlaces partiendo del nombre de una ruta presente en la tabla de rutas del sistema. En este caso, usamos el parámetro de servidor asp-route para especificar el nombre de la ruta.

Ruta (Startup.cs) opt.MapRoute(
      "profile",          // Route name
      "account/profile",  // Url
      new { controller = "Account",
            action = "Profile"
      }
);
Tag helper <a asp-route="profile">My profile</a>
Html helper @Html.RouteLink("My profile", "profile")
Resultado <a href="/account/profile">My profile</a>

3. Enlace con parámetros adicionales

En los ejemplos anteriores hemos visto cómo generar enlaces directos a acciones sin parámetros. Podemos añadir parámetros con cualquier nombre a la etiqueta utilizando el prefijo "asp-route-" seguido del nombre del parámetro.

Tag helper <a asp-controller="products" asp-action="edit"
   asp-route-id="18">Edit product</a>
Html helper @Html.ActionLink(
    "Edit product", "edit", "products", new { id = 18 }
)
Resultado (ruta por defecto) <a href="/products/edit/18">Edit product</a>

Como era de esperar, en los parámetros también podemos utilizar valores tomados del modelo o cualquier variable o expresión válida en el lado servidor utilizando la sintaxis habitual de Razor:

Tag helper <a asp-controller="products" asp-action="remove"
   asp-route-id="@Model.Id">Remove product</a>
Html helper @Html.ActionLink(
    "Remove product", "remove", "products",
    new { id = Model.Id }
)
Ejemplo de resultado
(ruta por defecto)
<a href="/products/remove/18">Remove product</a>

Y podemos incluir tantos parámetros como necesitemos:

Tag helper <a asp-controller="products" asp-action="edit"
   asp-route-id="@Model.Id"
   asp-route-categoryId="@Model.CategoryId" 
   asp-route-search="@Model.FromSearch">Edit</a>
Html helper @Html.ActionLink(
    "Edit", "edit", "products",
    new {
       id = Model.Id, 
       categoryId=Model.CategoryId, 
       search=Model.FromSearch 
    }
)
Ejemplo de resultado
(ruta por defecto)
<a href="/products/edit/18?categoryId=3&search=notebooks">
   Edit</a>

4. Enlace forzando el protocolo https

Por defecto se generarán enlaces relativos, por lo que el protocolo utilizado será el mismo que se haya indicado en la petición que está siendo procesado. Pero si queremos forzar un protocolo distinto, podemos hacerlo con el parámetro asp-protocol.

En este caso vemos la simplicidad y legibilidad con la que podemos indicar el protocolo de la dirección generada, sobre todo si lo comparamos con la complejidad de las interminables e ilegibles sobrecargas del helper ActionLink():

Tag helper <a asp-controller="home" asp-action="index"
   asp-protocol="https">Home</a>
Html helper @Html.ActionLink("Home", "index", "home", 
      "https", null, null, null, null)
Resultado (ruta por defecto) <a href="https://www.myserver.com/">Home</a >

El nombre del host generado, salvo que se indique lo contrario de la forma que veremos a continuación, será el mismo utilizado en la petición actual.

5. Enlace forzando el nombre del host

De la misma forma, podemos forzar el uso de un nombre de host distinto al utilizado en la petición actual utilizando el parámetro asp-host como sigue:

Tag helper
<a asp-controller="home" asp-action="index"
   asp-host="otherserver.com">Home</a>

Html helper
@Html.ActionLink("Home", "index", "home", 
      null, "otherserver.com", null, null, null)

Resultado (ruta por defecto) <a href="https://www.myserver.com/">Home</a >

El protocolo será el utilizado en la petición actual, salvo que indiquemos lo contrario con asp-protocol.

6. Enlace a un ancla (elemento con id) de la página

Si queremos ser muy específicos y dirigir el enlace hacia un elemento HTML concreto de la página destino, podemos añadir el identificador del mismo en el parámetro asp-fragment:

Tag helper
<a asp-controller="home" asp-action="index"
   asp-fragment="main">Home</a>

Html helper
@Html.ActionLink("Home", "index", "home",
      null, null, "main", null, null)

Resultado (ruta por defecto) <a href="/#main">Home</a>

Obviamente, estos tres últimos parámetros (asp-protocol, asp-host, y asp-fragment) pueden ser combinados para generar un enlace completo.

7. Enlace con atributos adicionales

¿Y qué ocurre si queremos introducir en nuestros links atributos adicionales, como identificadores, estilos, clases css o similares? Pues en este caso es donde los tag helpers muestran más claramente la superioridad de su sintaxis sobre los helpers tradicionales. Veámoslo con un ejemplo:

Tag helper <a asp-controller="products" asp-action="edit"
   asp-route-id="@Model.Id"
   id="mylink" class="productlink"
   style="font-weight: bold">Edit</a>
Html helper @Html.ActionLink("Edit", "edit", "products",
      new { id=Model.Id},
      new {
         id="mylink",
         @class="productlink",
         style="font-weight: bold"
      }
)
Resultado (ruta por defecto) <a id="mylink" class="productlink"
   style="font-weight: bold"
   href="/products/edit/18">Edit</a>

¡Y eso es todo! Espero que con estos ejemplos os quede claro su uso y veáis el potencial de esta nueva forma de codificar nuestras vistas, que tiene una pinta excelente :)

Publicado en Variable not found.

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

Blog Bitix

Aplicación web con Spark Framework y Java

January 24, 2016 11:00 AM

Spark Framework es un microframework web para Java que además hace uso de las novedades introducidas en la versión 8 del lenguaje. Para una aplicación no compleja o de un tamaño reducido permite desarrollar con su sencillez la funcionalidad de la aplicación inmediatamente y una arquitectura liviana.

Spark
Java

La cantidad de frameworks web disponibles en Java incluso para la misma tarea es notable, a veces cuesta decidirse por uno, sin embargo, tampoco es cuestión de elegir cualquiera. Deberemos evaluar las necesidades de la aplicación y las opciones que consideramos como adecuadas. Spark Framework es un microframework simple y sencillo, sin muchas de las funcionalidades de otras opciones más completas y complejas y que quizá no necesitemos. Su funcionalidad es suficiente capaz para servirnos en múltiples casos, uno de ellos es una API REST, otro una página web dinámica sencilla, prototipos funcionales, … su sencillez hace que podamos empezar a hacer cosas en poco tiempo. Spark es un framework muy sencillo al estilo de otros disponibles en otros lenguajes pero para la plataforma Java, ofrece soporte para las nuevas características introducidas en la versión 8 de Java que facilitan la tarea de programación como las lambdas.

Con la aparición de los dispositivos móviles algunas aplicaciones están cambiando su arquitectura. Para evitar duplicar funcionalidades si la aplicación es accedida mediante un navegador, una aplicación de escritorio, mediante un dispositivo móvil en el que queremos aprovecharnos de sus características nativas o queremos que una tercera parte se integre con la aplicación las aplicaciones, en vez de generar HTML en el servidor ofrecen una API REST con la que todos estos clientes se comunican para solicitar los datos o realizar las operaciones que necesitan, una vez que los clientes obtienen los datos estos presentan la información adecuadamente según el dispositivo.

En Java podemos desarrollar una interfaz REST usando RESTEasy pero también tenemos otras opciones, una Spark. En este artículo comentaré cómo hacer un ejemplo hola mundo usando Spark como opción si no necesitamos todas las funcionalidades que ofrecen otros frameworks más complejos y de mayor tamaño.

<noscript><pre><a href="https://gist.githubusercontent.com/picodotdev/3894e83348210dff422b/raw/HolaMundoSpark.java">HolaMundoSpark.java</pre></a></noscript>
<noscript><pre><a href="https://gist.githubusercontent.com/picodotdev/3894e83348210dff422b/raw/build.gradle">build.gradle</pre></a></noscript>

Spark ofrece un marco de trabajo en el que podemos crear rutas con las que asociar URLs con las acciones necesarias para devolver el resultado y las funcionalidades básicas de la parte de la interfaz web como acceso a la Request y Response, Cookies, Sessiones, Filtros, Redirecciones, manejo de excepciones o servir recursos estáticos y algunas integraciones para generar HTML con algunas librerías de plantillado como Thymeleaf, Freemarker o Mustache entre otras, la documentación en unas pocas horas se lee completamente.

Aunque en una aplicación REST no será necesario usar vistas con plantillas ya que el resultado más comúnmente empleado para proporcionar los datos es JSON probablemente sí que necesitemos usar persistencia en una base de datos con Hibernate o mejor aún con jOOQ o para facilitarnos la vida de programación queramos disponer de Inversion of Control e inyección de dependencias, la opción más común es emplear el contenedor de servicios Spring con la que además podremos proporcionar transacciones si nos conectamos a una base de datos relacional, seguridad también con Spring o con Shiro, Spring Boot como forma de iniciarlizar la aplicación, Spring Cloud Config para configuración en múltiples entornos, Spring Boot Actuator para obtener métricas… en definitiva tendremos libertad de elegir las librerías que consideremos adecuada para la tarea aunque la responsabilidad de realizar la integración será nuestra. Al final del artículo incluyo enlaces comentando varias de estas librerías específicamente.

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando el comando ./gradlew run.

En la dirección http://127.0.0.1:4567/hola obtendremos el mensaje de este ejemplo.

Otra opción usando Java u otros varios lenguajes para los que se ofrece soporte, basado en la programación reactiva y más escalable si llegamos a ese punto de necesidad es Vert.x que describo y muestro en otro ejemplo básico.

Referencia:
Ejemplo sencillo de servicio web con RESTEasy
Cliente javascript y java de servicio web REST con RESTEasy
Devolver xml, json o html con RESTEasy
Integración de Apache Tapestry con RESTEasy
Alternativa a Hibernate u ORM y ejemplo de jOOQ
Aplicación Java autocontenida con Spring Boot
Múltiples esquemas o bases de datos con jOOQ y Spring en Java
Obtener datos de múltiples tablas con jOOQ
Configuración de una aplicación en diferentes entornos con Spring Cloud Config
Información y métricas de la aplicación con Spring Boot Actuator
Steps toward the glory of REST
Hypertext Application Language

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

Jesús Perales

Convertir DVD a MP4 en Ubuntu

January 21, 2016 03:26 PM

Convertir DVD a MP4 en Ubuntu

Para convertir un DVD a MP4 comenzaremos por ver todos esos archivos raros que se encuentran dentro de la carpeta VIDEO_TS.

Para crear todo el vídeo completo es necesario unir todos los archivos .VOB en uno solo y posteriormente pasaremos a transformarlo en MP4.

Recomiendo copiar y pegar el contenido de la carpeta VIDEO_TS en un lugar accesible para la terminal, en mi caso el escritorio.

Convertir DVD a MP4 en Ubuntu

Utilizaremos el programa cat, que pertenece al paquete core utils, para unir todos esos archivos con el siguiente comando

cat *.VOB > movie.vob

La mayoría de las distribuciones GNU/Linux ya cuentan con este paquete instalado por defecto.

Convertir DVD a MP4 en Ubuntu

Podemos comprobar que se unieron todos los archivos VOB al encontrarnos con el archivo movie.vob que tiene un peso considerable, 1.3GB.

Convertir DVD a MP4 en Ubuntu

Ya unidos nuestros archivos pasaremos a convertir a MP4 utilizando HandBrake.

Lo podemos instalar usando el comando

sudo apt-get install handbrake

Al abrirlo solo debemos hacer click en Origen

Convertir DVD a MP4 en Ubuntu

Seleccionar el archivo movie.vob

Convertir DVD a MP4 en Ubuntu

Elegir el formato, en nuestro caso MPEG-4(avformat)

Convertir DVD a MP4 en Ubuntu

Y dar click en finalizar y esperar un largo momento a que termine el proceso.

Convertir DVD a MP4 en Ubuntu

Al final tendremos nuestro vídeo listo para subirse a Youtube o simplemente para tener un respaldo en caso de accidentes.

Convertir DVD a MP4 en Ubuntu

Como podemos ver es muy sencillo y lo único tedioso es la espera para que se termine de convertir, también pueden probar con VLC, aunque a mi no me funciono.

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

Jesús Perales

Primeros pasos con Spring #4, API REST

January 21, 2016 02:42 PM

Primeros pasos con Spring #4, API REST

En el articulo anterior vimos como guardar y buscar información en nuestra base de datos mediante Hibernate y JPA, utilizando un objeto DAO que igualmente es un servicio de Spring.

Ahora toca utilizar otro tipo de bean especial de Spring, llamado @RestController.

Esta clase será la encargada de atender las llamadas a nuestra API REST.

Para crear el controlador REST de Spring, damos click derecho en el proyecto y vamos a New -> Class

Primeros pasos con Spring #4, API REST

Lo llamaremos TodoController y su paquete será el com.todo.controller y damos click en Finish.

Primeros pasos con Spring #4, API REST

Agregaremos la anotación @RestController a la declaración de nuestra clase.

Primeros pasos con Spring #4, API REST

Y después la anotación @RequestMapping y le pasaremos un String con todos que será la ruta en la que este controlador responderá.

Primeros pasos con Spring #4, API REST

En nuestro controlador debemos inyectar el servicio de Spring que creamos anteriormente llamado TodoDao, para esto usaremos la anotación @AutoWired.

Primeros pasos con Spring #4, API REST

Ahora crearemos de uno por uno los métodos para guardar, buscar, actualizar y borrar nuestros Todos desde llamadas a nuestra API REST.

El método para guardar se llamara saveTodo, recibirá un Todo con la anotación @RequestBody y regresara un Todo, también tendrá la anotación RequestMapping y en lugar de pasar un String se pasara en su propiedad method el RequestMethod POST.

Dentro de el utilizaremos el TodoDao y lo retornaremos con su ejecución del método save pasando el Todo que recibimos.

Primeros pasos con Spring #4, API REST

Para probar que el método funciona podemos utilizar un extensión llamada restclient, de la siguiente forma.

Al abrir restclient vamos a la sección del menú Headers y damos click para desplegar lo y seleccionar Custom Header.

Primeros pasos con Spring #4, API REST

En name escribimos Content-Type y en value application/json, marcamos Save to favorite y presionamos okay

Primeros pasos con Spring #4, API REST

El valor de url muy comúnmente será http://localhost:8080/todo/api/todos/ , la cual está compuesta por:

  • localhost: dominio que redirige a nuestra IP local.

  • Puerto 8080: en el cual se ejecuta nuestro servidor de aplicaciones por defecto.

  • todo: el nombre de nuestra aplicación.

  • api: parte de la ruta que definimos para el Servlet MVC de Spring en el archivo web.xml, en las lineas 18-21.

  • todos: parte de la ruta que definimos en el RestController con la anotación @RequestMapping("todos").

Seleccionaremos el método Post ya que fue definido en el método saveTodo del RestController (@RequestMapping(method = RequestMethod.POST)).

Después en Body creamos un JSON con la siguiente estructura {"name": "test"} y damos click en SEND.

Deberíamos ver algo como la siguiente captura de pantalla.

Primeros pasos con Spring #4, API REST

Y en la consola de Eclipse debería registrarnos algo parecido a esto.

Primeros pasos con Spring #4, API REST

Con esta útil extensión podemos probar los métodos que estamos haciendo, para resumir el articulo omitiré las siguientes pruebas y me enfocare en la construcción de los métodos.

El método para buscar todos los Todos en nuestra aplicación se llamara getAllTodos, regresara una lista de Todos , también usara la anotación @RequestMapping, pero ahora utilizaremos el RequestMethod GET y regresara la ejecución del método findAll del TodoDao.

Primeros pasos con Spring #4, API REST

Crearemos un método para buscar Todos por id, este método se llamara getTodo, el cual tendrá la anotación @RequestMapping con el valor del RequestMethod igual a GET y una nueva propiedad llamada value con el siguiente String value = "/{id}" separado por coma, recibirá como parámetro un entero llamado id con la anotación @PathVariable con el String "id" y regresara la ejecución del método findById del TodoDao al cual le pasamos el entero id.

Primeros pasos con Spring #4, API REST

Para actualizar crearemos un método llamado updateTodo que estará anotado con @RequestMapping y el tipo de RequestMethod a usar será el PUT, el método recibe un Todo anotado con @RequestBody y regresa un Todo que se obtiene del método del TodoDao llamado update al que le pasaremos el Todo recibido.

Primeros pasos con Spring #4, API REST

Por ultimo nos queda el método para eliminar Todos de nuestra aplicación, deleteTodo, que tendrá la anotación @RequestMapping con la propiedad value de la siguiente forma value="/{id}" y el tipo de RequestMethod a usar sera el DELETE, este método recibe como parámetro un entero primitivo llamado id con la anotación @PathVariable pasando le un String "id" como argumento así @PathVariable("id") int id, regresara un Todo el cual obtendremos de la ejecución del método deleteById del TodoDao, al que le pasamos el id recibido como parámetro.

Primeros pasos con Spring #4, API REST

Es necesario aclarar que son y para que sirven las anotaciónes con las que estamos trabajando para tener un conocimiento mas amplio de lo que sucede en Spring:

  • @RestController: Una anotación de conveniencia que está a su vez anota con @Controller y @ResponseBody, es decir encapsula las anotaciones @Controller y @ResponseBody en una sola.

  • @Controller: Indica que una clase con esta anotación es un "controlador" (por ejemplo, un controlador web), mas información de los componentes de Spring aquí.

  • @ResponseBody: Anotación que indica que el valor de retorno del método debe estar en el cuerpo de una respuesta web.

  • @Autowired: Anotación para inyectar dependencias, existen algunas alternativas que podemos ver aquí.

  • @PathVariable: Anotación que indica que un parámetro del método debe estar dado por una variable de plantilla en la URL.

  • @RequestMapping: Sirve para mapear una determinada petición de un cliente a un controlador o a un método especifico ya sea por un String o por alguno de los diferentes métodos de HTTP que se encuentran en la clase ENUM RequestMethod.

  • @RequestBody: indica que un parámetro del método debe estar obligatoria mente en el cuerpo de la petición Web. El cuerpo de la petición se pasa a través de un HttpMessageConverter para resolver el argumento del método dependiendo del tipo de contenido de la petición.

Al finalizar este articulo ya tendiéramos nuestra API REST funcionando y tenemos conceptos básicos de como es el marco de trabajo con Spring, así como que son y para que sirven las anotaciones mas comunes en este tipo de proyectos.

Podemos decir que de cara a los desarrolladores que consumirán nuestra API, es sencilla, para mantenerla así es recomendable seguir esta guía de estilo para diseñar y desarrollar una API.

Algo que puede resultar confuso es por que cada vez que se reinicia el server los datos se pierden, bueno esto es debido a nuestra base de datos (H2) que mas que nada es de prueba, en próximos artículos detallare como cambiarla.

Otro dato curioso, es que segun esto podriamos utilizar PATCH en lugar de PUT, aun me resulta confuso decidir asi que lo e dejado con PUT.

El código del proyecto hasta el final de este articulo pueden descargarlo aquí, correcciones y comentarios son bienvenidos.

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

Picando Código

Super Mario Maker Wii U

January 21, 2016 11:00 AM

Super Mario MakerEn 2015 Nintendo lanzó el juego Super Mario Maker para Wii U. Se trata de un sistema de creación de niveles de Super Mario para Nintendo Wii U.

Crecí jugando juegos de Mario, empezando con su época inicial de 8 bits, pasando por varias versiones en consolas hogareñas y portátiles, hasta quién sabe cuántos bits hoy en día: Super Mario 3D World en Wii U es su más reciente encarnación y tiene un montón.

Así que la idea de poder crear mis niveles y compartirlos, además de poder jugar niveles creados por personas de todo el mundo me convenció enseguida.

El título lleva vendidas más de 1 millón de copias en Estados Unidos y más de 750.000 en Japón, siendo uno de los caballitos de batalla del Wii U durante la temporada de fin de año 2015. Y tiene sentido, al día de hoy los juegos de Mario son prácticamente universales.

El juego trae varias características similares a Mario Paint, juego que tuve en su época y en el que invertí muchas horas de entretenimiento con el mouse y mouse pad de Super Nintendo. Obviamente apela a la nostalgia de quiene crecimos con Mario, pero incluyendo suficientes elementos nuevos como para mantener fresca la fórmula (como viene haciendo Nintendo en general con los juegos de Mario).

El primer vistazo serio que tuve del juego fue durante la Nintendo World Championship 2015. Al estilo de la clásica película The Wizard (y como lo hizo en 1990), Nintendo realizó un evento de competición para establecer el “Campeón Mundial Nintendo 2015” durante la E3 de ese año.

El título final de la competencia fue Super Mario Maker, lo que sirvió a su vez como plataforma para mostrar su potencial al público. Les recomiendo que miren el video de esta última etapa de la competencia. En su momento lo miré en vivo y me entretuvo muchísimo. Si quieren una versión reducida, pueden ver este otro video que muestra un resumen con lo más destacado de la etapa de Super Mario Maker. Estos niveles están disponibles para jugar en nuestro Wii U :)

A pesar de que Nintendo le haya dado a la gente la posibilidad de “crear su propio Super Mario”, algo que se ve claro tras jugar muchos niveles es que los jugadores siempre van a comprar títulos de Super Mario lanzados por Nintendo. Los niveles y mundos diseñados por Shigeru Miyamoto y/o Takashi Tezuka tienen algo difícil de reproducir.

No he explotado mucho la parte de crear niveles, pero sí he jugado cientos de niveles de otras personas de distintas partes del mundo. Crear un nivel y que valga la pena es un trabajo complejo y que lleva tiempo. Tampoco le he dedicado tanto tiempo como quisiera al juego. Me he visto distraído por títulos como Wind Waker, Metroid Prime: Trilogy y ahora mucho Xenoblades Chronicles X.

De todas formas, al momento tengo publicados 3 niveles. Encontrarles nombre cuesta bastante, más sabiendo que los nombres que recuerdo son “World 1-1”, “Donut Plains” y poco más. Así que no me juzguen demasiado por los nombres…

Mi Primer nivel:

Super Mario Maker

18A1-0000-0063-F273

Fue mi primera prueba, empezando con una base del mundo 1-1 de Super Mario Bros. Tiene algún salto complicado y poco más. En el juego vamos desbloqueando cosas a medida que lo vamos jugando. Tanto los “motores” de los distintos Marios (Super Mario Bros., Super Mario Bros. 3, Super Mario World y New Super Mario Bros U) como los escenarios (castillo, subterráneos, bajo el agua, casa fantasma, etc.) e ítems.

Al ser mi primer nivel, no tenía demasiadas cosas disponibles así que no hace mucho uso de cosas raras.

Pueden jugarlo con el siguiente código:

18A1-0000-0063-F273

Mi segundo nivel

Super Mario Maker

3D6E-0000-00A3-5374

Ya con un poco más de juego arriba, pude crear un nivel bajo el agua. Se me ocurrió pensando en el juego Donkey Kong Country, pero creo que al final no quedó muy parecido a nada. Este nivel fue jugado en el canal de Twitch SuperTwoU en un stream de niveles de Mario Maker. Aparte de la experiencia de ver a alguien más jugando mi nivel en vivo por internet, fue divertido escuchar a los dos hermanos agonizar intentando pronunciar “picandocodigo” 😀

Pueden jugarlo con el siguiente código:

3D6E-0000-00A3-5374

 

Mi tercer nivel

Mario Maker

9A29-0000-00B7-CED3

Este nivel arrancó siendo como una versión remixada/homenaje al mundo 1-2 de Super Mario Bros. Fue el nivel que más tiempo me llevó, en el sentido que trabajé en él muchas veces a lo largo de un tiempo prolongado. Posiblemente un par de semanas, donde se me ocurrían ideas para implementarle cada tanto. Tiene más de un camino para llegar a la meta y creo que por lo que he visto cuando mis amigos lo juegan, es el más divertido de los 3.

Pueden jugarlo con el siguiente código:

9A29-0000-00B7-CED3

No he creado más niveles desde entonces, pero siempre está pendiente. Tras subir estos tres ya logré desbloquear todas las herramientas disponibles. Además han habido actualizaciones que traen cosas nuevas al mundo de Mario, y no he llegado a usarlas todavía.

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

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

Me gusta… de la informática

January 20, 2016 09:59 AM

Desde mis inicios, hasta la época del shareware, es decir, cuando podía dedicarle tiempo (y mucho) a lo que realmente me llamaba la atención, había determinadas tareas que me encantanban. En aquel momento no me lo había planteado, pero tenían mucho que ver con la programación de sistemas y la seguridad. Quizás por ser aspectos [...]

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

Picando Código

Gustavo Sala – El amor enferma

January 19, 2016 12:30 PM

El viernes pasado el artista argentino, filósofo y genio contemporáneo Gustavo Sala presentó en Montevideo su nuevo libro El Amor Enferma. La presentación se realizó en la librería Purpúrea en la Plaza del Entrevero por el centro de la ciudad. El local quedó un poco chico por la cantidad de gente que asistió a la presentación. Es una buena señal de que en Uruguay sabemos apreciar el inspirador arte de Sala.

Gustavo Sala - foto por Gerardo Carrasco Laino

Gustavo Sala – foto por Gerardo Carrasco Laino

Gustavo Sala es un historietista marplatense que dibuja y escribe sus tiras en varias publicaciones en Argentina, Uruguay y seguramente algún país más. Tiene un humor bastante degenerado, pornográfico y escatológico (algunos lo tildarían de “ofensivo”, pero porque no entienden lo que es el humor). Un tipo de humor que a mí particularmente me gusta mucho y el arte es ideal para este tipo de chistes.

Durante la presentación de El Amor Enferma, Gustavo Sala estuvo acompañado por el escritor uruguayo Ignacio Alcuri que ayudó a llevar adelante la presentación. Éstos dos están trabajando en un libro de humor gráfico que se las trae y que aparentemente va a estar disponible este año 😀

Hablando sobre cómo se gestó el libro, Sala comentó que todo empezó cuando una chica le rompió el corazón, y empezó a dibujar y publicar las tiras en un blog para dar lástima y hacer catársis. Más adelante la tira se empezó a publicar en la revista uruguaya Lento y así se fue generando el material para armar el libro.

Gustavo Sala y Nacho Alcuri

Gustavo Sala y Nacho Alcuri – foto por Gerardo Carrasco Laino

El libro contiene cosas algo distintas a lo que publica usualmente. La inspiración para estas tiras viene de un lugar diferente, basado en experiencias y sentimientos reales y cosas que escuchó o le pasaron a gente con la que habló. Las historietas son bastante personales y menos escatológicas (aunque alguna caca aparece) y con algunas me sentí demasiado identificado.

Sala comentó cómo hacer cosas así le resulta mejor como autor, al tener producciones diversas, se disfrutan más por contraste. Lo comparó con ver películas de géneros completamente diferentes y que a uno le gusten las dos. Ya terminé de leerlo y me encantó, y si bien me gusta mucho cuando se le va la moto y hace humor extremo, también disfruté mucho la faceta “romántica” del autor. Como decía, algunas tiras me resultaron hasta demasiado cercanas a nivel personal. Recomiendo mucho este libro al igual que todo el material publicado por Sala.

¡Viva la caca!

¡Viva la caca!

Terminada la presentación, hubo preguntas del público y posteriormente una gran parte de los asistentes procedimos a adquirir una copia de El Amor Enferma.

Sala se quedó firmando y dedicando ejemplares, y sumé a mi colección. Desde que adquirí Bola Triste de Sala en 2011, aprovecho sus visitas a Montevideo para adquirir algún otro título y pedirle una dedicatoria. Con la mejor predisposición del mundo, Sala se toma el tiempo de regalar una pequeña obra de arte a cada lector que le pide una firma.

En la librería también tenían ejemplares de un libro que había visto en internet pero no había podido comprar hasta ahora: ¡Viva la caca!. Lo leí en la librería misma y debo decir que este libro quedará en los anales de la historia como el pináculo de la literatura humana.

Es genial y muy divertido. Por lástima se terminaron todos los ejemplares, pero consideré comprar 2 o 3 copias para regalar a amigos y conocidos. Sumamente recomendable para quienes sabemos disfrutar del humor escatológico.

El evento estuvo muy divertido, como siempre es genial que se junte la dupla Sala Alcuri para obligarnos a salir de nuestras cuevas a reírnos con y de ellos un rato. Ahora a esperar ese libro que están armando en conjunto…

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

Arragonán

Semana 397

January 18, 2016 05:37 PM

Volviendo a la normalidad, ya salió una semana que pude sacar bastantes cosas. Incluido el recopilar las facturas para cumplir con la declaración trimestral.

Estuve trabajando en gestionar leads que fuero llegando después de navidades, respondiendo varios correos y hablando sobre un proyecto que parecía interesante, pero no nos terminó de encajar por varias cosas (tiempos, tecnologías…) y recomendamos a gente que pensamos que quizás pueda ayudarles.

Además estuvimos trabajando (yo la verdad que muy poco) en redactar el presupuesto formal para el posible proyecto sobre el que llevamos un tiempo trabajando.

En cuanto a lo que hay en marcha:

  • Hubo que hacer algún retoque en One-step, también en la configuración del despliegue. Así que volvemos a estar a las espera del OK del cliente final.
  • Con Maubic empecé la semana con poca dedicación dando soporte al equipo. Y acabé volviendo a una dedicación que venía siendo más habitual en semanas anteriores, para implementar y desplegar en staging un nuevo servicio y tener una sesión de trabajo para definir las siguientes tareas a realizar.
  • Continuamos trabajando en los cambios de Bichomanía, quedó pendiente resolver algunos recursos gráficos para poder lanzar los cambios de la home.
  • Como la semana empezó tranquila, aproveché para dedicar algo de tiempo a Mosica. Para coordinar y publicar un post resumen de conciertos en 2015 (como de costumbre, estas cosas dan más trabajo de lo que parece) y dedicarle algo de tiempo a hacer unas mejoras en la aplicación móvil, pero que se quedaron a medias.
  • Tuvimos una reunión para hablar del diseño visual de Outreach Tool, que va a quedar un poco a la espera de tener listos los prototipos de productos físicos.

Buena semana.

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

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

Shareware

January 18, 2016 12:50 PM

Hoy vuelvo con las batallitas del abuelo cebolleta, esta vez para hablaros del shareware. En 1982, Jim “Button” Knopf, crea PC-File, y decide distribuirlo como “user-supported software”, o sea “software soportado por el usuario“. Poco después, Bob Wallace crea PC-Write y lo distribuye como shareware. Ambos términos quieren decir lo mismo. El autor cedía una [...]

» 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