lunes, 25 de abril de 2011

Almacenamiento Local HTML5

Una de las (pocas) cosas que se echa de menos al desarrollar aplicaciones para la web es la persistencia. En esencia, el protocolo HTML es unidireccional (es decir, el servidor, a partir de una petición del navegador, genera una respuesta/página) y no guarda información de estado (es decir, no hay un "recuerdo" entre una página y la siguiente).

Esa es la teoría. En la práctica, con los años se han ido desarrollando mecanismos que permiten almacenar cierta información de manera persistente. En el lado del navegador (las cookies) y en el lado del servidor (las sesiones). Pero no son la solución real al problema de la persistencia. Las cookies tienen un límite de tamaño de 4KB y, además, se envían con cada petición que se hace al servidor, así que no es muy recomendable usarlas para almacenar demasiados datos. Por otro lado, el manejo de sesiones puede complicarse cuando tenemos múltiples frontales, ya que hay que habilitar algún mecanismo para que compartan dicha información.

Con las nuevas características de almacenamiento local de los navegadores modernos, eliminamos todos estos problemas de un plumazo.

En resumen:
  • Disponemos de 5MB de almacenamiento local por dominio, en el que podremos guardar pares clave-valor. Tanto la clave como el valor son cadenas de texto (así que cuidado a la hora de almacenar otros tipos de datos, ya que tendremos que realizar las conversiones adecuadas).
  • El almacenamiento es persistente, incluso al cerrar el navegador o cuando el navegador se cuelga o se cierra debido a un error.
  • Los datos no se envían al servidor bajo ningún concepto (salvo que el usuario los envíe).
  • Disponemos de getters y setters para cada clave, así como métodos para borrar el valor de una clave, consultar cuántos elementos tenemos almacenados o borrar todo el almacenamiento.
  • Cada vez que se hace una operación sobre el almacenamiento local y dicha operación suponga un cambio (entendiendo como que una operación que escribe en una clave el mismo valor que ya tenía, no lo produce) dispara un evento, que podemos escuchar y actuar en consecuencia.
  • Podemos utilizar el almacenamiento local para comunicar varias ventanas del navegador (en las que se ha abierto la misma aplicación web).
Hay una versión de almacenamiento local, llamada Almacenamiento de Sessión (session storage) que funciona exactamente igual salvo por el hecho de que, al terminar la sesión (por ejemplo, cerrando el navegador), todos los datos se pierden.

Como siempre, deberíamos detectar si el navegador usado dispone de estas características antes de usarlas.

Enlaces interesantes:

Web. Detección de características avanzadas

Últimamente parece que las cosas se mueven a buen ritmo en el mundo del HTML. Sin embargo, los avances en las recomendaciones para HTML5 (y CSS3) hacen que algunas características interesantes estén disponibles en unas versiones de los navegadores y no en otras. Lamentablemente, no todo el mundo puede tener su navegador (o navegadores) actualizados a la última. O bien, que usan versiones obsoletas del navegador (me refiero a la gente que anda todavía con IE6, cuando incluso Microsoft se está poniendo las pilas con las últimas versiones de su navegador.

Para todos estos casos, viene bien tener algún método rápido y más o menos estándar de saber qué características etnemos disponibles.

Una librería muy interesante que nos permite conseguir este propósito es Modernizr.

Tenemos que tener en cuenta que Modernizr únicamente detecta qué características están disponibles; no ofrece una implementación alternativa a las mismas para aquellos navegadores que no las soporten. Para eso ya nos tenemos que buscar las mañas por otro lado.

Geolocalización (W3C API)

Uno de los temas que están "de moda" desde la explosión de uso de Internet en los móviles ha sido la geolocalización.

La distribución de direcciones IP no es como la de los números de teléfono, en los que mirando el antiguo prefijo (las 2 ó 3 primeras cifras) o incluso las siguientes, nos podemos hacer una idea de dónde está instalada la línea. La asignación de direcciones IP es bastante más arbitraria. No obstante, existen bases de datos que pretenden localizar un dispositivo a partir de su dirección IP (dependiendo de los países la localización será más o menos exacta).

Con los móviles la cosa cambia. Se puede conocer su posición a partir de tres elementos:

  • La red WiFi a la que esté conectado.
  • La celda móvil a la que esté conectado.
  • Su GPS interno.

Dependiendo de los métodos usados la precisión será mayor o menor.

En cualquier caso, la API de geolocalización enmascara todos estos procesos y nos da acceso asíncrono a la información de geolocalización del dispositivo a través del navegador. Esta característica no sólo está en los navegadores móviles, sino también en los de "sobremesa" (al menos en Chrome, Firefox y Opera).

La API para obtener la información consiste básicamente en un objeto, navigator.geolocation, que provee de un método, navigator.geolocation.getCurrentPosition(), que recibe como parámetros dos funciones callback: la primera de ellas para manejar el éxito de la geolocalización, y la segunda para manejo de errores.

La función de éxito recibirá un objeto position que tiene una propiedad coords con valores interesantes (creo que los nombres son autoexplicativos):

  • latitude
  • longitude
  • altitude
  • accuracy
  • altitudeAccuracy
  • heading
  • speed

A partir de dichos valores, es sencillo representar nuestra posición en un mapa, usando la API Javascript de Google Maps. Se pueden ver un par de ejemplos en Labs:


En el ejemplo, se pretende usar un timeout para actualizar la posición, pero leyendo más a fondo la documentación hay un método watchPosition() que permite hacer lo propio. También existe el método clearWatch() que se emplea para concluir el seguimiento.

Enlaces relacionados:

martes, 19 de abril de 2011

Cómo instalar Nginx y PHP-FPM en Ubuntu 10.10

Voy a hacer una breve anotación a este respecto, ya que es un tema que he tenido que buscar un par de veces.

En realidad, se trata de una tarea que, a día de hoy, es sencilla. Hace meses, no había repositorios oficiales para PHP-FPM, y había que andar compilándolo o bien usar repositorios alternativos. No obstante, desde la versión 10.10 de Ubuntu, PHP-FPM se encuentra en los repositorios oficiales. Sólo hay que hace algún pequeño ajuste en la configuración, que veremos a continuación.

En nuestros proyectos ya llevamos tiempo confiando en la dupla Nginx + PHP-FPM, con buenos resultados. Apache(2) pronto será cosa del pasado.

1. Instalamos Nginx:

apt-get install nginx


No será la última versión, pero nos valdrá. Si queremos la última (Nginx acaba de lanzar su versión 1.0) debemos instalarla desde la web oficial.

2. Instalamos PHP-FPM:

apt-get install php-fpm


PHP-FPM se ejecuta como servicio (demonio) escuchando por defecto el puerto 9000.

3. Configuramos Nginx para que envíe las peticiones a PHP-FPM. Ésta es la parte más "complicada" del invento. En el fichero /etc/nginx/nginx.conf descomentamos las líneas correspondientes:

location ~ \.php$ {
    fastcgi_pass 127.0.0.1:9000;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME /var/www$fastcgi_script_name;
    include fastcgi_params;
}



Donde /var/www es el directorio raíz del servidor web, y tenemos que tener cuidado en separar las palabras include fastcgi_params, que en el fichero de configuración por defecto vienen juntas por error.

Otro cambio que puede ser útil, si queremos que el servidor web responda a varios dominios sin redirigir al primero de ellos (que es el comportamiento por defecto), consiste en añadir a la configuración el parámetro server_name_in_redirect off.

4. Reiniciamos NGinx:

/etc/init.d/nginx restart



5. Añadimos extensiones a PHP. Aquí, a gusto del cosumidor. Por ejemplo:

apt-get install php5-mysql php5-curl php5-gd php5-idn php-pear php5-imagick php5-imap php5-mcrypt php5-memcache php5-mhash php5-ming php5-ps php5-pspell php5-recode php5-snmp php5-sqlite php5-tidy php5-xmlrpc php5-xsl php5-json



Si añadimos extensiones, debemos reiniciar PHP-FPM:

/etc/init.d/php-fpm restart



Y ya está. Si creamos un fichero info.php con el código:

<?php
phpinfo();
?>



Y accedemos desde nuestro navegador, deberíamos ver las opciones de PHP que hemos instalado.

Fuente principal: http://www.howtoforge.com/installing-nginx-with-php5-and-php-fpm-and-mysql-support-on-ubuntu-10.10.

jueves, 14 de abril de 2011

@font-face

Cuando hicimos el último rediseño de la web, en noviembre de 2010, una de sus señas de identidad estética fue el uso de la fuente American Typewriter en algunas zonas, principalmente títulos de secciones y de módulos.


Dicha fuente no es una fuente estándar. Por tanto, para implementarla, la empresa encargada de ayudarnos con el rediseño nos aconsejó el uso de la librería Cufon. Cufon es una librería Javascript que se encarga de, una vez cargado el HTML, sustituir las fuentes por las elegidas, empleando componentes canvas y VML.


Así lo hicimos, porque andábamos liados con mil cosas. El resultado quedó aparente, pero no estábamos del todo satisfechos.


Lo que ha encendido la mecha ha sido ver que Cufon no funcionaba con IE9. Bastaba con actualizarlo para solucionar el problema, pero empezamos a buscar alternativas, y fue cuando rescatamos del olvido @font-face.


Siguiendo un tutorial de los muchos que podemos encontrar, hemos implementado @font-face en nuestra web. Se trata de una característica de CSS, propuesta en la versión 2, y que soportan la gran mayoría de los navegadores. Básicamente, definimos cada familia de fuentes que queramos usar, indicando dónde están los ficheros para obtenerlas. Cada navegador utiliza un formato diferente. Una vez definidas las familias, pueden emplearse en la hoja de estilos como si fueran fuentes estándar.


El único inconveniente que hemos encontrado es que las fuentes deben estar alojadas en el mismo subdominio que la web, salvo que empleemos una cabecera especial para servirla desde nuestros servidores de estáticos. De momento hemos optado por que sean los frontales quienes las sirvan.


Ahora las fuentes especiales se muestran más rápidamente y sin necesidad de Javascript, por lo que conseguimos una librería menos que descargar al acceder a la página.

martes, 12 de abril de 2011

Implementando un hotfolder en Linux

Una de las ventajas de la consola de UNIX, en general, y de Linux, en particular, es la facilidad de procesar por lotes multitud de ficheros. Eso está bien cuando tenemos todos los ficheros disponibles para ser procesados. Pero, ¿qué pasa cuando queremos montar un "hotfolder", es decir, un directorio donde los usuarios irán dejando sus ficheros para que vayan siendo procesados.


El reto principal consiste en saber cuándo el fichero se ha terminado de enviar, para pasar a procesarlo. Si lo enviáramos mediante un sistema construido a medida, se podría monitorizar el envío y generar un mensaje al finalizar, o implementar cualquier otro mecanismo.


En nuestro caso, queremos que los usuarios suban los ficheros por FTP, con lo cual no podemos añadir ningún mecanismo extra de control a la subida, teniendo que monitorizar el directorio por nuestros propios medios.


Buscando en Internet, hemos encontrados dos métodos que pueden ser útiles para lo que queremos conseguir:


Chequear el tamaño del fichero o si está abierto por algún proceso [1]. Podemos usar el comando fuser. Si el fichero no está en uso, no devolverá salida alguna, mientras que si lo está, nos dirá qué proceso lo está usando. En nuestro caso, el fichero estaría abierto por el servidor de FTP hasta que se complete la transferencia.


Monitorizar el uso de un fichero [2]. Mediante el uso de una utilidad como inotifywait, incluida en el paquete inotify-tools, podemos monitorizar, por ejemplo, cuándo se cierra un fichero en un directorio dado (también se puede hacer recursivamente) y, en el momento en que se produzca el evento, lanzar la ejecución de un proceso (en nuestro caso, el que procesa el fichero).


[1] http://www.unix.com/shell-programming-scripting/153036-check-if-file-finished-copy.html


[2] http://linux.die.net/man/1/inotifywait