Resoluciones del Nuevo Mes: Febrero 2016

De nuevo es tiempo de escribir las nuevas resoluciones, primero repaso de las de enero:

– Trabajar en mejorar los ejemplos de trader de SimpleGA [pendiente]
– Trabajar en SimpleDatabase [pendiente]
– Trabajar en SimpleMemolap [completo] ver repo
– Trabajar en Memolap [completo] ver repo
– Mejorar AjGenesisNode [pendiente]
– Mejorar SharpGo [completo] ver repo
– Mejorar Husky [completo] ver repo
– Trabajar en GoLin [completo] ver repo

También estuve trabajando en:

– Crear SRedux, una librería similar a Redux [completo] ver repo
– Crear ImmuO, objetos immutables en JavaScript [completo] ver repo
– Comenzar CrysSharp, lenguaje de programación Crystal implementando en C# [completo] ver repo
– Comenzar CrysJS, lenguaje de programación Crystal implementado JavaScript [completo] ver repo
– Agregar ejemplos ReactJS a los ejemplos de JavaScript [completo] ver repo
– Mejorar ElixirJS [completo] ver repo
– Crear ReduMan, manejador de reducers en JavaScript [completo] ver repo
– Crear RkStreams, streams reactivos en JavaScript [completo] ver repo

Mis nuevas resoluciones:

– Mejorar AjGenesisNode-Express
– Trabajar en CrysJS
– Trabajar en CrysSharp
– Trabajar en Memolap
– Trabajar en SimpleMemolap
– Mejorar mis ejemplos de SimpleGA
– Mejorar SharpGo
– Mejorar Husky
– Trabajar en GoLin
– Mejorar ImmuO
– Más ejemplos ReactJS
– Mejorar Aktores
– Mejorar ErlSharp

Me diviegto como logco 😉

Nos leemos!

Angel “Java” Lopez
@ajlopez

Posted in Proyectos Open Source, C#, JavaScript, NodeJs | Leave a comment

RkStreams, streams reactivos en JavaScript/NodeJS (1)

Esta vez no tengo un caso de uso concreto, pero es algo que es interesante de implementar usando simplicity pays, baby steps, y el flujo de trabajo de TDD (Test-Driven Development). No son como los streams nativos de NodeJS. En este proyecto:

https://github.com/ajlopez/RkStreams

Un stream es como un canal, por el que entran y salen simples objetos/valores JavaScript. Debe ser parecido a algunos conceptos de Redux (pero sin manejo de estado inmutable) y se debe parecer a los mailboxes del lenguaje Elm.

Lo interesante que la funcionalidad que logré implementar hasta ahora sale muy simple usando TDD. Es un buen ejercicio, y pueden ver siempre el historial de commits para ver cómo el proyecto va creciendo en capacidades de a poco, como respuesta a un nuevo test que se plantea, de pasar de “rojo” a “verde”.

Veamos un primer test, crear y usar un stream:

var rks = require('..');

exports['create and use stream'] = function (test) {
    test.async();
    
    var stream = rks.stream();
    
    stream.process(function (data) {
        test.ok(data);
        test.equal(data, 1);
        test.done();
    });
    
    stream.post(1);
};

Como es usual en mis proyectos, no expongo una “clase” de JavaScript, sino un método factoría .stream(). Luego el .post() permite enviar un objeto por el stream. Y el .process() permite tener funciones digamos subscriptoras, que procesan cada mensaje que llegue al stream. Esto permite separar la generación de eventos/objetos, de su proceso.

Pero los streams también puede producir otros streams. Un ejemplo clásico es transformar los objetos, con un .map(), que construye un nuevo stream:

exports['map stream'] = function (test) {
    test.async();
    
    var counter = 0;
    
    var stream = rks.stream();
    
    stream.map(function (x) { return x * 2; }).process(function (data) {
        counter++;
        
        test.ok(data);
        test.equal(data, counter * 2);
        
        if (data === 6)
            test.done();
    });
    
    stream.post(1);
    stream.post(2);
    stream.post(3);
};

O también podemos unir dos streams, con .merge(), de nuevo, devuelve un nuevo stream:

exports['post three messages to streams and merge'] = function (test) {
    test.async();
    
    var counter = 0;
    
    var stream1 = rks.stream();
    var stream2 = rks.stream();
    
    stream1.merge(stream2)
        .process(function (data) {
        counter++;
        
        test.ok(data);
        test.equal(data, counter);
        
        if (data === 3)
            test.done();
    });
    
    stream1.post(1);
    stream2.post(2);
    stream1.post(3);
};


O podemos crear un nuevo stream que sólo emita los objetos que cumplan con un predicado, usando .filter():

exports['filter stream'] = function (test) {
    test.async();
    
    var stream = rks.stream();
    
    stream
        .filter(function (x) { return x % 2 === 0})
        .filter(function (x) { return x % 5 === 0})
        .process(function (data) {
            test.ok(data);
            test.equal(data, 10);
            test.done();
        });
    
    for (var k = 1; k <= 10; k++)
        stream.post(k);
};

Tengo pendiente decidir el tratamiento de las excepciones en los filtros, mapeos y procesos. También quiero implementar un .branch() que permite que un stream se alimente de otro.

En próximos posts: esas nuevas funcionalidades, .process() selectivo, y ver cómo va quedando por dentro la implementación (a hoy, son unas pocas líneas de código).

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Posted in JavaScript, NodeJs, Test-Driven Development, RkStreams, Programación Funcional, Programación Reactiva | Comments Off on RkStreams, streams reactivos en JavaScript/NodeJS (1)

SimpleXmlDoc, parseando XML en JavaScript/NodeJS (1)

De vez en cuando, en mis proyectos públicos y no públicos de NodeJS, me toca analizar (parsear) un texto XML. Por ejemplo en NodeDelicious. En esos casos suelo usar la librería xml2js. XML no es uno de mis formatos favoritos, pero siempre hay alguna API que aparece por ahí que no se enteró del cambio de siglo y sigue entregando XML en vez del tan ubicuo JSON.

Como ejercicio de TDD, escribí un simple parser de XML, cumpliendo con los casos que tengo previstos para un proyecto no público. Siempre trato, siguiendo baby steps, simplicidad, y casos de uso, implementar solamente lo que necesito. Pueden ver el resultado en:

https://github.com/ajlopez/SimpleXmlDoc

y evidencia de seguir el flujo de trabajo de TDD en el historial:

https://github.com/ajlopez/SimpleXmlDoc/commits/master

Comencé con algunos tests simples, como reconocer un elemento en un texto:

var elements = require('../lib/elements');

exports['get element'] = function (test) {
    var element = elements.element("");
    
    test.ok(element);
    test.equal(typeof element, 'object');
};

Vean que puse un archivo aparte, elements.js, dedicado a implementar el análisis y proceso de cada elemento de un documento. La API principal está expuesta en el archivo simplexmldoc.js, que recupera y usa a elements.js. Pero al comienzo, me concentré en los primeros tests de elementos. Otro ejemplo al que llegué en algún momento:

exports['get autoclose element'] = function (test) {
    var element = elements.element("");
    
    test.ok(element);
    test.equal(typeof element, 'object');
    test.equal(element.elements().count(), 0);
    test.equal(element.text(), '');
};

O reconocer un namespace:

exports['get element with namespace'] = function (test) {
    var element = elements.element("");
    
    test.ok(element);
    test.equal(typeof element, 'object');
    test.equal(element.ns(), 'ns');
    test.equal(element.tag(), 'tag');
    test.equal(element.name(), 'ns:tag');
};

¿Cómo fue siendo implementado un elemento? Bueno, siguiendo TDD y baby steps, se fue implementando de a poco. En vez de definir toda su programación de una vez, fue creciendo en capacidades a medida que se plantearon nuevos casos de uso en los tests.

Como quería procesar un texto, sin partirlo directamente en palabras y otros tokens, vean cómo fue quedando internamente la implementación de un elemento:

function Element(text, offset) {
    offset = offset || 0;
    offset = find.open(text, offset);
    
    var tag = parseName(text, offset + 1, 'tag');
    var ns = null;
    
    if (text[tag.offset + tag.length] === ':') {
        ns = tag;
        tag = parseName(text, tag.offset + tag.length + 1, 'tag');
    }
    
    var nextgt = find.close(text, tag.offset + tag.length);    
    var nextlt = find.open(text, nextgt + 1);
    var elements = [];
    
    // ..... 

Un elemento tiene un texto y un desplazamiento dentro de ese texto. Las funciones auxiliares find.open y find.close se encargan de ubicar el comienzo de un elemento con < (menor) y el fin de un elemento (o de su comienzo) con > (mayor).

Al usar texto y desplazamiento, el texto original (posiblemente el documento completo), no tiene que partirse en otros textos, strings. Sino que queda de una pieza, y solamente cada elemento tiene punteros/desplazamientos a donde comienza y donde termina cada elemento. Esa implementación queda totalmente oculta en la API expuesta. Y si se me ocurre una nueva forma de implementarla, dado los tests de la API, tengo el soporte necesario para cambiar la programación interna, sin romper algo (o por lo menos, los tests me avisarán sin rompí algo).

Como siempre remarco, esto es lo que da TDD junto con búsqueda de simplicidad y baby steps: el desarrollo del software que estamos construyendo, de a poco, como si fuera un organismo que va creciendo. Notablemente, mi experiencia indica que esta forma de trabajo permite ir acomodando nuevos casos de usos, sin complicar lo escrito. Es rato que tenga complejidad accidental en un proyecto escrito de esta forma.

Por supuesto que debe haber librerías más poderosas. Pero es interesante programarla, y entrenarse en TDD. Además, ya la estoy consumiendo en un proyecto no público, siempre hacer “dog fooding”.

Próximos posts: alguna descripción de la API expuestas, casos de uso, conversión de y a objetos JavaScript, y de y a strings

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Posted in Proyectos Open Source, JavaScript, NodeJs, SimpleXmlDoc | Comments Off on SimpleXmlDoc, parseando XML en JavaScript/NodeJS (1)

JSDates, simples funciones de fecha en JavaScript/NodeJS (1)

En el proyecto MoneyPool y en otros, me ha tocado manipular fechas. En general, he seguido el camino más sencillo, y represento las fechas según los casos de uso que necesito. En ese proyecto, por ejemplo, necesita fechas de días, para ordenar movimientos o para recuperar movimientos en un rango de fechas. Antes que usar el objeto Date de JavaScript, decidí usar lo más simple: un simple string con formato YYYYMMDD (cuatro dígitos de año, dos de mes, y dos de días). Vean que no incluyo formateo ni por ejemplo, zona horaria, PORQUE los casos de uso que tengo no los necesitan. Y cuando los necesite, lo agregaré con confianza, porque he seguido el flujo de trabajo de TDD (Test-Driven Development).

Pero en esta semana, en un proyecto no público, aparecieron otros casos de uso, como agregar simplemente días, meses, y convertir a formatos de ISO String (este caso de uso lo podría discutir, pero así es como lo espera el programa cliente que va a consumir una API que exponemos). Podría usar algún módulo ya escrito, como moment, pero me pareció que para el uso que necesitamos ahora, no hacía falta. Ya saben como pienso: prefiero lo simple a lo fácil. Así que si hay que escribir código para casos de uso nuevos, no reniego y lo hago, aunque podría evitarlo con importar una librería. Pero insisto: mis decisiones se guían por simplicidad, baby steps, guiado por caso de uso, y todo eso me decía al oído “no hace falta moment… “ 😉

Así que ayer, siguiendo TDD, me puse a escribir:

https://github.com/ajlopez/JSDates

usando NodeJS, pero debería servir también desde el browser (podría escribir test adecuados). La idea es tener una representación simple de una fecha (no fecha/hora), un fecha/hora, y por ahora, poder sumarle días, meses, y convertir el resultado a un Date nativo de JavaScript cuando haga falta.

Un test típico:

exports['add months'] = function (test) {
    var date = jsdates.date(2016, 1, 12).addMonths(2);
    var ndate = new Date(2016, 2, 12);
    
    test.equal(date.year(), ndate.getFullYear());
    test.equal(date.month(), ndate.getMonth() + 1);
    test.equal(date.day(), ndate.getDate());
    
    test.equal(date.hours(), 0);
    test.equal(date.minutes(), 0);
    test.equal(date.seconds(), 0);
};

También hay funciones para conseguir el today (fecha actual) y el now (fecha/hora actual) y comenzar a manipularlos. Un test sobre now:

exports['create now'] = function (test) {
    var now = jsdates.now();
    
    test.ok(now);
    test.equal(typeof now, 'object');
    
    var date = new Date();
    
    test.equal(now.year(), date.getFullYear());
    test.equal(now.month(), date.getMonth() + 1);
    test.equal(now.day(), date.getDate());
    if (now.hours() === date.getHours())
        if (now.minutes() === date.getMinutes())
            test.ok(now.seconds() === date.getSeconds() || now.seconds() + 1 === date.getSeconds())
        else {
            test.ok(now.minutes() + 1 === date.getMinutes());
            test.equal(now.seconds(), 59);
            test.equal(date.getSeconds(), 0);
        }
    else {
        test.equal(now.hours() + 1, date.getHours());
        test.equal(now.minutes(), 59);
        test.equal(date.getMinutes(), 0)
    }
};

Vean que es algo frágil. Podría ser que el now que devuelve la librería no coincida en valores con el new Date() del tests. Lo que hice fue agregar un test experimental, usando el viejo truco de inyectarle a la librería cuál es el valor de now que quiero que use, en vez de usar new Date() nativo:

exports['use and create now'] = function (test) {
    var date = new Date();
    
    jsdates.set('now', date);
    
    var now = jsdates.now();
    
    test.ok(now);
    test.equal(typeof now, 'object');
    
    var date = new Date();
    
    test.equal(now.year(), date.getFullYear());
    test.equal(now.month(), date.getMonth() + 1);
    test.equal(now.day(), date.getDate());
    test.equal(now.hours(), date.getHours());
    test.equal(now.minutes(), date.getMinutes());
    test.equal(now.seconds(), date.getSeconds());
};

Todo es mejorable, y teniendo TDD, me aseguro que cualquier cambio no rompa la conducta esperada. La cuestión que luego de unas líneas de código, ya lo tengo publicado y usado en otro proyecto, habiendo podido saltearme el uso de moment. Seguramente habrá otras librerías simples, pero me gusta practicar programación y en especial TDD.

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Posted in JavaScript, NodeJs, Test-Driven Development, JSDates | Comments Off on JSDates, simples funciones de fecha en JavaScript/NodeJS (1)

SimpleTasks: Ejecutando Tareas en NodeJS (1)

Hace unos años escribí SimpleBus y lo usé como uno de los ejemplo en mi charla en JSConf Uruguay 2015. Puede recibir mensajes y los envía a todo subscriptor interesado. El subscriptor puede especificar las características de los mensajes en los que está interesado, ya sea por valores de propiedades o por una función predicado. Ver el ejemplo distribuido:

https://github.com/ajlopez/SimpleBus/tree/master/samples/Market

Pero en estos días, tengo otro caso de uso en puerta: ejecutar tareas. Una tarea debería ser descripta en un simple objeto JavaScript plano, sin funciones (serializable a JSON), y los subscriptores pueden ejecutar algunos tipos de tareas. Las tareas se distinguen por una propiedad especial, que es el tipo. Como siempre, fui programando todo usando el flujo de trabajo de TDD (Test-Driven Development) en el proyecto:

https://github.com/ajlopez/SimpleTasks

Como práctica de TDD y de programación. Debe haber alguna librería que cumpla con los casos de uso que quiero manejar, pero siempre es bueno practicar, y al practicar, uno comprende mejor el dominio que está tratando de implementar, los problemas que pueden surgir en la implementación. Y también me entreno en el camino de la simplicidad, y en aprender a tomar “baby-steps”, en vez de solucionar todo el problema de una sola vez. Esto permite que la implementación vaya emergiendo de los mini-casos de uso que van planteando los tests.

Un test que prueba el envío y ejecución de una tarea:

exports['post and subscribe task'] = function (test) {
    test.async();
    
    var engine = st.engine();
    
    engine.post({ type: 'process', options: { value: 42 } });
    
    engine.subscribe('process', function (task) {
        test.ok(task);
        test.equal(task.type, 'process');
        test.ok(task.options);
        test.equal(task.options.value, 42);
        
        engine.stop();
        
        test.done();
    });
}

El gran método es el .post: recibe una tarea, y se encarga de enviarla a UN SOLO subscriptor de esa tarea. Esto no sería más que un pasaje de mensaje, pero también hay opciones: el método post recibe un segundo argumento opcional, que describe algunas características de la tarea a ejecutar, por ejemplo, la fecha/hora de comienzo (no ejecutar la tarea hasta llegar a esa fecha/hora). Ejemplo de test:

exports['post and subscribe task with starting option'] = function (test) {
    test.async();
    
    var engine = st.engine();
    
    var start = (new Date()).getTime();
    var starting = dates.toNormalDateTimeString(new Date(start + 2500));
    
    engine.post({ type: 'process', options: { value: 42 } }, { starting: starting });
    
    engine.subscribe('process', function (task) {
        var now = (new Date()).getTime();

        test.ok(task);
        test.equal(task.type, 'process');
        test.ok(task.options);
        test.equal(task.options.value, 42);

        test.ok(now > start);
        test.ok(now > start + 1800);
        test.ok(now < start + 3500);
        
        engine.stop();
        
        test.done();
    });
}

Pero quiero agregarle más opciones: el principal caso de uso que tengo en mente, es tareas que se repiten, por ejemplo, por día, por semana todos los martes, o por hora hasta 5 veces, y así.

Una cosa que no maneja, en las tareas a ejecutar inmediatamente, es el “back pressure”: no detiene a los productores de tareas si se satura. Un tema a discutir si es necesario en algún caso de uso, imagino el uso de una cola bloqueante.

También quiero poder distribuir tareas a consumidores/ejecutores remotos. Podría usar un SimpleQueue, pero esta vez voy a ir experimentando con ActiveMQ y el protocolo Stomp, usando stomp-client.

Próximos posts: implementación de esas opciones, ejemplo local, y ejemplo distribuido (imagino que un webcrawler).

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Posted in Proyectos Open Source, JavaScript, NodeJs, Test-Driven Development, SimpleTasks | Comments Off on SimpleTasks: Ejecutando Tareas en NodeJS (1)

Motor de Reglas SimpleRules (3) Agregando Hechos

Anterior Post

Para mi proyecto de motor de reglas en JavaScript:

https://github.com/ajlopez/SimpleRules

usaba un modelo. ¿Qué es un modelo? Un simple objeto JavaScript que se entrega al motor de reglas, y éste lo usa como base para emitir acciones, generalmente modificando el mismo modelo. Un modelo, por ejemplo, podría representar a un paciente médico, con su temperatura, sus datos de edad, género, etc. Un ejemplo de test:

exports['add and run rule on model'] = function (test) {
    var model = { temperature: 37 };
    
    var eng = engine({});
    
    eng.rule({ name: 'rule1', title: 'Rule 1' })
        .when("model.temperature == 37")
        .then("model.hasFever = true");
        
    eng.run(model);
    
    test.equal(model.temperature, 37);
    test.equal(model.hasFever, true);
}

En este caso, el modelo se ve enriquecido por la acción de una regla, detectando que tiene fiebre. Pero he visto que en otros motoros de reglas también se maneja otro modelo, lo voy a llamar “facts” (hechos) que son los que el motor de reglas agrega, separado del modelo de entrada. Entonces, decidí escribir un test en el que se le entrega al motor un modelo, y objeto de hechos vacío, obteniendo un resultado:

exports['add and run rule on model and facts'] = function (test) {
    var model = { temperature: 37 };
    var facts = { };
    
    var eng = engine({});
    
    eng.rule({ name: 'rule1', title: 'Rule 1' })
        .when("model.temperature == 37")
        .then("facts.hasFever = true");
        
    eng.run(model, facts);
    
    test.equal(model.temperature, 37);
    test.strictEqual(model.hasFever, undefined);
    test.equal(facts.hasFever, true);
}

También escribí un test donde los hechos se usan como entrada, como datos para las condiciones que se deben cumplir en alguna regla:

exports['add and run rule using facts in condition'] = function (test) {
    var model = { age: 80 };
    var facts = { hasFever: true };
    
    var eng = engine({});
    
    eng.rule({ name: 'rule1', title: 'Rule 1' })
        .when("model.age > 70")
        .when("facts.hasFever")
        .then("facts.critical = true");
        
    eng.run(model, facts);
    
    test.equal(facts.critical, true);
}

Una vez escrito esto, modifiqué la implementación para que cada condición y cada acción (internamente son funciones) admita como argmuentos modelo Y hechos, en vez de solamente manipular el modelo. Una vez modificada la implementación interna (apenas modificar unas líneas en un lenguaje dinámico como JavaScript), los tests de arriba pasaron, y ya tengo un motor de reglas que maneja un modelo y hechos, como objetos separados. Realmente la modificación fue simple, gracias a seguir el flujo de trabajo de TDD (Test-Driven Development) desde el principio. Y además de pasar los tests nuevos de arriba, quedó asegurado que los anteriores tests siguen pasando.

Esto es uno de los beneficios de esta forma de trabajo: la implementación de nuevos casos de uso, las refactorizaciones internas, y los rediseños de la API, se pueden encarar sin mayor “dolor”, gracias al proceso ágil.

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Posted in JavaScript, NodeJs, Test-Driven Development, SimpleRules | Comments Off on Motor de Reglas SimpleRules (3) Agregando Hechos

Resoluciones del Nuevo Mes: Enero 2016

Un nuevo año comienza, y es hora de escribir las resoluciones para enero. Primero, repaso de las de diciembre:

– Preparar charla sobre FinTech [pendiente]
– Trabajar en Husky [pendiente]
– Trabajar en SharpGo [completo] ver repo
– Mejorar AjGenesis para Node [completo] ver repo
– Publicar AjLispJava [pendiente]
– Trabajar en Golin [pendiente]
– Trabajar en RkModel [pendiente]
– Mejorar SimpleDT [completo] ver repo

Additionally I was working on:

– Mejorar Memolap [completo] ver repo
– Mejorar SimpleMemolap [completo] ver repo
– Mejorar ElixirJS [completo] ver repo
– Mejorar SimpleDatabase [completo] ver repo
– Mejorar SimpleLisp [completo] ver repo
– Comenzar SimpleTasks [completo] ver repo
– Publicar una nueva versión de SimpleAsync [completo] ver repo
– Comenzar MoneyPool [completo] ver repo
– Comenzar y publicar MongoRepo [completo] ver repo
– Refactorizar SimpleGA y agregar un nuevo ejemplo de traders [completo] ver repo
– Actualizar AjGenesisNode-Entity a una nueva versión [completo] ver repo
– Actualizar AjGenesisNode-Model a una nueva versión [completo] ver repo
– Actualizar AjGenesisNode-Express una nueva versión [completo] ver repo
– Simplificar SimpleRules y agregarle un DSL [completo] ver repo
– Mejorar OStore y publicar una nueva versión [completo] ver repo
– Agregar soporte de JSON a SimpleDSL [completo] ver repo
– Agregar opción recursiva a SimpleUnit [completo] ver repo
– Comenzar BTraderLib [completo] ver repo

Mis resoluciones para el nuevo mes que comienza:

– Trabajar en mejorar los ejemplos de trader de SimpleGA
– Trabajar en SimpleDatabase
– Trabajar en SimpleMemolap
– Trabajar en Memolap
– Mejorar AjGenesisNode
– Mejorar SharpGo
– Mejorar Husky
– Trabajar en GoLin

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Posted in Proyectos Open Source, C#, JavaScript, NodeJs | Leave a comment

SimpleDatabase, implementando RethinkDB en NodeJS (1)

Hace ya unos años entró en mi radar el proyecto:

http://www.rethinkdb.com

y si bien no tuve que usarlo en ninguno de mis trabajos profesionales, me interesó la idea que expresa en su API. Esto me llevó a comenzar a implementarlo, en memoria, con JavaScript/NodeJS, y con métodos sincrónicos (otro que nació hace tiempo ya es el OStore, que ya emula bastante de MongoDB, directamente en memoria). Y por supuesto, como una excusa más para practicar TDD.

Pueden leer más de la API original del proyecto en:

http://www.rethinkdb.com/api/

Mi implementación inicialmente tenía métodos como:

sdb.dbCreate('purchases').run();

Vean que el .run() era el que lanzaba la ejecución de todo lo que se expresaba antes (algo así también implementé en SimpleAsync, y en estos días saqué la necesidad de poner .run(), simplemente se ejecuta). Pero el run() de RethinkDB es algo más interesante. Mientras que la fluent API va expresando lo que se va a ejecutar, el run lo lanza y ejecuta contra una connection.

Entonces. en estos días festivos, refactoricé mi código a soportar cosas como en este test::

exports['create database'] = function (test) {
    test.async();
    
    sdb.dbCreate('purchases').run(connection, function (err, data) {
        test.ok(!err);
        test.ok(data);
        test.done();
    });
};

La connection por ahora es local, y se obtiene con código como:

exports['get connection'] = function (test) {
    test.async();
    
    sdb.connection(function (err, data) {
        test.ok(!err);
        test.ok(data);
        connection = data;
        test.done();
    });
};

En la API original de RethingDB, al pedir una connection se especifica en que servidor y puerto está escuchando.

Por eso, estoy pensando en luego refactorizar para tener una connection que establezca una comunicación con un servidor remoto. No parece complicado, luego del trabajo que realicé de llamar a objetos remotos (ver SimpleRemote).

Lo interesante es que toda la fluent API que precede al run, va armando internamente lo que tiene que ejecutar, y luego lo lanza contra la connection que se le provee al final. Cuando la connection sea remota, tengo pensado serializar a JSON el comando armado y enviarlo a ejecutar en el servidor remoto (una estrategia parecida a la que ví en la implementacion de Linq en .NET)

La API original es bastante larga, y me falta implementar decenas de métodos. Pero hay algunos interesantes, que ya van tomando forma, como la inserción de registros:

sdb.db('test')
.table('users')
.insert([
    { name: 'Adam', age: 800 },
    { name: 'Eve', age: 700 }
])
.run(connection, function (err, data) {
    // callback code
});

y el armado de funciones a aplicar en un update o en un filter:

sdb
.db('company')
.table('persons')
.get(adamId)
.update({ age: sdb.row('age').add(100) })
.run(connection, next);

Para los tests, estuve haciendo “dog fooding” de mi proyecto SimpleAsync, que permite encadenar callbacks, ejemplo:

exports['insert document'] = function (test) {
    test.async();
    
    var doc = { name: 'Adam' };
    
    async()
    .exec(function (next) {
        sdb
        .db('test')
        .table('persons')
        .insert(doc)
        .run(connection, next);
    })
    .then(function (result, next) {
        test.ok(result);
        test.equal(result.inserted, 1);
        test.equal(result.errors, 0);
        test.ok(result.generated_keys);
        test.equal(result.generated_keys.length, 1);
        test.equal(result.generated_keys[0], 1);

        test.done();
    })
};

Espero seguir descriendo la implementación en próximos posts. TDD (Test-Driven Development) me ha servido en la semana pasada para encarar el refactor quirúrgico de reimplementar todo a usar una connection y método run asincrónico. No ha sido fácil, pero tampoco fue algo muy difícil: una vez establecida firmemente la implementación de una connection local, cimentada con el flujo de trabajo de TDD, luego pude refactorizar el API pública para que aceptara ser asincrónica en el run.

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Posted in Proyectos Open Source, JavaScript, NodeJs, Test-Driven Development, SimpleDatabase | Comments Off on SimpleDatabase, implementando RethinkDB en NodeJS (1)

Motor de Reglas SimpleRules (2) Implementando un DSL

Anterior Post
Siguiente Post

Otro de los casos de uso que quiero implementar para usar mi proyecto de motor de reglas:

https://github.com/ajlopez/SimpleRules

Es poder definir las reglas en un lenguaje textual simple, podría decir en un DSL (Domain Specific Language). Ejemplo de lo que tenía en mente:

rule
    name FeverRule
    when
        model.temperature >= 38
    then
        model.fever = true
        
rule
    name CriticalFever
    when
        model.temperature >= 40
        model.age >= 12
    then
        model.critical = true

Quiero implementarlo usando indentación como en Python, para evitar sobrecargarlo con palabras o caracteres delimitadores de comienzo y fin de cada parte, como begin y end o las clásicas llaves { y }

Un opción sería usar un parser, tomar una librería ya existente, usar mi propio proyecto SimpleGrammar, o definir y consumir una gramática usando Backus-Naur Format o similares. Pero pensé que debía haber una forma más simple. Y al poco de meditar encontré un camino.

La primer idea es leer un texto, partirlo en líneas, y en cada línea proceder a calcular el nivel de indentación que tiene. Ejemplo de test:

exports['parse text'] = function (test) {
    var result = lines("rule");
    
    test.deepEqual(result, [ { indent: 0, text: "rule" } ]);
};

exports['parse text with indent and spaces'] = function (test) {
    var result = lines("  rule  ");
    
    test.deepEqual(result, [ { indent: 2, text: "rule" } ]);
};

exports['parse text with two lines'] = function (test) {
    var result = lines("rule\n  when");
    
    test.deepEqual(result, [ 
        { indent: 0, text: "rule" },
        { indent: 2, text: "when" }
    ]);
};

exports['parse text with two lines and carriage return'] = function (test) {
    var result = lines("rule\r\n  when\r\n");
    
    test.deepEqual(result, [ 
        { indent: 0, text: "rule" },
        { indent: 2, text: "when" }
    ]);
};

Luego, fui armando una librería auxiliar a usar por el módulo principal, que tomara la salida de las líneas analizadas, y forme un árbol de textos, donde ya no importa la indentación. Ejemplos de tests:

var parse = require('../lib/parse');

exports['parse line'] = function (test) {
    var result = parse("rule");
    
    test.deepEqual(result, [{ text: "rule" }]);
};

exports['parse line and indented line'] = function (test) {
    var result = parse("rule\n  when");
    
    test.deepEqual(result, [
        { text: "rule", elements: [
            { text: "when" }
        ]}
    ]);
};

exports['parse rule'] = function (test) {
    var result = parse("rule\n  when\n    model.temperature >= 37\n  then\n    facts.fever = true");
    
    test.deepEqual(result, [
        { text: "rule", elements: [
            { text: "when", elements: [
                { text: "model.temperature >= 37" }
            ] },
            { text: "then", elements: [
                { text: "facts.fever = true" }
            ] }
        ]}
    ]);
};

Y luego de estos baby steps, implementados de la forma más simple, llegué a compilar reglas, ejemplo de tests:

var simplerules = require('..');

exports['compile and run simple rule'] = function (test) {
    var text = [
        "rule",
        "  when",
        "    model.temperature >= 37",
        "  then",
        "    model.fever = true"
    ].join('\n');
    
    var engine = simplerules.compile(text);
    
    test.ok(engine.rules);
    test.equal(engine.rules.length, 1);
    
    var model = { temperature: 38 };
    
    engine.run(model);
    
    test.strictEqual(model.fever, true);
}

Y así, en apenas una hora y monedas, tengo una primera implementación de un DSL de definición de reglas. Gracias a seguir el principio de simplicidad, baby steps y el flujo de trabajo de TDD. En próximo post, espero mostrar algún refactor de implementación y nuevos casos de uso, y explicar el algoritmo de disparo de reglas.

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Posted in JavaScript, NodeJs, Test-Driven Development, SimpleRules | Comments Off on Motor de Reglas SimpleRules (2) Implementando un DSL

Motor de Reglas SimpleRules (1) Simplificando con TDD

Siguiente Post

Hace ya uno años, escribí un motor de reglas en JavaScript/NodeJS, usando como es habitual el flujo de trabajo de TDD (Test-Driven Development). A las reglas se les entrega un modelo (un simple objeto JavaScript) y se van disparando según se cumplan condiciones que se defina en cada regla. Y si una regla se dispara, se ejecutan las acciones correspondientes.

En este diciembre, volví a vistar el proyecto, porque tengo unos casos de uso nuevos donde aplicarlo. Y me doy cuenta que no necesito todo lo que había antes definido. Por ejemplo, antes definía una condición y la controlaba con:

exports['define and apply condition'] = function (test) {
    var r = rule({ name: 'rule1' });
    r.condition({ name: 'temperature', value: 37 });
    var model = { temperature: 37 };
    
    test.strictEqual(r.checkConditions(model), true);
}

O sea, la condición estaba definida en un objeto JavaScript, especificando que campo del modelo había que controlar y por cual valor.

También tenía la posibilidad de entregar un operador a aplicar:

exports['define and apply condition with operator'] = function (test) {    
    var r = rule({ name: 'rule1' });
    r.condition({ name: 'temperature', value: 37, operator: '>=' });
    var model = { temperature: 40 };
    
    test.strictEqual(r.checkConditions(model), true);    
}   

Internamente, todo esto lo compilaba a JavaScript. Lo mismo para las acciones: las definía con objetos JavaScript, y por cada tipo nuevo de acción (por ejemplo, cambiar un valor en el modelo), tenía que inventar una forma de represantarla, como representé arriba las condiciones.

Pero ahora, con nuevos casos de uso que estoy pensando, veo que no necesito la granularidad de arriba. Directamente me basta poner un string con la condición o la acción a ejecutar. Por ejemplo, ahora el primer test de arriba es:

exports['define and apply condition'] = function (test) {
    var r = rule({ name: 'rule1' });
    r.when("model.temperature == 37");
    var model = { temperature: 37 };
    
    test.strictEqual(r.checkConditions(model), true);
}

y el segundo test es ahora:

exports['define and apply condition with operator'] = function (test) {    
    var r = rule({ name: 'rule1' });
    r.when("model.temperature >= 37");
    var model = { temperature: 40 };
    
    test.strictEqual(r.checkConditions(model), true);    
}   

El gran cambio es que ahora a la regla se le entrega una expresión en texto para representar una condición. Esto permite enviar cualquier expresión que necesite para esta regla, sin tener que definir y procesar objetos “ad-hoc”.

Pierdo el control de los textos: antes, cuando entregaba un objeto, el código a ejecutar lo armaba yo. Pero ahora veo que lo de arriba es más simple para lo que tengo que hacer. Y lo pude implementar en minutos, porque tenía todos los tests armados, y fue cuestión de refactorizar la implementación y los tests. Si alguna vez me encuentro con un caso de uso que amerite la creación de condiciones de forma más controlada, puedo implementar de nuevo la descripción por objetos, o quizás apele a una API fluent de definición de condiciones. Por ahora, no necesité ese poder. Me guío por los casos de uso que tengo entre manos, y avanzo con firmeza. TDD me da la fortaleza de aceptar el cambio, ahora o en el futuro.

Esto es algo que da el poder de TDD: el coraje de cambiar una implementación, hacer hasta refactor quirúrgico, sin morir en el intento. Es algo que aprecio mucho, y en proyectos profesionales me ha servido para ir adaptándome ágilmente a nuevos casos de uso, sin grandes complicaciones.

En un próximo post, explicaré cómo, siguiendo el principio de simplicidad, pude refactorizar otros aspectos de la implementación. Y hasta pude implementar un DSL (Domain Specific Language) textual de definición de reglas, en apenas una hora. De nuevo siguiendo el flujo de trabajo de TDD. Pueden ver el historial de commits del proyecto.

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Posted in Proyectos Open Source, JavaScript, NodeJs, Test-Driven Development, SimpleRules | Comments Off on Motor de Reglas SimpleRules (1) Simplificando con TDD