sábado, 26 de noviembre de 2011

node v0.6.3 publicada. Como actualizar node.js con n

Actualización de 30 de octubre de 2013: este post se publicó en 2011 y las cosas han cambiado desde entonces. En junio de 2012, introdujeron unas modificaciones interesantes sobre el uso de n, como muy bien apunta Carlos Canicio en su comentario.

Ahora, para instalar la última versión estable hay que usar (en función de la instalación ya igual ni te hace falta el sudo):
$ n stable

Para instalar la última versión que probablemente será de número impar y eso significa que es inestable (ahora están en las 0.11.*):
$ n latest

Por ejemplo, si quieres jugar con las opciones de ES6 que tiene V8, deberías instalar una versión inestable y arrancar a node con el flag --harmony.

Otra opción que te puede interesar es arrancar n sin parámetros, ya que con la flechas del teclado puedes navegar por las versiones de node y seleccionar la que te interese.


Ayer publicaron la versión v0.6.3 de node.js. La principal novedad de esta versión es que incluye el gestor de paquetes npm, con lo que ya no hace falta instalarlo directamente.

Para actualizar la versión de node.js, una de las utilidades más sencillas es utilizar el paquete  "n"  (https://github.com/visionmedia/n).

En el portátil (Mac) en el que estoy escribiendo este post tengo instalada una versión anterior:

$ node -v
v0.4.12

Lo primero nos aseguramos de tener "n" instalado con npm.
$ sudo npm install -g n
Password:
/usr/local/bin/n -> /usr/local/lib/node_modules/n/bin/n
n@0.5.4 /usr/local/lib/node_modules/n 


Ahora ya se puede actualizar node. Esta tan sencillo como poner:

$ sudo n latest

Veremos algo como:

######################################################################## 100,0%
Checking for program g++ or c++          : /usr/bin/g++ 
Checking for program cpp                 : /usr/bin/cpp 
Checking for program ar                  : /usr/bin/ar 
Checking for program ranlib              : /usr/bin/ranlib 
Checking for g++                         : ok  
Checking for program gcc or cc           : /usr/bin/gcc 
Checking for gcc                         : ok  
Checking for library dl                  : yes 
Checking for openssl                     : not found 
Checking for function SSL_library_init   : yes 
Checking for header openssl/crypto.h     : yes 
Checking for library util                : yes 
Checking for library rt                  : not found 
Checking for fdatasync(2) with c++       : no 
'configure' finished successfully (0.918s)
Waf: Entering directory `/usr/local/n/node-v0.6.3/out'
DEST_OS: darwin
DEST_CPU: x64

.... (Y un largo etcétera)


Y revisamos de nuevo la versión de node.js para confirmar una correcta actualización

$ node -v
v0.6.3

Sobre n 

El paquete "n" permite la gestión de los binarios de node de manera muy sencilla.

"n" instala node por defecto en /usr/local/n/versions, donde puedes ver que tienes actualmente instalado. Puedes activar versiones anteriores de node si lo deseas. Lo node activados se instalan en el prefijo /usr/local, que si se desea se puede cambiar mediante la variable de entorno N_PREFIX. Para cambiar el comportamiento de n simplemente cambia N_PREFIX con el valor que prefieras.

"n" soporta los siguientes comandos:

   n                           Versiones instaladas
   n latest [config ...]       Instala o activa la ultima release de node 
   n <version> [config ...]    Instalar y/o usar node version <version>
   n use <version> [args ...]  Ejecutar node  <version> con argumentos [args ...] 
   n bin <version>             Informar path de binarios de <version>
   n rm <version ...="">       Eliminar version(es)
   n --latest                  Informar ultima version de node disponible
   n ls                        Informar de versiones disponibles
En los listados se marca con un círculo "ο" la versión activa y en el listado de las disponibles se marca con un asterisco las descargadas.
Por ejemplo, slistado las versiones disponibles con n ls me aparecen las siguientes:
n ls
    0.0.1
    0.0.2
    0.0.3
    0.0.4
    0.0.5
    0.0.6
    0.1.0
    0.1.1
    0.1.2
    0.1.3
    0.1.4
    0.1.5
    0.1.6
    0.1.7
    0.1.8
    0.1.9
    0.1.10
    0.1.11
    0.1.12
    0.1.13
    0.1.14
    0.1.15
    0.1.16
    0.1.17
    0.1.18
    0.1.19
    0.1.20
    0.1.21
    0.1.22
    0.1.23
    0.1.24
    0.1.25
    0.1.26
    0.1.27
    0.1.28
    0.1.29
    0.1.30
    0.1.31
    0.1.32
    0.1.33
    0.1.90
    0.1.91
    0.1.92
    0.1.93
    0.1.94
    0.1.95
    0.1.96
    0.1.97
    0.1.98
    0.1.99
    0.1.100
    0.1.101
    0.1.102
    0.1.103
    0.1.104
    0.2.0
    0.2.1
    0.2.2
    0.2.3
    0.2.4
    0.2.5
    0.2.6
    0.3.0
    0.3.1
    0.3.2
    0.3.3
    0.3.4
    0.3.5
    0.3.6
    0.3.7
    0.3.8
    0.4.0
    0.4.1
  * 0.4.2 
    0.4.3
    0.4.4
    0.4.5
    0.4.6
    0.4.7
    0.4.8
    0.4.9
    0.4.10
    0.4.11
    0.4.12
    0.5.0
    0.5.1
    0.5.2
    0.5.3
    0.5.4
    0.5.5
    0.5.6
    0.5.7
    0.5.8
    0.5.9
    0.5.10
    0.6.0
    0.6.1
  * 0.6.2 
  ο 0.6.3 

miércoles, 23 de noviembre de 2011

Expresiones de función invocadas inmediatamente ( IIFE )

NOTA IMPORTANTE: Este artículo es una traducción semilibre de este otro artículo de Ben Alman (@cowboy) que me resultó muy interesante: http://benalman.com/news/2010/11/immediately-invoked-function-expression/. A Ben le corresponde todo el mérito de la explicación.

Otro elemento a tener en cuenta es que lo que se trata en este artículo es sobre JavaScript, independientemente de que se ejecute en cliente o en servidor.



En caso de que no os hayáis dado cuenta, soy un poco purista de la terminología. Así que, después de escuchar demasiadas veces el popular y, sin embargo, engañoso término de javascript "función anónima autoejecutable", he decidido finalmente organizar mis pensamientos en un artículo.

Además de proporcionar una información muy completa acerca de cómo funciona realmente este patrón, hago una recomendación sobre cómo deberíamos llamarlo.

Por favor, ten en cuenta que este artículo no pretende ser uno del tipo "yo tengo razón y tú estás equivocado". Estoy realmente interesado en ayudar a la gente a comprender conceptos potencialmente complejos, y creo que el uso de una terminología coherente y precisa es una de las cosas más fáciles que la gente puede hacer para facilitar la comprensión.

Entonces, en cualquier caso, ¿de qué va todo esto?
En JavaScript, cada función, cuando se invoca, crea un nuevo contexto de ejecución. Dado que las variables y funciones definidas dentro de una función sólo se pueden acceder desde su interior, pero no desde fuera, ese contexto creado por la invocación de una función proporciona una manera muy fácil de crear "privacidad".

// Debido a que esta función devuelve otra función que tiene acceso
// a la variable "privada" i, la función devuelta tiene unos ciertos "privilegios"

function crearContador() {
  // `i` solo es accesible desde dentro de `crearContador`.
  var i = 0;

  return function() {
    console.log( ++i );
  };
}

// Fíjate en que tanto `contador` como `contador2` tienen 
// cada uno su propio `i` independiente.

var contador = crearContador();
contador(); // logs: 1
contador(); // logs: 2

var contador2 = makeCounter();
contador2(); // logs: 1
contador2(); // logs: 2

i; // ReferenceError: i is not defined (sólo existe dentro de crearContador)
  
En muchos casos, no necesitarás varias "instancias" de lo que sea que devuelva la función crearLoQueSea, pudiendo hacer lo necesario con una sola instancia y en otros casos ni siquiera estarás devolviendo de manera explícita un valor.

El fondo de la cuestión
Veamos: si se define una función como 'function foo(){}' o 'var foo = function(){}' , lo que acabas teniendo es un identificador de una función, que se puede invocar al poner paréntesis () detrás, como 'foo()'.

// Debido a que una funcion solo se puede invocar poniendo () después del nombre
// como foo(), y debido a que foo es solo la referencia a la expresión de una función
// como `function() { /* código */ }`...

var foo = function(){ /* código */ }

// ... no se podría deducir que la función se podría invocar poniendo () inmediatamente
// después de la expresión de la función?

function(){ /* código */ }(); // SyntaxError: Unexpected token (
 
Como puedes ver, esto falla.

Cuando el parser se encuentra con la palabras clave 'function' en el ámbito global o dentro de una función, la considera como la declaración de una función y no como una expresión de función. Si no le indicas al parser explícitamente que debe espirar una expresión, lo interpreta como la declaración de una función sin nombre y lanza una excepción de SyntaxError porque las declaraciones precisan siempre de un nombre.

Una digresión: las funciones, paréntesis, y SyntaxErrors
Curiosamente, si especificas el nombre de una función y pones los paréntesis inmediatamente después de la función, el parser también lanzaría un SyntaxError, pero por una razón diferente.

Mientras que los paréntesis colocados después de una expresión indican que la expresión es una función que se debe invocar, los paréntesis colocados después de una declaración se interpretan como totalmente separados de la declaración y actúan como un operador de agrupación (utilizado como un medio para controlar la precedencia de evaluación).

// Aunque la declaración de la función es sintácticamente válida, es una declaración
// y los parántesis que hay a continuación resultan erróneos porque como operador 
// de agrupación necesitan contener una expresión

function foo(){ /* código */ }(); // SyntaxError: Unexpected token )

// Ahora bien, si introduces una expresión dentro de los paréntesis
// no se lanza ninguna excepción...
// pero la función tampoco se ejecuta, porque esto:

function foo(){ /* código */ }( 1 );

// Es equivalente a lo siguiente, una declaración de función seguida de una 
// expresión completamente independiente.

function foo(){ /* código */ }

( 1 );
  
Expresión de función invocada inmediatamente o Immediately-Invoked Function Expression, IIFE
Afortunadamente, corregir SyntaxError es sencillo. La forma más aceptada para informar al parser que debe esperar una expresión de función es envolverla en paréntesis, porque en JavaScript, los paréntesis no pueden contener declaraciones. En este punto, cuando el parser encuentra la palabra clave 'function' sabe que debe analizarlo como una expresión de función y no una declaración de función.

// Cualquiera de los dos siguientes patrones se pueden usar para invocar
// de forma inmediata una expresión de función, utilizando el contexto 
// de ejecución de la función para crear "privacidad"

(function(){ /* código */ }()); // Crockford recomienda esta
(function(){ /* código */ })(); // Pero esta funciona igual de bien

// Puesto que el punto de las paréntesis o los operadores es forzar
// a desambiguar entre expresiones de funciones y declaraciones de funciones,
// se pueden evitar cuando el parser exprea una expresión (ver la nota más abajo)

var i = function(){ return 10; }();
true && function(){ /* código */ }();
0, function(){ /* código */ }();

// Si no te interesa el valor devuelto, o el hecho de que tu código 
// resulte un poco más ilegible, puedes ahorrarte algún byte prefijando
// la función con un operador unario

!function(){ /* código */ }();
~function(){ /* código */ }();
-function(){ /* código */ }();
+function(){ /* código */ }();

// Esta es otra variación  de @kuvos - No tengo clara las implicaciones
// en rendimiento, si las hubiera, por utilizar la palabra clave 'new', pero funciona.
// http://twitter.com/kuvos/status/18209252090847232

new function(){ /* código */ }
new function(){ /* código */ }() // Solo se necesita paréntesis si se pasan argumentos
  
Una nota importante sobre los paréntesis 
En los casos en los que los paréntesis extra de "desambiguación" que rodean la expresión de la función sean innecesarios (porque el parser ya espera una expresión), sigue siendo una buena idea usarlos cuando se realiza una asignación, por convención.

Dichos paréntesis suelen indicar que la expresión de la función se invocará de inmediato, y que la variable contendrá el resultado de la función, no la propia función. Esto puede ahorrarle a alguien que esté leyendo el código, la molestia de tener que desplazarse hasta el final de lo que podría ser una expresión de función muy larga para ver si finalmente se ha invocado o no.

Como regla general, aunque escribir código ambiguo podría ser técnicamente necesario para mantener el parser JavaScript de lanzar excepciones SyntaxError, escribir código sin ambigüedades es también es bastante bueno para evitar que otros desarrolladores te lanzan a tí excepciones del tipo "WTFError"!

Guardando el estado con closures
Al igual se pueden pasar argumentos a las funciones cuando se invocan mediante su identificador, también se pueden pasar cuando se invoca inmediatamente una expresión de función. Puesto que la función (el closure) referencia los valores pasados, estos quedan "fijados" de modo que una Immediately-Invoked Function Expression se puede utilizar para guardar eficazmente un estado.

Si deseas obtener más información sobre los 'closures', puedes consultar este artículo: http://skilldrick.co.uk/2011/04/closures-explained-with-javascript/

// Esto no funciona como podrías pensar en primera instancia,
// porque el valor de 'i' nunca queda fijado. Lo que ocurre es que
// cuando haces click en cada enlace (después de que se haya ejecutado
// el bucle), el alert informa del total de elementos porque es el valor
// que tiene la variable 'i' en ese punto

var elems = document.getElementsByTagName( 'a' );

for ( var i = 0; i < elems.length; i++ ) {

  elems[ i ].addEventListener( 'click', function(e){
    e.preventDefault();
    alert( ' Soy el link nº ' + i );
  }, 'false' );

}

// Esto SI funciona, porque dentro de cada closure IIFE, el valor de 'i'
// queda fijado como 'indiceFijado'. Tras ejecutarse el bucle, aunque
// el valor de 'i' es el del total de elementos, dentro de la IIFE
// el valor de 'indiceFijado' es el que se le pasase en cuando la expresión
// de función fue ejecutada, con lo que cuando un enlace se hace click
// el valor correcto aparece en el alert

var elems = document.getElementsByTagName( 'a' );

for ( var i = 0; i < elems.length; i++ ) {

  (function( indiceFijado ){

    elems[ i ].addEventListener( 'click', function(e){
      e.preventDefault();
      alert( ' Soy el link nº ' + indiceFijado);
    }, 'false' );

  })( i );

}

// Puedes usar un IIFE como este donde el closure afecta solo a la función
// handler del click y no a la asignación completa del 'addEventListener'.
// En ambos ejemplos la variable queda fijada, pero encuentro el anterior 
// más sencillo de leer

var elems = document.getElementsByTagName( 'a' );

for ( var i = 0; i < elems.length; i++ ) {

  elems[ i ].addEventListener( 'click', (function( lockedInIndex ){
    return function(e){
      e.preventDefault();
      alert( 'Soy el link nº' + lockedInIndex );
    };
  })( i ), 'false' );

}

// En el siguiente ejemplo se combina la variable fijada con el valor final
// de 'i'

var elems = document.getElementsByTagName( 'a' );

for ( var i = 0; i < elems.length; i++ ) {

  (function( indiceFijado ){

    elems[ i ].addEventListener( 'click', function(e){
      e.preventDefault();
      alert( ' Soy el link nº ' + indiceFijado + ' de ' + i);
    }, 'false' );

  })( i );

}
 
Tenga en cuenta que en los dos ejemplos del medio, 'indiceFijado' podría haber sido llamado 'i' sin ningún problema, pero el uso de un identificador diferente, como un argumento de la función hace que el concepto sea mucho más fácil de explicar/entender.

Uno de los efectos secundarios más ventajosos de las expresiones de función invocadas inmediatamente es que, la expresión de la función anónima se invoca de inmediato, sin necesidad de utilizar un identificador, y por tanto el closure puede ser utilizado sin contaminar el ámbito actual.

¿Qué hay de malo en la "función anónima autoejecutable"?
Ya has visto lo mencionado un par de veces, pero en el caso de que no quede claro, yo estoy proponiendo el término "expresión de función inmediatamente ejecutada", y "IIFE" (del inglés “Immediately-Invoked Function Expression”) si te gusta las siglas.

¿Qué es una IIFE? Es una expresión de función que se invoca de inmediato. Como dice su nombre, vamos.
Me gustaría ver a los miembros de la comunidad JavaScript adoptar el término "expresión de función inmediatamente ejecutada" y "IIFE" en sus artículos y sus presentaciones, porque me parece que hace que la comprensión de este concepto un poco más fácil, y porque el término "función anónima autoejecutable" no es nada preciso:

// Esto si es una función "autoejectuable"  Es una función que se ejecuta (o invoca)
// a si misma de forma recursiva:

function foo() { foo(); }

// Esta es una función anónima autoejectable. Debido a que no tiene 
// identificador, debe usar la propiedad `arguments.callee` (que
// especifica la función que se ejecuta actualmente) para ejecutarse a si misma.

var foo = function() { arguments.callee(); };

// Esto "podría" ser una función anónima autoejecutable, pero solo mientras
// el idenfificador 'foo' la referencia. Si cambiases 'foo' a otra cosa tendrías
// una función anónima "que solía auto ejecutarse"

var foo = function() { foo(); };

// Mucha gente llama esto "función anónima autoejecutable" aunque
// no se autoejecuta, porque no se invoca a si misma. Sin embargo,
// sí se invoca inmediatamente.

(function(){ /* código */ }());

// Agregando un identificador a una la expresión de una función (y creando
// por tanto una función de expresión con nombre) puede ser muy util
// en depuración. Sin embargo, una vez nombrada, la función deja de ser
// anónima

(function foo(){ /* code */ }());

// Las IIFEs también pueden ser autoejecutables, aunque probablemente este
// no sea el patrón más útil

(function(){ arguments.callee(); }());
(function foo(){ foo(); }());

// Un último apunte: esto causará un error en BlackBerry 5, debido a que
// dentro de la función con nombre, ese nombre es "undefined". Awesome, huh?

(function foo(){ foo(); }());
  
Espero que estos ejemplos hayan dejado claro por qué el término "autoejecutable" es algo engañoso, ya que la función no se ejecuta a sí misma, a pesar de que la función está siendo ejecutada. Además, "anónimo" es innecesariamente específico, ya que una expresión de función invocada inmediatamente puede ser tanto anónima como con nombre.

Por lo tanto, eso es todo. Esa es mi gran idea.

Dato curioso: puesto que 'arguments.callee' está en desuso en ECMAScript 5 en modo estricto, en realidad es técnicamente imposible crear una "auto-ejecución de la función anónima" en ECMAScript 5 modo estricto.

Una última acotación al margen: El patrón de módulo
Ya que he comentado las expresiones de función, sería negligente si por lo menos no mencionase el patrón de módulo. Si no estás familiarizado con el patrón de módulo en JavaScript, verás que es similar al primer ejemplo, pero devolviendo un objeto en lugar de una función (y que se implementa generalmente como un singleton, como en el siguiente ejemplo).

// Crea una función anónima que se invoca inmediatamente 
// y asigna un valor a una variable. Esto elimina el middleman
// de la función "creaLoQueSea".
// 
// Como se explicó más arriba, aunque los paréntesis no son necesarios
// en esta expresión de función, se usan como convención para 
// ayudar a clarificar que la variable se asigna con el 
// *resultado* de evaluar la función y no con la función misma.

var contador = (function(){
  var i = 0;

  return {
    get: function(){
      return i;
    },
    set: function( val ){
      i = val;
    },
    incrementa: function() {
      return ++i;
    }
  };
  
}());
 
Este enfoque del patrón de módulo no sólo es increíblemente poderoso, pero además es increíblemente sencillo. Con muy poco código, puedes crear espacios de nombre para propiedades y métodos relacionados, organizando módulos de código enteros de una manera que minimice la contaminación del ámbito global y cree privacidad.

Seguir leyendo
Espero que este artículo fuese ilustrativo y que haya respondido a algunas de tus preguntas. Por supuesto, si tienes más dudas que cuando empezaste a leer, te listo unos artículos (en inglés) con los que puedes ampliar tus conocimientos:


Me gustaría dar las gracias a Asen Bozhilov y John David Dalton por contribuir con su asesoramiento técnico, así como a Morgan Nick por sus puntos de vista.


sábado, 19 de noviembre de 2011

Publicada la versión v0.6 de node.js

Hace dos semanas publicaron la versión v0.6 de node.js (de hecho, ahora mismo ya vamos por la v0.6.2). Entre las mejoras que incluye se pueden destacar las siguientes que detallaré a continuación:
  • Balanceo de carga entre múltiples procesos
  • Mejoras en depuración por línea de comando
  • Módulo de compresión zlib
  • Mejor soporte para comunicación entre procesos

Balanceo de carga entre múltiples procesos

Una instancia de node.js corre en un único thread. Para aprovechar de las ventajas de los sistemas de varios núcleos, podemos lanzar un cluster de procesos de node para gestionar mejor la carga.

El módulo cluster permite crear una red de procesos que compartan los puertos del servidor

var cluster = require('cluster');
var http = require('http');
var numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
  console.log("Cpus encontradas:"  + process.pid)
  // Creamos un proceso trabajador por core.
  for (var i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  cluster.on('death', function(worker) {
    console.log('El trabajador ' + worker.pid + ' ha muerto');
  });
} else {
  // Los procesos trabajadores tienen un servidor http.
  http.Server(function(req, res) {
    res.writeHead(200);
    res.end("Hola mundo desde el proceso " + process.pid+ "\n");
  }).listen(8000);
}

Recargando la página varias veces veo que saca  "Hola mundo desde el proceso 9020" y "Hola mundo desde el proceso 420".


Mejoras en depuración por línea de comando

El motor V8 viene un un potente depurador que se puede acceder mediante el protocolo TCP. Para simplificarnos la vida, node.js trae un cliente para dicho depurador. Para utilizarlo hay que arrancar node.js con el argumento debug. Aparecerá a un prompt en el que se pueden realizar las tareas de depuración. A continuación muestro un ejemplo donde se utilizan los siguiente comandos:

  • cont : continúa hasta el siguiente punto de interrupción
  • next :  ejecuta siguiente línea de comando
  • repl : abre el "repl" del debugger, que nos permite ejecutar en la linea de comandos evaluaciones contra el contexto de la aplicación
  • quit : salir

El ejercicio:

C:\node.js>node debug miscript.js
< debugger listening on port 5858
connecting... ok
break in C:\node.js\miscript.js:2
  1 // miscript.js
  2 x = 5;
  3 setTimeout(function () {
  4   debugger;
debug> cont
< Hola
break in C:\node.js\miscript.js:4
  2 x = 5;
  3 setTimeout(function () {
  4   debugger;
  5   console.log("mundo!");
  6 }, 1000);
debug> next
break in C:\node.js\miscript.js:5
  3 setTimeout(function () {
  4   debugger;
  5   console.log("mundo!");
  6 }, 1000);
  7 console.log("Hola");
debug> repl
Press Ctrl + C to leave debug repl
> x
5
> 2+2
4
debug> next
break in C:\node.js\miscript.js:6
  4   debugger;
  5   console.log("mundo!");
  6 }, 1000);
  7 console.log("Hola");
  8 });
< mundo!
debug> quit

El fichero miscript.js es el siguiente:
// miscript.js
x = 5;
setTimeout(function () {
  debugger;
  console.log("mundo!");
}, 1000);
console.log("Hola");

Es bastante parecido a lo que se puede hacer por línea de comando en el depurador de Chrome (aunque ahora con al utilidad de desarrolladores apenas se usa).

Tienes una referencia a los comandos de depuración aquí: http://nodejs.org/docs/v0.6.0/api/debugger.html#commands_reference

Módulo de compresión zlib

Con este módulo resulta muy sencillo utilizar gzip, tanto al servir páginas como al consumir servicios en modo cliente. Los siguientes código lo ilustran perfectamente:


//ejemplo de un cliente con soporte gzip
//www.apache.org devuelve la pagina con gzip. 34KB de tamaño 9KB de trafico con gzip
var zlib = require('zlib');
var http = require('http');
var fs = require('fs');
var request = http.get({ host: 'www.apache.org', 
                         path: '/',
                         port: 80,
                         headers: { 'Accept-Encoding': 'gzip,deflate' } });
request.on('response', function(response) {
  var output = fs.createWriteStream('www.apache.org.index.html');
  switch (response.headers['content-encoding']) {
    // se puede usar zlib.createUnzip() para gestionar ambos casos
    case 'gzip':
      response.pipe(zlib.createGunzip()).pipe(output);
      break;
    case 'deflate':
      response.pipe(zlib.createInflate()).pipe(output);
      break;
    default:
      response.pipe(output);
      break;
  }
});

Tras ejecutar este script y podremos ver en el fichero "www.apache.org.index.html" el código fuente descargado.

A nivel de servidor es igual de sencillo:


// ejemplo de servidor
// Nota: hacer un gzip en cada petición es muy caro. Sería más 
// eficiente cachear la respuesta comprimida

var zlib = require('zlib');
var http = require('http');
var fs = require('fs');
http.createServer(function(request, response) {
  var raw = fs.createReadStream('index.html');
  var acceptEncoding = request.headers['accept-encoding'];
  if (!acceptEncoding) {
    acceptEncoding = '';
  }

  // Nota: this is not a conformant accept-encoding parser.
  // See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3
  if (acceptEncoding.match(/\bdeflate\b/)) {
    response.writeHead(200, { 'content-encoding': 'deflate' });
    raw.pipe(zlib.createDeflate()).pipe(response);
  } else if (acceptEncoding.match(/\bgzip\b/)) {
    response.writeHead(200, { 'content-encoding': 'gzip' });
    raw.pipe(zlib.createGzip()).pipe(response);
  } else {
    response.writeHead(200, {});
    raw.pipe(response);
  }
}).listen(1337);


Mejor soporte para comunicación entre procesos

El método fork() es un caso especial de spawn() para crear procesos de node.js. Adicionalmente a los métodos habituales de un proceso hijo (ChildProcess) creados por spawn, el objeto devuelto por fork tiene un canal de comunicaciones con su padre. A través del canal se envian mensajes con child.send(message, [sendHandle]) y los mensajes se reciben mediante el evento "message" en el hijo.

Los dos siguientes scripts lo ilustran perfectamente.
padre.js
var cp = require('child_process');
var hijo = cp.fork(__dirname + '/hijo.js');
hijo.on('message', function(m) {
  console.log('El PADRE recibió el mensaje:', m);
});
hijo.send({ hola: 'mundo' });

hijo.js
process.on('message', function(m) {
  console.log('El HIJO recibió el mensaje:', m);
});
process.send({ foo: 'bar' });

La ejecución queda como sigue:
C:\node.js>node padre.js
El PADRE recibió el mensaje: { foo: 'bar' }
El HIJO recibió el mensaje: { hola: 'mundo' }



sábado, 5 de noviembre de 2011

Herramientas para facilitar el desarrollo con node.js

Hoy estoy comenzando un pequeño ejercicio consistente en programar una pequeña aplicación web en node.js que optimice imágenes en formato png.

La idea es tener un formulario con el que se suba una imagen en formato png y se optimice con un optipng o alguna librería parecida. Si además, podemos ir informando al cliente del avance del proceso de upload y optimización en tiempo real, pues mejor que mejor.

En el ejercicio me gustaría probar las siguientes herramientas:
De momento, os presento unos ejemplos de como se usan las herramientas. En los siguientes posts iré contando los avances que haga en el ejercicio y como iré usando las distintas piezas (con más o menos acierto).

Express.js
Resulta muy cómodo para responder a urls amigables y agregar middleware:


var app = require('express').createServer();

//middleware para logs
app.use(express.logger({ format: ':method :url' }));

app.get('/', function(req, res){
  res.send('Hola mundo');
});

//urls amigables con parámetros
app.get('/usuario/:id', function(req, res){
    res.send('Usuario ' + req.params.id);
});

app.listen(8000);

Jade
Al más puro estilo haml, una plantilla como

!!! 5
html(lang="en")
  head
    title= pageTitle
    script(type='text/javascript')
      if (foo) {
         bar()
      }
  body
    h1 Jade - node template engine
    #container
      - if (youAreUsingJade)
        p You are amazing
      - else
        p Get on it!

pasa a ser

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Jade</title>
    <script type="text/javascript"> 
      if(foo) {
        bar()
      }
    </script> 
  </head> 
  <body>
    <h1>Jade - node template engine</h1> 
    <div id="container"> 
      <p>You are amazing</p>
    </div> 
  </body>
</html>


Connect-form
Para gestionar  los uploads de ficheros (para una app de express):

  app.post('/', function(req, res, next){
  // connect-form adds the req.form object
  // we can (optionally) define onComplete, passing
  // the exception (if any) fields parsed, and files parsed
  req.form.complete(function(err, fields, files){
    if (err) {
      next(err);
    } else {
      console.log('\nuploaded %s to %s'
      ,  files.image.filename
      , files.image.path);
     res.redirect('back');
    }
  });

  // We can add listeners for several form
  // events such as "progress"
  req.form.on('progress', function(bytesReceived, bytesExpected){
    var percent = (bytesReceived / bytesExpected * 100) | 0;
     process.stdout.write('Uploading: %' + percent + '\r');
  });
});




Socket.io
La gestión de las comunicaciones entre cliente y servidor se realiza en base a eventos.

En el servidor:
var io = require('socket.io').listen(80);
io.sockets.on('connection', function (socket) {
  socket.emit('news', { hello: 'world' });
  socket.on('my other event', function (data) {
    console.log(data);
  });
});

En el cliente:

<script src="/socket.io/socket.io.js"></script>
<script>
  var socket = io.connect('http://localhost');
  socket.on('news', function (data) {
    console.log(data);
    socket.emit('my other event', { my: 'data' });
  });
</script>

miércoles, 2 de noviembre de 2011

Servidor https con node.js

Montar un servidor https con node.js es casi trivial. Realizando dos sencillas tareas, lo tendremos funcionando en muy poco tiempo. La primera y más tediosa (por decir algo) es generar la clave y el certificado. La segunda tarea es crear y arrancar el servidor.

1. Generar el certificado
Esta tarea se hace en tres pasos:

1.a Primero generamos la clave privada:

openssl genrsa -des3 -out localhost.key 1024

Nos pedira una password para la clave que utilizaremos en los siguientes pasos:

Generating RSA private key, 1024 bit long modulus
.....................++++++
.......++++++
e is 65537 (0x10001)
Enter pass phrase for localhost.key:

La clave tiene el siguiente aspecto:

cat localhost.key 
 
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,38E8987484A8E569

WrRipQoSD+Tf8EmIiZHpd0ucL5gP5wFmxPAeUdIvEvBGCobPafPHCGi6u7B8yHtc
fykQK3SO+1BueMBgkha0GGLd8t+aENUtZK9rcrfUg2XUL0a7BvVZD2LU46zChtD7
YJS7UHyc53m5Lj4OX80Xq2wzL5OyHI064cCMwLthkj75onVevyVGniZm8xfV9Ez2
VpnVlu/djODNIoKfOGhhRejKi36sZNaPouwa9ubtaZyW9xzGrsH5M3ufMZpXe1Gf
W9rQ5x9oTyGZE7fbHj4UKRnXf/DpgY0rXcgs77P9J+kQ0oa5NsK/qxPSwveS1Utt
0SD1TTAvAPnb2CUd7NgVkYsfPpD/A/p1b5mL8nQrhO6BNP/ARKG5k7MfDEYmvbyN
OeVaaMxm2gy7tn0Z3XI8oWstnXGV4NfebSyDfsb0aCtaaK5DFRkWgt6Navc2d2bL
71lM2ipEmC2ZpXm5RsAl65Ghw2BW/XWTAwlUpP274c8adOzbSYeG4FwMHAr8tnK9
kYc6BKkGqqh6NDwlOfD2HUo2zy+41GmoNm4l9Onv914Gac2QRgsw/EmetwZxGFtZ
5cyN4YddVJq/L5CVIdLThVym63qgSoGZ4BLtxJAaW2dAq+F9Z31NfxOYOFbIjaK8
sXF5GJhPlUIY2N/1ofdH7XbilgSepD2Dvvld4nmanix19dGRW2cKGgq8Pojmq6lj
kDGPGVMksjfiSulZJsRgrQl+xHOvFUY74fihqzHWIya+6nqE9dN/QwEyffaHrHk3
AHHko5RAHcRjG93fpgCke5zwFPgz4torf+qDBj0/NVJBF951pW8euA==
-----END RSA PRIVATE KEY-----

1.b. Lo siguiente es generar el CSR o (Certificate Signing Request, petición de firma de certificado)

openssl req -new -key localhost.key -out localhost.csr

Rellenamos los datos que se nos van pidiendo:

Enter pass phrase for localhost.key:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:ES
State or Province Name (full name) [Some-State]:Madrid
Locality Name (eg, city) []:Madrid
Organization Name (eg, company) [Internet Widgits Pty Ltd]:NewCo,s.l.
Organizational Unit Name (eg, section) []:Dpto de IT    
Common Name (eg, YOUR name) []:NewCo
Email Address []:localhost@localhost

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

El aspecto del csr es parecido:

-----BEGIN CERTIFICATE REQUEST-----
MIIB6DCCAVECAQAwgY0xCzAJBgNVBAYTAkVTMQ8wDQYDVQQIEwZNYWRyaWQxDzAN
BgNVBAcTBk1hZHJpZDANBDHAGA1UEChMKmV3Q28scy5sLjETNAJDA1UECxMKRHB0
byBkZSBJVDEOMAwGA1UEAxMFTmV3Q28xIjAgBgkqhkiG9w0BCQEWE2xvY2FsaG9z
dEBsb2NhbGhvc3QwgZ8wDQYJKoZIhvcNAQE9Jan(gY0AMIGJAoGBALeou1eIQ0x2
NOtVujG0L5Mulo7CZIBPWZvOSZgR46JTtLmH/NTHA9dPM/MO+vuxo5X0X551x/N3
+MDP+GO1JAYkN/mXvW4jRMzM0AEWdd1sODxxCQtizFwj0biTrQ1StceGRbdsW6+2
B4acAfQGvimAfotdSvoO6NianDh7XQGzAgMBAAGgGjAYBgkqhkiG9w0BCQcxCxMJ
Y2hhbGxlbmdlMA0GCSqGSIb3DQEBBQUAA4GBAID43dLdWhSl5ni+jjThllIdkAMB
FLZHityDjo6bRi17pNUWoCWjKPHHHAIDHNg6OvcBiwCk+lrj1uC3/X0R3m4oeKRR
J+CCkP6Bsv804mS6X1c34TDEzR/mulAgQMvAHd2isPyrRdla1QaVgipMyuKhDPyE
c6iNvcf8iJ/ASSBW
-----END CERTIFICATE REQUEST-----

1.c. Y por último generamos el certificado ssl.

openssl x509 -req -days 365 -in localhost.csr -signkey localhost.key -out localhost.crt

Nos pedira la password de la clave

Signature ok
subject=/C=ES/ST=Madrid/L=Madrid/O=NewCo,s.l./OU=Dpto de IT/CN=NewCo/emailAddress=localhost@localhost
Getting Private key
Enter pass phrase for localhost.key:

Y ya tenemos todo listo para arrancar. Lo comprobamos:

ls -l

-rw-r--r--  1 miusuario  staff  952  2 nov 21:40 localhost.crt
-rw-r--r--  1 miusuario  staff  737  2 nov 21:32 localhost.csr
-rw-r--r--  1 miusuario  staff  963  2 nov 21:23 localhost.key

2. Crear y arrancar el servidor

El código no puede ser más sencillo. Creamos el fichero https-server.js con el siguiente código:

var https = require('https');
var fs = require('fs');

var options = {
  key: fs.readFileSync('localhost.key'),
  cert: fs.readFileSync('localhost.crt')
};

https.createServer(options, function (req, res) {
  res.writeHead(200);
  res.end("¡Responidiendo por SSL!\n");
}).listen(8000);

Lo arrancamos con:

node.js https-server.js

Nos pedirá la password de la clave:

Enter PEM pass phrase:

Ahora solo hay que acceder a https://localhost:8000 para ver en acción al servidor.

martes, 1 de noviembre de 2011

Microframeworks en javascript

Cada vez más se están popularizando los llamados "microframeworks" de javascript. Estos microframeworks son pequeñas librerías que habitualmente pesan de menos de 5kb y que están centradas un temas muy concretos.

Uno de las frases que se utilizan para justificar el uso de estos microframeworks es que tan importante es lo que tienen como lo que no tienen. En librerías más tradicionales como jQuery hay mucha funcionalidad adicional que no se usa, código para compatibilidad con navegadores a los que tal vez no se vaya a dar soporte, etc. que hacen que las páginas pesen más, que tarden más en cargar, que tarden más en inicializarse, que se consumen más recursos del navegador, etc. Con los microframeworks solo utilizamos lo que sea imprescindible, pudiendo mejorar la experiencia del usuario.

Thomas Fuchs (autor de script.aculo.us, miembro del grupo que mantiene prototype.js y autor de la nueva y pequeña librería zepto.js) ha publicado recientemente un buscador de microframeworks llamado microjs en http://microjs.com.

Como no es oro todo lo que reluce, y sin ánimo de ser extensivo, voy a escribir algunos pros y contras de los microframeworks:

Pros
  • Los microframeworks suelen ser pequeños, compactos y poco acoplados, funcionando como módulos con los que componer aplicaciones
  • Los microframeworks se focalizan en resolver un problema y en resolverlo bien, eliminando duplicidades de código y piezas que no se usen.
  • Los microframeworks suelen tener un nivel de calidad de código bastante aceptable. Al tener que ser tan pequeños, exigen un esfuerzo a los desarrolladores que tendrán que aprender nuevas y mejores técnicas para poder resolver el objetivo concreto dentro de esos límites de tamaño establecidos.
  • Cada microframework, usado de manera individual resulta bastante sencillo de entender. En poquísimo tiempo se puede estar usando satisfactoriamente un microframework que se acabe de descubrir. Aunque la documentación no sea todo lo buena que quisiéramos, entrando en el código fuente podemos descubrir como funciona.
  • Muchos de estos microframeworks, al no estar acoplados a elementos de los navegadores, pueden usarse indistintamente en cliente y en servidor con node.js.

Contras

  • Las librerías tradicionales como jQuery, Dojo, Motools, etc. tienen una base de usuarios muy amplia y, gracias a ello, una madurez de la que carecen los microframeworks.
  • La compatibilidad con navegadores está garantizada dentro de lo posible. Cuando hay que dar soporte a usuarios con IE6, por ejemplo, esto es un gran activo.
  • La actualización de microframeworks puede convertirse en un problema si tenemos que estar manteniendo una aplicación que utilice muchos, mientras que con las librerías tradicionales, el upgrade de versiones resulta más directo.
  • La documentación está centralizada en un único sitio. Con los microframeworks habrá que revisar la documentación en un sitio por cada microframework, y esta documentación puede tener una calidad dispar. De cara a la mantenibilidad, esto puede impactar negativamente.
  • Si se empiezan a utilizar demasiados microframeworks a la vez, tal vez el peso conjunto supere al de una de estas librerías, perdiendo la ventaja inicial que proporcionaban.

Como siempre ocurre en tecnología, la decisiones sobre si utilizar una y otra aproximación dependerá de muchos factores, pero en mi caso, después de algunas experiencias con webapps para móviles con jquery mobile (24kb min+gzip  más los 31kb correspondientes de jquery), tengo muy claro que le voy a dar una oportunidad seria a los microframeworks.

Como muestra un botón

Hace poco descubrí un "micro" microframework con la funcionalidad de publish/subscribe que me resultó muy interesante.

Se usa de la siguiente manera:

    //suscribirse a un canal
    var handle = subscribe("/some/topic", function(msg){
        console.log(msg);
    });

    //publicar en el canal
    publish("/some/topic", ["first time"]);
    publish("/some/topic", ["second time"]);

    //eliminar la suscripcion
    unsubscribe(handle);

    //la suscripción ya no esta escuchando el canal
    publish("/some/topic", ["message will not be logged"]);


El código fuente, que minimizado y gzip pesa solo 198 bytes, es tan solo esto:

(function(d){var e=d.c_||{};d.publish=function(a,b){for(var c=e[a],f=c?c.length:0;f--;)c[f].apply(d,b||[])};d.subscribe=function(a,b){e[a]||(e[a]=[]);e[a].push(b);return[a,b]};d.unsubscribe=function(a){for(var b=e[a[0]],a=a[1],c=b?b.length:0;c--;)b[c]===a&&b.splice(c,1)}})(this);


El código fuente sin minimizar resulta bastante más inteligible. La librería se llama MinPubSub de Daniel Lamb y la podeis encontrar en https://github.com/daniellmb/MinPubSub .



Republicación automática en node.js

A la hora de desarrollar es muy cómodo que nuestra aplicación se republique automáticamente cada vez que guardemos las modificaciones de un fichero de node.js. Para conseguir esto, lo más cómodo que he visto hasta ahora es utilizar supervisor (https://github.com/isaacs/node-supervisor).

Con supervisor la aplicación se volverá a cargar cada vez que se modifique un fichero .js o cada vez que la aplicación casque.

Para instalar supervisor utilizamos npm con el parámetro -g (global). La idea es tenerlo disponible en todos los desarrollos pero que no vaya integrado en ellos:

sudo npm install supervisor -g

Una vez instalado para que desplegar una app cada vez que modifiquemos solo hay que:

supervisor miapp.js

Así, pongamos que mi app.js tiene el siguiente código:

var http = require('http');
var port = 8080;
http.createServer(function (req, res) {
   res.writeHead(200, {'Content-Type': 'text/plain'});
   res.end('Hello Node.js\n');
}).listen(port);
console.log('Server running at port: ' + port);


En la consola veremos lo siguiente:

DEBUG: Running node-supervisor with 
DEBUG:   program 'miapp.js' 
DEBUG:   --watch '.' 
DEBUG:   --extensions 'node|js' 
DEBUG:   --exec 'node' 
DEBUG: Starting child process with 'node miapp.js' 
DEBUG: Watching directory '/Users/miusuario/Documents/.' for changes.
Server running at port: 8080

Cambiamos el puerto a 8081, guardamos y en la consola vemos:

DEBUG: crashing child 
DEBUG: Starting child process with 'node miapp.js'
Server running at port: 8081


Si accedemos con el navegador a http://localhost:8081 vemos que efectivamente el servidor a cambiado al nuevo puerto.