Construyendo un Servicio en OSX

enero 16, 2011

Uno de los aspectos que pasan mas desapercibidos en OSX son los “Servicios”. Seguramente por el nombre pensareis que es un programa que se ejecuta de fondo en nuestro ordenador.

Si bien ese pensamiento no es del todo incorrecto, los servicios en OSX es algo mas. Con servicios me refiero a los elementos del menú que sale cuando pinchamos sobre el nombre del programa activo en la barra de menús.

La localización de dicho menú ha ido cambiando con el tiempo, en versiones anteriores ocupaban una posición mas destacada, pero poco a poco han ido pasando a un plano mas “oculto”.

Sin embargo, la posición de esta funcionalidad no nos tiene que despistar, ya que la utilidad que prestan es bastante buena.

Para comprobar su funcionamiento, solo tenemos que seleccionar algo de texto, e ir al menú Servicios. Desde dicho menú se nos dará la opción de hacer diversas cosas, como por ejemplo, crear una nota. Además no solo los servicios están accesibles desde este menú, si no que podremos acceder usando atajos de teclado. Por ejemplo, si pulsamos SHIFT+ ⌘+Y, crearemos la nota al instante.

Una de las grandes ventajas de los servicios es que están disponibles para todo el sistema, esto es, independientemente de la aplicación donde seleccionemos el texto, si pulsamos el atajo de teclado, crearemos una nota con el texto seleccionado.

Como podemos leer en la propia documentación de Apple, posibles servicios serian, reconocedores de texto en una imagen, cifrado de texto o incluso, aunque no venga en la guía de Apple, sería útil un servicio para insertar una cita de Chuck Norris en el documento actual 🙂

Desde el punto de vista de la programación, se pueden distinguir dos elementos, por un lado el propio servicio, y por otro la aplicación que interactua con él. La pieza que intercomunica ambas partes es el clipboard. Cuando pinchamos en un servicio, la aplicación pone los datos necesarios en el clipboard, e invoca al servicio. El servicio por su parte, recoge dichos datos, los procesa y los vuelve a poner en el clipboard. Finalmente, la aplicación recoge lo que haya en el clipboard y hace lo que considere oportuno con dichos datos.

En la entrada vamos a crear un servicio, que seleccionado texto, lo consulte en la wikipedia usando Safari.

El código que atiende al servicio, es sencillo, a grandes rasgos, los pasos que hay que dar son por un lado, crear el método al que se llamará, y por otro, registrar el servicio en el sistema.

Desafortunadamente, xcode no tiene plantillas para crear los servicios, por lo que tendremos que empezar desde un proyecto vacío que cree un ejecutable.

Una vez que tenemos el proyecto creado en el XCode, debemos añadir (si no lo estaba antes), el framework de Cocoa y Foundation, y añadir la clase que implementará el método que atiende el servicio.

En nuestro caso, dicha clase es WikipediaService


==== WikipediaService.h

#import <Cocoa/Cocoa.h>


@interface WikipediaService : NSObject {

}

- (void) wikipediaSearch: (NSPasteboard*) pboard 
				userData:(NSString*) theUserData 
				   error:(NSString**) theError;

@end

==== WikipediaService.m

@implementation WikipediaService

- (void) wikipediaSearch: (NSPasteboard*) pboard userData:(NSString*) theUserData error:(NSString**) theError
{
	// Test for strings on the pasteboard.
	NSArray *classes = [NSArray arrayWithObject:[NSString class]];
	NSDictionary *options = [NSDictionary dictionary];
	
	if (![pboard canReadObjectForClasses:classes options:options]) {
		*theError = NSLocalizedString(@"Error recuperando datos del pboard", 
									  @"pboard couldn't give string.");
		return;
	}
	
	// Get and encrypt the string.
	NSString *pboardString = [pboard stringForType:NSPasteboardTypeString];
	NSString *wikipediaString = [NSString stringWithFormat:@"http://en.wikipedia.org/wiki/%@", pboardString];
	NSURL *url = [[NSURL alloc] initWithString: wikipediaString];
	[[NSWorkspace sharedWorkspace] openURL:url];
	
	[pboard clearContents];
}

@end

Como vemos, el clipboard es la parte que comunica ambos procesos. Con el mensaje canReadObjectForClasses primero comprobamos si el clipboard contiene datos de tipo cadena, y luego con el mensaje stringForType del “pasteboard” obtenemos dicha cadena.

Por otro lado, en nuestro punto de entrada de la aplicación, debemos situar el código que registra el servicio en el sistema.

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

	NSRegisterServicesProvider([WikipediaService new], @"WikipediaService");
	NSUpdateDynamicServices();
	[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
	return 0;
}

Antes de ejecutar y probar nuestro servicio, nos queda una parte muy importante. En el fichero plist de la aplicación, debemos indicar los datos referentes al servicio. Que indicará al sistema como llamar y como mostrar la información asociada.

	<key>NSServices</key>
	<array>
		<dict>
			<key>NSMenuItem</key>
			<dict>
				<key>default</key>
				<string>Wikipedia Search</string>
			</dict>
			
			<key>NSKeyEquivalent</key>
            <dict>
                <key>default</key>
                <string>R</string>
            </dict>
			
			<key>NSMessage</key>
			<string>wikipediaSearch</string>
			
			<key>NSPortName</key>
			<string>WikipediaService</string>
			
			<key>NSSendTypes</key>
			<array>
				<string>NSStringPboardType</string>
				<string>NSPasteboardTypeString</string>
			</array>			
		</dict>
	</array>

Si os fijáis, en el campo, NSKeyEquivalent, podemos asignar una tecla, que sumada a Command, sera el shortcut para llamar a nuestro servicio. Si dicha letra esta en mayúsculas, ademas necesitaremos pulsar Shift.

Con todo ya terminado, solo nos queda instalarlo, para ello debemos copiar el “Bundle” que ha generado nuestro proyecto en XCode a ~/Library/Services. Para que el servicio sea reconocido, debemos cerrar sesion y volverla a inciar, pero existe una forma manual para forzar al sistema para que reconstruya el indice de servicios.

Esto es ejecutando el siguiente comando: /System/Library/CoreServices/pbs

Una vez que tenemos nuestro servicio instalado, solo tenemos que seleccionar cualquier texto para buscarlo en la wikipedia.


Manejando Safari desde Objetive-C

enero 2, 2011

Recientemente me surgió la necesidad de controlar remotamente un explorador web en Windows. En el sistema operativo de Microsoft, gracias a los objetos COM, es posible “instanciar” un objeto InternetExplorer y controlarlo desde código fuente. Gracias a .NET y la Interoperabilidad con los objetos COM, la tarea inicialmente no parece ser para nada complicada, aunque como la compañía de Redmond nos tiene acostumbrados, lidiar con los detalles será una tarea que nos llevará bastante tiempo.

En OSX por su parte este objetivo también esta cubierto, y como pasa habitualmente, de una forma bastante más elegante :D.

La idea es poder controlar, y crear instancias de una aplicación que esta ejecutandose, o que vamos a iniciar. Para ello, como requisito imprescindible, la aplicación debe estar preparada para ser manejada.

El componente clave para que todo esto se pueda realizar son los denominados “Apple Event”, que son paquetes de información que se envían en forma de mensaje entre las diferente aplicaciones que se están ejecutando en el Sistema Operativo.

Por ejemplo, si queremos que el Safari en ejecución navegue a una URL, y eso lo queremos hacer desde nuestra aplicación, deberemos enviar un Apple Event que contenga los datos necesarios para llevar a cabo esa acción.

Originalmente, solo era posible enviar eventos usando AppleScript, de hecho, el lenguaje se creo alrededor de dichos eventos. Pero hoy en día se puede utilizar multitud de lenguajes para enviar “Apple Events” como Python, C#, Ruby o Javascript.

Lo que hace posible esta interoperabilidad de lenguajes es lo que se conoce como el Scripting Bridge, como ya hablamos en el blog cuando queríamos usar Python para controlar el iTunes.

A diferencia de la entrada anterior, en esta entrada, vamos a usar Objecitve-C para controlar el Navegador Web, dado que ObjC es un lenguaje compilado, necesitamos tener una referencia de lo que podemos hacer con el “objeto” Safari, de tal manera que podamos compilar nuestro código.

Para ello usamos el comando sdp, que generará un fichero .h y que usaremos para compilar nuestro código. La entrada del comando sdp son los ficheros .sdef, que contienen, en forma de XML, una descripción sobre lo que se puede hacer con el objeto en cuestión.

A nivel de controlar el Navegador, una de las cosas mas utiles es que podemos ejecutar código javascript, con lo que tenemos acceso a todo el documento web que estamos visualizando.

A continuación os dejo un fragmento de código que navega a google, realiza una busqueda usando javascript y muestra el numero de resultados encontrados.

SafariApplication* safari = [SBApplication 
  applicationWithBundleIdentifier:@"com.apple.Safari"];
	
SBElementArray* windows = [safari windows];
SafariTab* currentTab = [[windows objectAtIndex: 0] currentTab];
	
[currentTab setURL:@"http://www.google.com"];
[safari doJavaScript: 
 @"document.getElementsByName('q')[0].value = 'Safari'" 
				  in:currentTab];
[safari doJavaScript: @"document.forms[0].submit()" 
				  in: currentTab];
id result = [safari 
   doJavaScript:@"document.getElementById('resultStats').firstChild.data"
           in:currentTab];
NSLog(@"Results: %@", (NSString*)result);

Un uso en el que esta técnica es útil es cuando queremos hacer pruebas automatizadas de alguna web que estemos realizando. Aunque para estas tareas existe selenium, que si utilizamos firefox, seguramente cubra nuestras necesidades.


Anillo de Mensajes en Erlang

diciembre 24, 2010

Desde hace algún tiempo, llevo investigando el mundo de la programación funcional. Después de probar varios lenguajes como F#, o Clojure, finalmente me decanté por Erlang.

La verdad es que estoy pasandomelo bien recordando los viejos tiempos de Lisp o Prolog :), y sobre todo aprendiendo una forma diferente de afrontar los problemas.

Para el aprendizaje, mi libro de cabecera es Programming Erlang de la editorial Pragmatic Bookshelf. Libro que me esta gustando bastante, ya que asume que no tienes ni idea de programación funcional, como era mi caso.

Quizás para otro post os pueda contar algo de los beneficios que tiene la programación funcional, como por ejemplo, lo bien que escalan en entornos multiprocesador y lo robustos que son para entornos multihilo, gracias a la eliminación de los “side-effects”.

En dicho libro se plantea un ejercio en el que se pide que se cree un “anillo” de M procesos, de tal manera que enviando un mensaje a uno de esos procesos, dicho mensaje circule por el anillo N veces.

Una vez que se tiene la estructura, se pide que se mida el tiempo que tarda en circular los M*N mensajes, se compare con lo mismo hecho en otro lenguaje de programación (preferiblemente no funcional) y después que se publique en un blog.

Así que obediente de mi, aqui os dejo la primera parte, el ejercicio hecho en Erlang. Para otra entrada dejo la comparación con lo mismo hecho en otro lenguaje 🙂

-module(ring).
-export([ringtest/2, ringNode/1]).

ringNode(PPid) ->
	receive
		{exit} ->
			PPid!{exit},
			true;
		{0} ->
			PPid!{exit},
			true;
		{N} ->
			PPid!{(N-1)},
			ringNode(PPid)
	end.

ringtest(N, M) ->
	ringtest(N-2, self(), N*(M-1)).

ringtest(0, ThisPid, M) ->
	Pid = spawn(ring, ringNode, [ThisPid]),
	Pid!{M},
	ringNode(Pid);
ringtest(N, ThisPid, M) ->
	Pid = spawn(ring, ringNode, [ThisPid]),
	ringtest(N-1, Pid, M).

Analizando los tiempos (que están en microsegundos), lo cierto es que erlang es bastante rápido, tanto en crear procesos como en enviar mensajes entre ellos. El ordenador que ejecuto el programa era un ATOM D525 de 1.8Ghz y en crear 5000 procesos, y enviar el mensaje 100 veces alrededor del anillo, sólo tardó 1,2 segundos. Sin duda, una cifra bastante interesante.

Despueés de ver estos números, esta claro que no es casulidad que sea una de las plataformas preferidas a la hora de desarrollar aplicaciones que se muevan en estos tipos de entornos.

No soy ningun experto de Erlang, de hecho aun estoy aprendiendo, así que alguien lo lee y tiene algún comentario, serán bienvenidos!


Objetos distribuidos en Cocoa usando NSConnection y NSProxy

diciembre 11, 2010

Cuando nuestros programas crecen lo suficiente, nos encontramos con situaciones donde tenemos varios hilos de ejecución o incluso diferentes procesos que se ejecutan de forma paralela en la misma o en diferente máquina. En estas situaciones, es habitual tener que comunicar estos diferentes módulos.

Una típica forma de comunicación es el uso de objetos “distribuidos”, desde los vetustos (y terroríficos :)) objetos CORBA, pasando por RMI de Java o Remoting de .NET, hasta los mas modernos WebServices, estos mecanismos han sido y son ampliamente utilizados.

Explicado de una forma muy simple, estos mecanismos permiten llamar a un método de una instancia de un objeto que no se esta ejecutando en el mismo entorno que el llamador. Ese entorno puede ser otro hilo, otro proceso u otra máquina.

Como era de esperar, Cocoa plantea su aproximación al uso de objetos distribuidos. Como hemos hablado anteriormente en el blog, el mecanismo de invocación de métodos en Objetive-c basado en mensajes, ayuda a entender y a enfocar de otra manera el funcionamiento de la invocación remota de métodos de objetos.

En un lenguaje como Java, cuando invocas a un método remoto, realmente estas invocando a un método que tiene que existir en una clase “local”. Esta clase “local” habitualmente es un Proxy que se autogenera estáticamente. El Proxy por su parte, transforma la llamada en una comunicación con el servidor remoto, que se encarga de invocar el método y devolver el retorno.

En Objetive-C, una llamada a un objeto remoto, se traduce en un mensaje que se “enruta” hasta llegar al objeto receptor. En este caso, el Proxy no es necesario autogenerarlo, pues como hablamos, el Proxy será un objeto que es capaz de recibir en tiempo de ejecución cualquier tipo de mensaje. No tenemos la necesidad de que el método realmente exista para poder compilar nuestro código.

Las clases que entran en juego son numerosas, y el funcionamiento completo tiene su “intríngulis” :), por lo que en este post nos centraremos en el aspecto mas práctico, es decir, poner un ejemplo de como funciona.

El código se divide en dos partes, por un lado esta el objeto que va e estar ejecutandose en modo “servidor” y por otro, el lado del cliente.

Hablando del servidor, vamos a tener un objeto de tipo “Calculadora” que va a tener un método “addNumber:with:”, que como dice su nombre, sumará dos números. A este objeto lo vamos a llamar de forma remota.

@protocol CalculatorProtocol
- (int) addNumber:(int) a with:(int)b;
@end

@interface Calculator : NSObject<CalculatorProtocol> {
}
@end

El protocolo, nos va a ayudar a que el proxy sepa que el método existe. Realmente esto no es estrictamente necesario, ya que el mensaje se enrutará de todas maneras hasta llegar a la clase Calculator, pero sin embargo es una buena ayuda de cara a la comprobación que el runtime hace para saber si la clase remota responde o no al mensaje.

Una vez que tenemos el objeto que queremos exportar, necesitamos publicarlo, o como se llama en la documentación: “Vending an Object”.

Para ello, vamos a hacer uso de la clase NSConnection

Calculator* calculator = [[Calculator alloc] init];
NSConnection* theConnection = [[[NSConnection alloc] init] autorelease];
[theConnection setRootObject:calculator];
[theConnection registerName:@"calculator"];

[[NSRunLoop currentRunLoop] run]; 
 // Solo si nuestra aplicación no lo ha hecho ya
 // En caso de una aplicación Cocoa, esto no es necesario

Con esas 5 lineas, nuestro Servidor ya estará escuchando peticiones, y el objeto estará listo para usarse desde otro entorno de ejecución, fácil, ¿no?.

La parte cliente, no es que sea mucho mas compleja 😉

id theProxy;
theProxy = [[NSConnection rootProxyForConnectionWithRegisteredName:@"calculator" host:nil] retain];
[theProxy setProtocolForProxy:@protocol(CalculatorProtocol)];
NSLog(@"%d", [theProxy addNumber:5 with:3]);

Una vez mas el objeto NSConnection nos va a servir para obtener la conexión al objeto remoto. Usamos la misma cadena que se utilizó en el registro, y pasamos “nil” al parámetro host, ya que en este ejemplo, el servidor esta en la misma máquina.

Ambos fragmentos de código se ejecutan en procesos diferentes, y como se espera, el resultado del cliente es imprimir un 8 en la pantalla de logs.

Sin duda, el post no ataca toda la complejidad que hay detrás de los mecanismos, y las diferentes alternativas que existen de cara a la configuración de la conexión u otros aspectos que entran en juego, pero aún así se puede ver lo sencillo que es invocar un mensaje sobre un objeto que esta ejecutandose fuera de nuestro proceso.


E4X, Javascript y XML

noviembre 13, 2010

Hace un tiempo, acceder a documentos XML era perfectamnete posible, pero había que pasar por APIs que quizás no eran todo lo sencillas que deberían ser.

Las dos principales APIs que nos servían para procesar un documento XML eran DOM y SAX. Los nombres describían la forma de acceder al documento, mientras que en DOM se cargaba todo el documento en memoria, y podiamos acceder de una forma más sencilla, en SAX registrabamos una serie de “callbacks” a los que se nos iba llamando segun se procesaba el documento.

Sin duda DOM era mas sencilla desde el punto de vista del uso, pero menos eficiente, si nuestro documento era muy grande, necesitabamos tenerlo completamente en memoria. Por su lado SAX era más eficiente, pero la forma de desarrollar con él era bastante incomoda, ya que teniamos que separar nuestro código en multiples funciones de callback y nosotros teniamos que ser responsables de mantener el estado entre las diferentes llamadas.

Sin duda, hoy en día XML es el lenguaje de intercambio de datos más extendido (ya veremos en unos pocos años si esto sigue siendo así). Y sobre todo teniendo en cuenta que en el entorno del navegador, usar XML suele (o solía) ser común para intercambiar datos usando AJAX.

Debido a todo esto, poco a poco los lenguajes han ido incorporando mecanismos cada vez más comodos para procesar los documentos XML, como por ejemplo en Java JAXP . En este caso vamos a hablar sobre E4X, o la extensión de ECMAScript para el procesado de documentos XML.

Tratar un documento XML con esta extensión es sumamente sencillo, de hecho, el propio código habla por si mismo.

var library = <library postalcode="28000">
<books>
<book id="1">
  <title>Book Title 1</title>
  <author>Author of Book 1</author>
</book>
<book id="2">
  <title>Book Title 2</title>
  <author>Other Author of Book 2</author>
</book>
</books>
<magazines>
<magazine id="1">
<title>Magazine title 1</title>
</magazine>
</magazines>
</library>

alert(library.books.book[0].title); // Book Title 1
alert(library.books.book.(@id == "2").author); // Other Author of Book 2

Como veis, el procesado de un documento XML se simplifica de una manera dramática. Al mapear un documento XML a un objeto, su forma de acceso es practicamente similar a como lo haríamos con dicha estructura de datos.

La sintaxis de acceso al documento nos recuerda en cierta manera a XPath, pudiendo hacer cosas como:

alert(library.books..*.length()) 
	// El operador .. accede a todos los hijos.
alert(library.magazines.magazine[0].@id) 
	// Usando el operador @ accedemos a los atributos
alert(library.books.book.(@id == "2").author); 
	// Podemos filtrar por el valor de los atributos

Además de ayudar a procesar un documento, tambien tenemos facilidad para generar XML, si “encerramos” una expresion entre {}, se sustituirá por su valor, o incluso usando el operador +, añadiremos nuevos elementos al documento.

var b = "book"
var xmlDoc = <{b}><title>The Title</title></{b}>
alert(xmlDoc.toXMLString())

library.books.book += <book id="3"><title>....</book>

Después de ver todo lo que E4X puede hacer por nosotros, ahora contaremos la parte mala :).

Actúalmente, solo esta soportado en productos de Mozilla, esto es, Firefox, Thunderbird o Rhino, además de ActionScript, pero no se puede utilizar en Chrome, Safari o Internet Explorer. Y, ¿Por qué sucede esto?, pues básicamente por que los desarrolladores de los navegadores no consideran que sea necesaria esta implementación, ya que consideran que el acceso estandar usando DOM en Javascript es lo suficientemente versatil como para no necesitar nada más. Además, librerías como JQuery tienen extensiones para manejar documentos XML.

En cualquier caso, al ser un estandar nos garantizamos que si algun día deciden incorporar E4X en los navegadores actúalmente no soportados, funcionará de forma identica a la que lo hace ahora mismo en Firefox o incluso en ActionScript.


Aplicación de escritorio usando Javascript

noviembre 1, 2010

El otro día me preguntaba si seria posible hacer una aplicación de escritorio usando Javascript. Como sabemos, Javascript es uno de los lenguajes que mas peso ha tenido en los últimos años, y que ha tenido que sufrir la guerra de navegadores e implementaciones a la que hemos sido testigos recientemente.

Sin embargo, Javascript es un lenguaje fascinante, con aspectos muy avanzados y que poco a poco ha ido añadiendo cosas cada vez mas interesantes, como por ejemplo sus interesantes mecanismos de introspección y ejecución de código de forma dinámica.

Pero, en esta entrada nos vamos a centrar en como hacer una aplicación de escritorio usando Javascript, cosa que seguramente no sea muy habitual.

Para ello, nos vamos a valer de la plataforma con la que mas se confunde Javascript y que nada tiene que ver, si, estamos hablando de Java. Aun recuerdo las noches que pasaba por el IRC, mas concretamente en el canal #java, la cantidad de gente que entraba a preguntar cosas sobre javascript era tremenda. Lo cierto es que el nombre no ayuda, y la confusión es bastante probable.

Aunque hay varias opciones, he elegido Java como opción ya que aporta varias cosas importantes, por un lado es su gran librería gráfica Swing, que esta integrada en la plataforma, por otro lado su característica mas destacable, la multiplaformidad, lo mismo se ejecuta en Linux, OSX o Windows. Estas dos cosas son ventajas en su mismas de Java, lo que actúa como nexo de unión es Rhino (http://www.mozilla.org/rhino/).

Rhino es una librería hecha por mozilla, que permite integrar Javascript totalmente en la plataforma de Java. Lo cierto es que Rhino esta integrado desde la versión 1.6 dentro de Java, por lo que si queremos interpretar código javascript en Java, no necesitamos nada mas que la propia JDK, sin embargo, en el paquete de rhino, aparte de la librería, viene un interprete interactivo de javascript que es bastante útil.

Lo cierto es que gracias a la integración entre Java y Javascript, hacer una aplicación de escritorio es bastante sencillo. Si por ejemplo ejecutamos el siguiente programa:

var frame = new javax.swing.JFrame();
frame.setSize(200,200);
frame.setVisible(true);
frame.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);

Usando el interprete de rhino:

java -jar js.jar prueba.js

Obtendremos, como esperamos, una ventana vacía.

Como vemos, este método es sencillo y bastante potente para realizar prototipos, ya que podemos probar cualquier código java, usando las comodidades de Javascript y quitandonos el paso de la compilación.

Incluso, podremos usar la consola interactiva de Javascript, para ir modificando la interfaz poco a poco, e ir viendo los cambios sobre la marcha.

Sin duda, uno de los beneficios de Javascript que aprovechamos en esta forma de interactuar con swing son la posibilidad de definir una función en la llamada que añade los listeners a los controles. Así por ejemplo, si tenemos un botón, podemos definir el código controlador que se lanza al pulsarlo, en la propia llamada al método addActionListener.

Como en el siguiente código:

function main() {
	var frame = new javax.swing.JFrame();
	frame.setSize(200,200);
	frame.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
	
	var button = new javax.swing.JButton("Push Me!");
	var label = new javax.swing.JLabel("JLabel");
	var panel = new javax.swing.JPanel();
	
	button.addActionListener(function() {
		label.setText("Pressed!")
	});
	
	panel.add(button);
	panel.add(label);
	
	frame.getContentPane().add(panel);
	
	frame.setVisible(true);
}

Después de ver todo esto, queda respondida la duda que iniciaba el post. Si, se pueden hacer aplicaciones de escritorio usando Javascript, claro que con la inestimable ayuda de Java y Rhino 🙂


HTML5 y Bases de Datos locales

octubre 16, 2010

Otra de las grandes novedades de HTML5 es las nuevas alternativas respecto a la forma de guardar información en el cliente por parte de una web. Hasta ahora, la única alternativa que se tenia desde una web era usar cookies. HTML5 introduce tres nuevas formas, “local” y “session” storage y por otro lado la posibilidad de manejar toda una base de datos relacional que reside en cada cliente. A diferencia del localstorage o sessionstorage, que son mas similares a las cookies, el hecho de manejar una base de datos totalmente funcional es una novedad bastante importante que puede ayudar a nuestras webs a guardar información de una forma mucho mas estructurada.

Como hemos dicho, la base de datos es de tipo relacional, por lo que el lenguaje para “hablar” con ella será enteramente SQL.

Aunque expresamente la W3 desaconseja ligar el estándar a una implementación concreta, en la practica, prácticamente la totalidad de navegadores han usado SQL lite para implementar la gestión de la base de datos.

Como sabemos, no el SQL que entiende todas las bases de datos es igual, existen lo que se denominan “dialectos”, debido a que SQL lite es casi la opción “de-facto” podremos usar el “dialecto” del SQL lite en las sentencias. Aunque, al igual que el estándar desaconsejaba ligarse a una implementación, no es buena idea ligar el código a un dialecto, pues puede ser que en algún momento uno de los navegadores decida utilizar otro “backend” de bases de datos, y haga nuestro código incompatible. Lo ideal es usar, en la medida de lo posible, el SQL mas cercano al estándar.

Pese a todo esto, de momento y hasta que no se plantee una implementación independiente, el estándar de HTML5 respecto al “WebSQL” apunta a que el dialecto a seguir es del SQL lite.

Pasando ya a como usar la base de datos desde javascript, el primer paso es obtener una referencia a la base de datos para poder operar con ella. Para ello usaremos la función openDatabase() donde le pasaremos, el nombre de la base de datos, la versión del esquema y el tamaño estimado.

Una vez tenemos la referencia de la base de datos, vamos a ver que, prácticamente la totalidad de los métodos son asíncronos, por lo que típicamente los argumentos serán callbacks. En este caso Javascript sale a nuestra ayuda, como seguramente sabremos, en Javascript podemos encapsular en una variable la declaración de una función, por lo que la forma mas común de pasar los parámetros a las funciones que operan en la base de datos es incluir la declaración de los propios callbacks, ya que habitualmente, no será muy útil reutilizar dichos callbacks.

Las dos funciones más usadas sin duda van a ser “transaction” y “executeSql”, con la primera crearemos una transacción que se ejecutará contra la base de datos de forma atómica (bueno, esa es la definición de transacción, no?), mientras que con la segunda, como su nombre indica ejecutaremos las propias sentencias sql.

A continuación veremos un simple ejemplo en el que creamos una base de datos de usuarios en el cliente, donde guardamos cosas como su nombre de usuario o password.

<html>
<head>
<script type="text/javascript">
    var db;
    function init() {
        db = openDatabase("DB Prueba", "0.1", "Database Prueba", 200000);
        if (db) {
            // Database opened
			db.transaction( function(tx) {
				tx.executeSql("CREATE TABLE IF NOT EXISTS usuarios(userid integer primary key autoincrement, username text, password text)")
			});
        }

		listUsers();

    }

	function showUsers(users) {
		var place = document.getElementById("usersDiv");
		if (place.getElementsByTagName("ul").length > 0 )
			place.removeChild(place.getElementsByTagName("ul")[0]);
		var list = document.createElement("ul");

		for ( var i = 0; i < users.length; i++) {
			var item = document.createElement("li");
			item.innerHTML += "<b>userId:</b>" + users[i][0] + " <b>userName:</b>"
					+ users[i][1] + " <b>password:</b>" + users[i][2] +
					"<button onclick='removeUser("+ users[i][0]+")'>Remove</button>";
			list.appendChild(item);
		}
		place.appendChild(list);
	}

	function listUsers() {
		db.transaction( function(tx) {
			tx.executeSql("SELECT * FROM usuarios", [],
				function(tx, result){
					var output = [];
					for(var i=0; i < result.rows.length; i++) {
						output.push([result.rows.item(i)['userid'],
								result.rows.item(i)['username'],
								result.rows.item(i)['password']]);
					}

					showUsers(output);

				});
		});
	}

	function addUser(username, password) {
		db.transaction( function(tx) {
			tx.executeSql("INSERT INTO usuarios(username, password) VALUES(?,?)", [username, password]);
		});
		listUsers();
	}

	function removeUser(userId) {
		db.transaction(function(tx) {
			tx.executeSql("DELETE FROM usuarios WHERE userId=?",[userId], listUsers);
		})
	}
</script>
</head>
<body onload="init()">
<div id="usersDiv">
	Usuarios en local
</div>
<div id="newuser">
	Crear nuevo usuario<br/>
	Usuario: <input type="text" id="username"/><br/>
	Password: <input type="password" id="password"/><br/>
	<button onclick="addUser(username.value, password.value)">Add User</button>
</div>
</body>

</html>

Desde luego es una gozada que sea tan sencillo trabajar con una base de datos de una forma tan sencilla 🙂

Además, tenemos que sumarle la gran calidad de las herramientas de depuración que nos aportan los navegadores, en la captura que encabeza la entrada podemos ver la que tiene Safari. Si somos usuarios de chrome veremos que la ventana es idéntica, esto es por que Chrome al igual que Safari se basa en webkit.

Después de leer el post, podemos sacar la conclusión de que es muy sencillo operar con bases de datos en cliente, y es totalmente cierto, pero ahora hay que pensar bien cuando esta opción es la mejor y cuando los datos solo se guardan en el servidor.