miércoles, 30 de junio de 2010

Creación de servidores en javascript

Aunque javascript está diseñado para ejecutarse en el cliente al cargar páginas web, no está limitado a este ámbito.
Existe un proyecto llamado CommonJS que amplia la funcionalidad del lenguaje haciéndole poder interactuar con el sistema como un lenguaje más.

node.js es un framework para la creación de servidores que se apoya en la librería CommonJS.
Está construido completamente siguiendo la filosofía "event-loop" y todas las operaciones I/O se realizan de manera asíncrona para una buen aprovechamiento de la concurrencia del sistema.

Así pues, node.js permite crear servidores optimizados de forma sencilla desde un lenguaje dinámico como javascript, un ejemplo muy simple de un servidor creado con node.js es el siguiente:

var sys = require('sys');
var net = require('net');

var acc = 0;

net.createServer(function (socket) {
socket.setEncoding("utf8");
socket.addListener("connect", function () {
socket.write("Accumulator server\r\n");
socket.write("Accumulator value: " + acc + '\n');
});
socket.addListener("data", function (data) {
var input = parseInt(data);
if(!isNaN(input)){acc += parseInt(data);}
socket.write("Accumulator value: " + acc + '\n');
});
socket.addListener("end", function () {
socket.end();
});
}).listen(8888, "127.0.0.1");

Se puede consultar en mayor profundidad el API oficial.

miércoles, 16 de junio de 2010

Nuevos tipos de formularios en html5

Con el salto a la nueva versión de html se van añadir nuevos tipos de entradas posibles a los formularios.
Por ejemplo, crear un 'slider' para seleccionar un rango se limitará a dar el tipo range a un input.

<input type="range" min="0" max="100" step="2">

Este componente enriquece la interfaz de usuario, hay otros aún más útiles, por ejemplo el siguiente indica que la entrada debe ser de tipo numérico, esto puede ayudar a dispositivos móviles con pantallas reducidas a desplegar tan solo un teclado con números, aprovechando mejor el espacio.

<input required type="number" min="0" step="1" >

También sirve el tipo email para idénticos propósitos. Además de esta forma los navegadores pueden añadir mayor integración (si conocen que el tipo de entrada es email pueden sugerir los de nuestra libreta de direcciones).

Otro tipo de entradas muy comunes son las de fecha, en buscadores de reservas de hoteles, vuelos, etc. La mayoría de webmaster solucionan el problema de mostrar un calendario con javascript.
En html5 se deja este la responsabilidad de representar estos tipos de entradas a los navegadores, habiendo inputs de tipo date, month, week, time...

Para más información.

domingo, 13 de junio de 2010

Hot swapping en erlang

Hot swapping en un entorno con múltiples procesos distribuidos se refiere a la actualización de uno de esos procesos sin parar en ningún momento el sistema.

Si se encuentra algún error en el software, o se necesita añadir nueva funcionalidad, el proceso afectado encola las peticiones nuevas que van llegando, actualiza su código y con el nuevo código sigue atendiendo a las peticiones que tenía encoladas. Los demás procesos del sistema no notan nada de este cambio.

En muchas de las tecnologías usadas hoy en día, todo este proceso es bastante complejo, sin embargo, en erlang es trivial ya que lo soporta de forma nativa el lenguaje.

Supongamos dos procesos, uno que sirve datos.

-module(data).
-export([get/0, version/0]).

get() -> 42.

version() -> 1.

Y otro que los consume.

-module(consumer).
-compile(export_all).

start() -> spawn(fun() -> loop() end).

loop() ->
sleep(),
io:format("Value: ~p at version: ~p~n", [data:get(), data:version()]),
loop().


sleep() ->
receive
after 1000 -> true
end.

El proceso consumidor irá mostrando cada segundo los datos que le manda el otro proceso. En el momento que se necesite cambiar la funcionalidad del proceso creador de datos, simplemente se recompila y el consumidor el único cambio que notará es el valor que tienen los nuevos datos.

1> c(data).
{ok,data}
2> c(consumer).
{ok,consumer}
3> consumer:start().
<0.53.0>
Value: 42 at version: 1
Value: 42 at version: 1
Value: 42 at version: 1
Value: 42 at version: 1
Value: 42 at version: 1
...
c(data).
{ok,data}
Value: 43 at version: 2
Value: 43 at version: 2
Value: 43 at version: 2
Value: 43 at version: 2
...

En entornos críticos o en los que el sistema completo tarda mucho tiempo en arrancar, esto permite actualizar código sin que haya que mantener el servicio parado mientras se actualilza.

jueves, 10 de junio de 2010

Código Thread-Safe vs Código reentrante

Estos dos conceptos son habitualmente confundidos y, aunque están relacionados se corresponden a propiedades distintas.

Una función es thread-safe cuando se le puede llamar desde múltiples hilos concurrentemente, la ejecución terminará correctamente sin que unos hilos interfieran con otros.

Una función reentrante es aquella a la que se le pasa como parámetro todos los valores que usa, no utiliza ningún objeto global (variable global, singleton), no tiene efectos secundarios y no llama a ninguna función no reentrante.

Una función reentrante será siempre thread-safe, si no hace ningún cambio en el estado global de un programa no hay posibilidad que interceda con el código de otra hebra.

¿Y al contrario? Una función thread-safe no tiene porqué ser reentrante, ya que puede modificar el estado global del programa siempre que lo haga ordenadamente mediante exclusión mutua.

Una función reentrante cumplirá además con la transparencia referencial. Transparencia referencial es una propiedad que se cumple cuando, al cambiar una función con sus parámetros por el valor que devuelve, el programa se comporta de la misma forma.

Este tipo de funciones (reentrantes) tienen la ventaja de que se puede seguir su funcionamiento matemáticamente y que admiten memoization como optimización. Un ejemplo aquí.

domingo, 6 de junio de 2010

Ejecución de código remoto con ruby

Existe una librería llamada msgpack que permite la ejecución remota de código ruby (entre otros lenguajes), lo que más destaca de esta librería es su sencillez y la poca configuración necesaria para funcionar.

En un ejemplo cliente/servidor, el servidor es una instancia de una clase que contiene los métodos que queremos ejecutar de forma remota. Además de eso tan solo tenemos que elegir a qué clientes escuchamos y en qué puerto.

require 'rubygems'
require 'msgpack/rpc'

class RemoteServer
def printer(arg)
puts "Printing: #{arg}..."
return true
end
end

server = MessagePack::RPC::Server.new
server.listen('0.0.0.0', 8888, RemoteServer.new)
server.run

Un cliente que se conecte al servidor anterior sería el siguiente.

require 'rubygems'
require 'msgpack/rpc'

client = MessagePack::RPC::Client.new('127.0.0.1', 8888)
called = client.call_async(:printer, 'message')
result = called.get

La llamada call_async hace que se ejecute el método de forma asíncrona, si necesitamos el valor devuelto por la función será necesario llamar a get que se asegura de la terminación del método y de recoger el resultado.
También se puede ejecutar código de forma síncrona con call.

Además, la librería permite crear sesiones para reaprovechar las conexiones con el servidor.

require 'rubygems'
require 'msgpack/rpc'

session_pool = MessagePack::RPC::SessionPool.new
client = session_pool.get_session('127.0.0.1', 8888)
result = client.call(:printer, 'message')

jueves, 3 de junio de 2010

Solución al problema de la inclusión cíclica en C++

En aplicaciones complejas se puede dar el caso de que una clase(Class1) haga uso de otra(Class2), pero a su vez esta última haga uso de la primera.

En este caso estamos ante un problema de definición cíclica, para definir una clase se necesita la otra y viceversa. Se ejemplifica en el siguiente código.

#ifndef CLASS1_H
#define CLASS1_H

#include "class2.h"

class Class1{
void method(Class2* param);
};

#endif


#ifndef CLASS2_H
#define CLASS2_H

#include "class1.h"

class Class2{
void method(Class1* param);
};

#endif

Para solucionar este problema, podemos indicarle al compilador que la clase existe y será definida más adelante, así, aún teniendo dependencia cíclica se puede generar el código correctamente.
Para indicarselo al compilador tan solo hay que definir class Clase antes de usarla.

#ifndef CLASS1_H
#define CLASS1_H

#include "class2.h"

class Class2;

class Class1{
void method(Class2* param);
};

#endif


#ifndef CLASS2_H
#define CLASS2_H

#include "class1.h"

class Class1;

class Class2{
void method(Class1* param);
};

#endif

miércoles, 2 de junio de 2010

Web SQL en html 5

Con la llegada de html 5 los navegadores pondrán a disposición de los programadores web la tecnología web SQL.

Esta tecnología consiste básicamente en una base de datos local a los navegadores accesible desde javascript. Permitirá que las aplicaciones web puedan hacer mayor trabajo offline y sincronizarse cada cierto tiempo con el servidor.

El acceso a estas bases de datos se hace mediante SQL.
Para abrir una base de datos se necesita ejecutar lo siguiente.

function openDB(dbName){
var version = 1;
var db = window.openDatabase(dbName, version, dbName, 2*1024);
return db;
}

Una vez abierta la base de datos para ejecutar SQL podemos disponer de las siguientes funciones.

function executeSQL(db, sqlString, args){
db.transaction(function(t){
t.executeSql(sqlString, args);
});
}

function executeSQL(db, sqlString, args, callback){
db.transaction(function(t){
t.executeSql(sqlString, args, callback);
});
}

Se puede pasar opcionalmente un callback a la función executeSQL, es especialmente útil en las consultas para tratar el resultado. Un ejemplo de callback para consultas es showEntries definida más adelante.
Este es un ejemplo típico de uso de web SQL.

function insertEntry(db, name){
executeSQL(db, "INSERT INTO entries(name) VALUES (?)", [name]);
}

function selectAllEntries(db, callback){
executeSQL(db, "SELECT * FROM entries", [], callback);
}

function showEntries(t, rs){
var entry;

for(var i=0; i<rs.rows.length; i++){
entry = rs.rows.item(i);
alert(entry['name']);
}
}


var db = openDB("example");
executeSQL(db, "CREATE TABLE IF NOT EXISTS entries(id INTEGER PRIMARY KEY, name TEXT)", []);

for(var i=0; i<10; i++){
insertEntry(db, "test"+i);
}

selectAllEntries(db, showEntries);