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.

Anuncios

DSL en Ruby. Generador de LaTeX.

octubre 3, 2010

Lo cierto es que, Ruby es un lenguaje que cuanto más aprendes, más te sorprende. Lo último que he visto sobre el fantástico lenguaje creado por Matz, es la facilidad con que se pueden fabricar “interpretes” de lenguajes específicos, o lo que también se conoce como DSL por ser las anglosajonas siglas de Domain-specific Languages.

Como se puede esperar, estos lenguajes se centran en resolver problemas muy específicos, en lugar de, como C o Java, estar enfocados a un ámbito genérico. Ejemplos de estos DSL pueden ser lenguajes como SQL, HTML, VHDL o YACC. Cada uno de ellos existen para resolver un problema, y difícilmente se podrá utilizar para otra cosa. No me imagino hacer una web con YACC o una consulta a una base de datos con VHDL :-S

Crear estos lenguajes suele ser una tarea compleja, y en muchas ocasiones se suele usar otro lenguaje mas genérico. De hecho, hacer un “interprete” de un lenguaje usando C++ mediante macros y templates es un entretenimiento que no esta accesible a cualquiera 😉

Sin embargo, cuando Ruby se nos cruza en el camino, todo parece resolverse como por arte de magia.

Antes de meternos de lleno en el objetivo del post, que es una especie de sencillo interprete de LaTeX (si, el lenguaje que describe como es un documento 😀 ), vamos a ver que es lo que Ruby hace por que esto sea posible.

Sin duda, el principal elemento que añade la mayor parte de la magia es una sola sentencia, dicha sentencia es: “alias method_missing tag“.

Esa sentencia hará que cualquier invocación de un método no existente de una clase, se “reenviará” al método que le decimos a continuación, en nuestro caso, “tag”.

Si nos paramos a pensar la potencia de este mecanismo es tremenda, podemos hacer que nuestra clase “conteste” potencialmente a cualquier método. Sin duda le añade un factor dinámico al código que es difícil de encontrar en otros lenguajes, y sobre todo, con esa elegancia.

Otro elemento muy importante de la creación de DSL es el método instance_eval de cualquier clase de Ruby. Dicho método ejecutará lo que le pasemos como parámetro en el contexto del objeto sobre el que se invoca. La verdad es que es mas difícil explicarlo que verlo en acción, así que seguro que queda mas claro en este ejemplo:

class Prueba
  def metodo
    puts "Hola Mundo"
  end
end

p = Prueba.new
p.instance_eval "metodo"
=> "Hola Mundo"

Como veis, lo que hace instance_eval es ejecutar el parámetro, como si fuera código que se ejecuta sobre la variable p.

El último elemento clave son los bloques en Ruby. Sin duda uno de mis elementos favoritos del lenguaje, todo método en Ruby, puede llevar asociado un bloque, y dicho bloque se puede capturar como parámetro de un metodo. Si combinamos los bloques con el instance_eval, podemos tener lo siguiente.

class Prueba
  def metodo1
    puts "Método 1"
  end

  def metodo2
    puts "Método 2"
  end

  def self.generate(&block)
    Prueba.new.instance_eval(&block)
  end
end

Prueba.generate do
  metodo1
  metodo2
end
=> Método 1
=> Método 2

Mola, ¿no? 🙂

Pues bien, básicamente con estos elementos, podemos empezar nuestro mini-generador de LaTeX.

El objetivo será que usando una sintaxis mas próxima a Ruby, seamos capaces de crear un documento de LaTeX sin usar casi ninguna {} y sin preocuparnos de que ponemos un \end cuando debemos.

Para ello, debemos crear una clase que sea capaz de que ejecutar el siguiente código en un interprete de Ruby tenga como salida un documento con formato LaTeX

LaTeX.generate(STDOUT) do
  documentclass "article"
  usepackage "amsmath"
  title "\\LaTeX"
  date ""
  start "document" do
    title "hola"
    "Hola, hoy son las #{Time.now} y \\sqrt{2} = #{Math.sqrt(2)}"
  end
end

Como veis, ademas de tener una sintaxis mucho mas “limpia” ganamos la incorporación del propio código Ruby, pudiendo usar cosas como “Time.now” o operaciones como la raíz cuadrada.

Aunque parezca mentira, usando los mecanismos que hemos puesto anteriormente, la clase necesaria seria algo como:

class LaTeX
  def initialize(out)
    @out = out
  end
  
  def tag(tagname, name=nil)
    if tagname == :start
      tagname = :begin
    end
    @out << "\\#{tagname}"
    if name
      @out << "\{#{name}\}"
    end
    @out << "\n"
    if block_given?
      content = yield
      if content
        @out << content.to_s << "\n"
      end
      @out << "\\end"
      if name 
        @out << "\{#{name}\}"
      end
      @out << "\n"
    end
    nil
  end
    
  alias method_missing tag
  
  def self.generate(out, &block)
    LaTeX.new(out).instance_eval(&block)
  end
end

Unicamente, con ese fragmento sencillo de código, seremos capaces de que construir un documento LaTeX sea mucho mas sencillo que lo habitual, pues la ejecución tendrá como resultado

\documentclass{article}
\usepackage{amsmath}
\title{\LaTeX}
\date{}
\begin{document}
\title{hola}
Hola, hoy son las Sat Oct 02 23:49:35 +0200 2010 y \sqrt{2} = 1.4142135623731
\end{document}