Angel "Java" Lopez

NET, Java, PHP y Desarrollo de Software

This Blog

Syndication

Search

Tags

Community

Email Notifications

Archives

.NET

ASP.NET

Windows Form

VB.NET

C#

Sitios

Blogs

October 2010 - Posts

Agentes en AjTalk: Introducción

Extendí mi proyecto AjTalk (una virtual machine interpretada tipo Smalltalk, escrite en C#), para soportar algunas ideas que ya había implementado en AjSharp:

Agents in AjSharp (Part 1)
Agentes en AjSharp (Parte 1)
Agents in AjSharp (Part 2)
Agentes en AjSharp (Parte 2)
Web Crawler using Agents in AjSharp
Web Crawler usando Agentes en AjSharp

y en AjAgents:

AjAgents: a new implementation
AjAgents: una nueva implementación

Usualmente, enviamos un mensaje (su nombre como símbolo, y argumentos) desde un objeto “sender” (enviador) a un “receiver” (receptor):

El receptor toma el mensaje y lo procesa. Mientras tanto, el enviador está esperando el resultado, o al menos, el final del procesamiento. Pero quiero explorar otra manera de hacer las cosas: enviar el mensaje, y olvidarse. El enviador continuaría con su propio proceso. De esta manera se parece a los sistemas vivos: una neurona envía un “mensaje” a otra neurona, y entonces esta neurona puede enviar o no más “mensajes” a otras neuronas. El procesamiento en paralelo emerge de esta actividad desacoplada: es una forma de “message passing”, un tipo de “tell, don’t ask”, o de “Ey! Este es mi mensaje, haz algo bueno con él, cuando puedas y tengas tiempo, pero no me importa recibir una respuesta”.

Para tener todo esto, escribí dos clases en AjTalk, y algo de sintaxis. Podemos definir una subclase que produce agentes usando:

Object agent: #MyAgent.

Podemos definir métodos en la nueva clase MyAgent. Cuando creamos una instancia y la usamos:

myAgent := MyAgent new.
myAgent doSomethingWith: par1 andWith: par2.

el mensaje es enviado al agente, PERO no es procesado en el thread del enviador. Sólo es puesto en una cola. El agente procesa el mensaje desde esa cola, usando otro thread (ahora hay una cola y thread por agente, podría reimplementarlo para que hubiera una cola compartida entre varios agentes, y un pool de threads):

En la implementación actual, la cola es una cola bloqueante, tiene un tamaño máximo: cuando no hay más lugar, el arrivo de un nuevo mensaje bloquea al thread enviador. Podría implementar otras estrategias para tratar demasiados mensajes (descartar los nuevos, descartar los viejos, etc…). Por ahora, esta estrategia bloqueante me sirve, para ir coordinando distintos agentes.

Las clase C# que implementan esta conducta son BaseAgent (que es un BaseObject que sobreescribe el método ExecuteMethod):

public override object ExecuteMethod(IMethod method, object[] arguments)
{
    Message message = new Message(method, arguments);
    this.queue.PostMessage(message);
    return null;    // TODO what to return?
}

y un MessageQueue. Pueden descargar el código actual de AjTalk desde el trunk en http://code.google.com/p/ajtalk/.

Mi idea es escribir un caso de uso para este tipo de agentes: podría ser mi “clásico” ejemplo de web crawler, o algo más simple (recuerden que no tengo una librería de clases AjTalk, todavía, pero podría aporvecharme de todo .NET). Tengo objetos distribuidos, también: próximo post será sobre ese tema. Podría llegar a combinar las dos: armar un web crawler distribuido.

Nos leemos!

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

Posted Fri, Oct 29 2010 10:51 by lopez | with no comments

AjTalk: Accediendo a objetos y tipos .NET

Uno de los puntos de diseño principales en AjTalk (mi proyecto de código abierto implementando una Virtual Machine tipo Smalltalk en C#) es tener accceso a la tecnología de implementación, en este caso, .NET (podría reescribir el proyecto en Java). Con esta facilidad podemos acceder a todo el poder de una librería de clases, y a otras librerías, escritas y ya probadas en un amplio ecosistema (.NET en este caso), como librerías de acceso a datos, serialización, GUIs, ORMs, etc. Para cumplir con este punto de diseño agregué algunas características a AjTalk para tener ese tipo de acceso.

Primero, podemos invocar métodos .NET en objetos .NET:

1 toString

retorna “1”. Es equivalente a la expresión C#:

1.ToString()

Notemos que podemos escribir el método con una letra inicial en minúscula (no es obligatorio). El número 1 es un entero, un System.Int32 en AjTalk, no un IObject (ver Trabajando en AjTalk: una Virtual Machine tipo Smalltalk en C# para una descripción de un IObject, que son los objetos de AjTalk). Podemos invocar un método con parámetros:

1 toString substring: 1 with: 2

Equivalente a la expresión C#:

1.ToString().Substring(1,2)

Podemos usar with: para los parámetros adicionales. El nombre de método es obtenido de la primera palabra del nombre del mensaje, en el caso de arriba, substring:with: es el símbolo del mensaje, y el método nativo es .Substring con dos enteros como parámetros.

Podemos crear nuevos objetos .NET, accediendo a los tipos nativos:

myFileInfo := @System.IO.FileInfo new: 'FooBar.txt'

El @ precede al nombre .NET, en este caso, un nombre de tipo. Podemos enviar los mensajes new, new:, new:with:, etc, para invocar un constructor pasando parámetros. El código de arriba es equivalente a:

myFileInfo = new System.IO.FileInfo(“FooBar.txt”);

Podemos invocar métodos estáticos, también:

@System.IO.File exists: ‘FooBar.txt’

Agregué un “truco”, me parece interesante, a la definición de las clases:

Object subclass: #Collection
!

Collection subclass: #List nativeType: @System.Collections.ArrayList
!

myList := List new
!

myList add: 1
!

myList add: 'foo'
!

 

Lo nuevo es el nombre de mensaje subclass:nativeType:. En el código de arriba, podemos usar List, una clase AjTalk, como un “wrapper” a un tipo nativo System.Collections.ArrayList. La expresión List new retorna un ArrayList nativo, no un “wrapper” basado en IObject. Y podemos usar los métodos nativos. En este ejemplo, podemos llamar al método nativo .Add en el objeto ArrayList.

Pero hasta podemos agregar nuevos métodos AjTalk a esas clases sobre tipos nativos:

Object subclass: #Rectangle nativeType: @AjTalk.Tests.NativeObjects.Rectangle
!Rectangle methods!
area
    ^self height * self width
! !

rect := Rectangle new.
rect width: 10.
rect height: 20.
result := rect area
!

AjTalk.Tests.NativeObjects.Rectangle es una clase C# que escribí para prueba, con dos propiedades Height y Width. El código agrega un nuevo método  area. self apunta a un objeto nativo. Entonces, self height retorna la propiedad .Height del objeto .NET.

Podemos llamar a un método nativo explícitamente (por si el nombre del método fue redefinido), podemos usar ! (signo de admiración)

FileInfo
:=
@System.IO.FileInfo !new: 'NonexistentFile.txt'

Notemos que si usamos un formato tipo fileIn de Smalltalk (AjTalk tiene un lector de ese formato, simplificado), tenemos que repetir el ! (uno solo indica el fin de un chunck, a ejecutar en general en el momento).

FileInfo
:=
@System.IO.FileInfo !!new: 'NonexistentFile.txt'
!

Próximos temas a tratar en posts: agentes, objetos distribuidos, y objetos transaccionales en AjTalk.

Nos leemos!

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

Posted Tue, Oct 26 2010 10:43 by lopez | 1 comment(s)

Trabajando en AjTalk: una Virtual Machine tipo Smalltalk, escrita en C#

En estos días, estoy trabajando en mi proyecto AjTalk, una virtual machine interpretada, tipo Smalltalk, escrita en C#. El año pasado, escribí algunos posts sobre el proyecto. Es tiempo de refrescar su estado: puede manejar más de una VM en un proceso .NET, tiene agentes, procesamiento en paralelo, objetos distribuidos, acceso a la librería de clases .NET, y está por tener transacciones de objetos. Espero que sirva este posts para retomar el tema de cómo funciona.

Pueden bajar el código fuente desde (Work in progress):

http://code.google.com/p/ajtalk/

El aspecto actual de la solución (escrita usando Visual Studio 2008):

La librería núcleo es AjTalk, y todo su código fue escrito usando TDD. Los tests están en el proyecto AjTalk.Tests.

AjTalk está preparado para manejar sus propios tipos de objectos, que implementan la interfaz IObject:

y también puede manejar tipos y objetos nativos .NET. Strings, enteros y números en general son creados y manejados como objetos nativos .NET. Escribiré un post dedicado al tema del acceso a estos objetos nativos desde el lenguaje AjTalk (similar a Smalltalk).

La principal implementación de IObject es BaseObject. Internamente, mantiene una referencia a un objeto IBehavior y a un arreglo interno de variables de instancia:

    [Serializable]
    public class BaseObject : IObject, ISerializable
    {
        private IBehavior behavior;
        private object[] variables;
        public BaseObject()
        {
            this.behavior = null;
            this.variables = null;
        }
        public BaseObject(IBehavior behavior, int nvars)
        {
            this.behavior = behavior;
            this.variables = new object[nvars];
        }
//...
    }

Noten que las variables de instancias no son IObjects: son objetos generales, pueden tanto referenciar a objetos nativos .NET o a objetos IObject. Yo tenía planes de acceder a los objetos nativas via un decorador que implement IObject, en vez de referenciarlos directamente, pero decidí no hacerlo. La referencia es directa.

IBehavior está definida como:

Sus responsabilitidades son definir y recuperar métodos por nombre, crear instancias, y mantener la jerarquía de superclases. Los métodos son objetos que implementan IMethod, que deriva de IBlock:

Actualmente, todas estas interfaces están implementadas usando clases nativas. De esta manera, puedo usarlas sin tener una librería de clases escrita en el propio AjTalk. Es una suerte de “scaffolding”: una forma de tener una base inicial sobre la cual apoyar el resto de lo que estoy construyendo.

Hay 201 tests, todos en verde:

Buen code coverage, pero podría mejorar el número:

Próximos tópicos a tratar por aquí: la implementación de Machine, Blocks, acceso a objetos y tipos .NET, agentes, objetos distribuidos, y objetos transaccionales (Work in progress).

Nos leemos!

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

Posted Mon, Oct 25 2010 9:32 by lopez | 1 comment(s)

AjGenesis y Generación de Código: viendo la luz

Ya tenía pendiente este post, desde hace unos meses. El bueno de @ferclaverino comentó su experiencia sobre generación de código en general, y con AjGenesis en particular, en su post:

Generación de Código, seguimos sin ver la luz?

(visiten el blog de @ferclaverino, recomiendo su material sobre Scrum y desarrollo ágil). Le agradezco desde acá, de forma especial, porque no es común encontrar un post sobre el uso de AjGenesis. Leo ahí:

En el pasado agile open de programación, junto con @claudiomeschini, @mariodallago y otra persona más que no recuerdo quien era, participamos de una sesión que los muchachos llamaron "Generación de código, seguimos sin ver la luz?" … El material del encuentro lo pueden encontrar acá: http://www.agiles.org/agile-open-tour/ba-2010-coding/resultados

Bien ahí! No sabía que el tema interesó en una conferencia ágil. Eso es bueno: discutir los pros y contras de este camino.

Pero notable encontrar también experiencia de alguien usando AjGenesis:

Yo comenté sobre una experiencia de hace unos años usando AjGénesis en un proyecto real y la de hace unas semanas, donde a partir de un modelo muy simple, generamos una solución web mvc con las características que venimos usando últimamente (service layer, domain model, repository, log4net, structure map, manejo de errores c/páginas amigables, etc). También generamos los scripts de nAnt p/integración continua con cruise control y deploy automatico al ambiente de QA.

Qué bueno! Sigo leyendo (yo comentaría todo el post, vayan y lean el post completo, destaco acá algunos fragmentos):

Una distinción importante para hacer en el modelo es separar el modelo de la tecnología o aspectos de implementación (como podrían ser paths, lenguage de programación, etc).

Exactamente. Algo que promuevo desde el comienzo del diseño de AjGenesis.

Algo interesante de AjGénesis es que el modelo se puede cargar de cualquier lado. Por ejemplo se podría cargar una parte del modelo a partir de las clases de una dll. Y luego completar ese modelo con más información, cómo por ejemplo que campos son requeridos p/armar un ABM.

Bien de nuevo! Tomar el modelo de donde sea, y completarlo. Más puntos incorporados en AjGenesis desde su “inception”.

Otra cosa que puede servir a la hora de diseñar un modelo es la de favorecer convención sobre configuración, para que sea menor la cantidad de cosas a definir en el mismo. Por ejemplo, podría asumir que todos los campos de mis entidades son requeridos y especificar puntualmente cuales no.

Siempre uno puede decidir. Y más en AjGenesis, si vieron los templates y tasks de ejemplo. ¿Qué pasa con el mantenimiento de los templates y de lo generado? Hay que separar bien lo manual, de lo generado automáticamente, de lo generado por única vez y luego seguimos manual. Fernando escribe:

Una manera de lidiar con esto vimos que es importante crear buenas abstracciones que separen la parte del problema que puede ser resuelta con generación de código, de la que no. De esa forma, hacer un cambio se traduce a modificar el modelo y volver a generar el código.

Eso es un gran subproducto de la actividad de diseño con generación de código desde un modelo: separar los problemas de la solución, lo importante de las “technicalities”. Hasta donde ví, muchas de los detalles pueden generarse automáticamente. Por ejemplo, en un proyecto real, cambiamos la solución de persistencia, y prácticamente todo el resto sobrevivió. Regeneramos los DAOs para que usaran el nuevo “approach”. También cambiamos las validaciones de las entidades.

Y más:

Conversamos también sobre algunos criterios para evaluar una herramienta de generación de código:

  • El código generado debería ser el mismo que escribiría a mano sin usar la herramienta.
  • Una prueba ácida sería que a partir del mismo modelo, debería ser posible generar código para otra tecnología.
  • El modelo a definir debe ser libre.
  • Posibilidad de cargar este modelo de cualquier lado: xml, excel, txt, dll, tablas, etc.

Notable! Hay alguien más que mi tía Carlota leyendo lo que escribo  …. ;-) ;-)

Es interesante encontrar este tipo de post y discusión. Siempre recomiendo que tomen alguna vez el camino de generación de código, con o sin AjGenesis (aunque si van por otra herramienta, dudo que consigan los puntos mencionados arriba). Vean si les sirve o no.

Nos leemos!

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

Posted Thu, Oct 21 2010 10:28 by lopez | 2 comment(s)

Analizando Nupack (Parte 1) Introducción

Hace algo más de una semana, Microsoft publicó un manejador de paquetes open source, para .NET con integración con Visual Studio:

http://nupack.codeplex.com/

NuPack tiene un programa de consola, una extensión para Visual Studio, y unservidor para hostear los paquetes que queremos publicar (Microsoft tiene un servidor en línea, pero podemos tener nuestro propio servidor donde publicar los paquetes que nos interesan). El código de Nupack fue escrito en colaboración con desarrolladores open source, entre ellos, los que desarrollaron ién el  Nu project, otro manejador de paquetes open source. Y el proyecto Nupack ahora acepta colaboración de la comunidad de desarrollo open source. Por ejemplo, horas después del lanzamiento (el proyecto estuvo en desarrollo por meses), programadores externos a Microsoft actualizaron el código para que pueda ser compilado en Mono, la implementación de .NET multiplatforma.

NuPack no se encarga de compilar el códifo fuente de los paquetes. Lo que hace es exponer los binarios o el código fuente de un paquete para ser bajados e integrados en la solución actual que estemos manejando en Visual Studio. Maneja las dependencias entre paquetas, pero queda a cargo de los autores de los paquetes el mantener la salud de las dependencias. En este sentido, el proyecto es menos ambicioso que otros, como el proyecto Horn, que tomaba el códifo fuente de los repositorios open source originales y los compilaba.

Me gustaría aprender cómo NuPack implementa lo que hace, cuál es su estructura interna, cómo maneja los feeds y downloads, su integración con Visual Studio y PowerShell, el modo que el equipo de desarrollo utilizó tests, etc…. Este es un post introductorio breve, con enlaces sobre NuPack, y una introducción al análisis del proyecto.

Hay posts interesantes sobre la historia de NuPack y su relación con el proyecto Nu y similares:

Unicorns, Triple Rainbows, Package Management and Lasers escrito por @bsimser uno de los autores del proyecto Nu
The Evolution of Package Management for .NET

Hay otro proyecto con los paquetes publicados:

http://nupackpackages.codeplex.com/

Video en Channel 9

Web Camps TV #8 - NuPack with Phil Haack | Web Camps TV | Channel 9

Varios desarrolladores han comenzado a empaquetar su software para ser expuestos en NuPack, por ejemplo:

Using Solution Factory + NuPack to create Opinionated Visual Studio Solutions
forks/jglozano/turbineserver: log

La comunidad .NET ha recibido el proyecto con opiniones dividas. Por un lado, hay un gran entusiasmo e interés en Twitter y foros, pero también escepticismo. Este es un thread que encontré en la lista del proyecto Horn:

Microsoft NuPack

Mis enlaces sobre NuPack:

http://delicious.com/ajlopez/nupack

Nos leemos!

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

Posted Tue, Oct 19 2010 10:00 by lopez | with no comments

Filed under: ,

Presentando Patrones de Diseño (Design Patterns)

Como ya había anunciado, el sábado 16 de Octubre participé de una VAN de la comunidad ALT.NET Hispano, sobre Patrones de Diseño. En cualquier momento, la gente de ALT.NET Hispano publica el video de la VAN (Des-conferencia virtual). Mientras, les dejo acá la presentación DesignPatterns.pptx.

La idea fue presentar algunos patrones a través de un ejemplo. Usé para eso el patrón Interpreter: estuvimos explorando el código de un intérprete escrito en C#. Lo pueden bajar desde mi proyecto Google AjCodeKatas (en el directorio Patterns), donde posiblemente vaya evolucionando, o pueden tomar la versión actual desde Patterns20101017.zip (noten que los namespaces que usé fueron elegidos para describir patrones, más que para un intérprete real). El ejemplo fue desarrollado con TDD (Test-Driven Design), pero no vimos el proceso en la charla: vimos el resultado, directamente. Para los que quieran ver un intérprete desarrollado usando TDD pueden seguir mi serie de posts sobre el tema en el tag TDD ejemplo: Escribiendo un intérprete en .NET (Parte 7).

También mencionamos código de NHibernate, pero no llegamos a verlo. Comenté sobre Facade y Strategy usando una aplicación de demo generada con AjGenesis y que usa NHibernate. Pueden bajar la versión actual (work in progress) de AjTestNh20101017.zip.

Acá va una lista de los patrones que vimos, y el código elegido para ejemplicar cada uno:

Interpreter: con el intérprete de expresiones que escribí como base de la charla.

Composite: con comandos en el intérprete. Uno de los comandos contiene una lista de comandos.

Template Method: lo encontrarán en el método Evaluate de las BinaryExpressions: se evalúan dos expresiones, y el resultado final se calcula invocando al método .Apply, que es reimplementado en las subclases.

Strategy: En vez de escribir un objeto de una clase por Strategy, escribí Func<obj,obj,obj> a aplicar dentro de la evaluación de ArithmeticBinaryExpression.

Decorator: a cada comando y expresión del intérprete, se le pasa un IContext, que tiene la responsabilidad de mantener los valores de las variables que manejamos. Lo decoré para tener un IContext que avise con eventos qué variables y valores se consultan y modifican.

Observer: usé directamente eventos de .NET, que son una evolución de este tema.

Adapter: cuando quise consumir un archivo como TextReader, con código del lenguaje a interpretar, necesitaba leer, no líneas, sino palabras del lenguajes (clase Token). El Lexer del ejemplo es un adapter para eso.

Abstract Factory: acá me aparté del intérprete y usé una implementación que termina usando los providers de ADO.NET, que a su vez funcionan como Abstract Factory.

Facade: junto con Strategy, son los más importantes a aprender. Todo Strategy nos deriva a composición en lugar de herencia, y a Dependency Injection, Inversion of Control y contenedores. Como Facade mencioné la “API de Negocios” que ya había comentado en otra VAN, y mostré una capa fina de servicio lógico en la aplicación de NHibernate que mencioné más arriba.

No llegué a presentar Facade en el ejemplo intérprete, pero pueden ver la clase Machine, que termina usando por debajo a Parser (otra especie de Adapter), Lexer, archivos, y Context, sin que tengamos que manejar esos objetos desde “afuera”.

Y también encontrarán un ejemplo de Visitor, que toma un programa del intérprete y lo “decompila” a texto.

Tiene más ejemplos y los diagramas UML (que usé en la charla) en:

http://www.dofactory.com/Patterns/Patterns.aspx

Terminó la presentación y vino una discusión, muy interesante. Alguien de Guatemala (disculpen, no recuerdo el nombre) necesitaba trabajar en un caso: para un sistema, hay que calcular el precio de los productos, pero esas reglas cambian mucho en el tiempo y por producto. Surgieron ideas: poner una Factory que genere la Strategy a aplicar en cada caso, y hasta tener una Strategy que interprete código que se levante de un texto. Me hizo recordar a sistemas de sueldos y jornales que ví acá en Argentina. Ahí mencioné que pueden usar el ejemplo de:

Un ejemplo de Dynamic Expressions

donde se compila una expresión lambda escrita en texto. Ahí se mencionan otras alternativas.

Otro camino es usar AjSharp como intérprete, ver los posts de AjSharp Debería escribir un post sobre cómo resolver un tema parecido al planteado en la VAN. También pueden ver a AjSharp como un intérprete mucho más terminado que el ejemplo de la charla. Es parte de AjCodeKatas y pueden verlo aquí.

Otro camino (debo post sobre mi charla en el pasado CodeCamp de Buenos Aires, sobre Intérpretes y Compiladores en .NET), es usar Dynamic Language Runtime:

http://dlr.codeplex.com/

Y hay también facilidades en .NET para compilar “on the fly”, usando CodeDOM, Reflection.Emit, y el compilador de .NET. Pero eso pienso que sería un camino más duro. Escribiré sobre eso en el post que debo sobre mi charla en el CodeCamp.

Gracias a la comunidad ALT.NET Hispano y en especial, al flamante MVP @jorgegamba y también al bueno de @fabiomaulo que me acercó información de nuevos patrones, y a todos los que colaboran con la grabación, edición del video y la organización de todas estas actividades.

Ah! Y en cerca del principio del video, hay una sorpresa … ;-)

Nos leemos!

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

Posted Mon, Oct 18 2010 10:31 by lopez | 1 comment(s)

Cloud Computing explicado en español

Este es un post de presentación de un video, que me va a servir de introducción a una serie más técnica sobre Cloud Computing. Hace unas semanas encontré este video en español, gracias a @bolleroo que lo compartió en su twitter. Ya conocía el video original, en inglés, producido por SalesForce.

Creo que todos los que estamos en desarrollo hemos estado escuchando de cloud computing desde hace unos años, acompañados de términos relacionados como Software as a Service, Platform as a Service, y otros. Hay ofertas de Microsoft, Amazon, Google. Hay distintas tecnologías y servicios ofrecidos. Y es, también, un desafío a la programación (¿cómo programar para la nube?). Y una oportunidad de negocios: ofrecer nuestro software en la nube, con capacidades multi-tenant, white-labeling, etc.

Les recuerdo, que acá en Argentina, está abierta la participación al concurso:

Open Challenge de Telefónica Negocios

De todo lo que nombré arriba ¿qué temas prefieren que exploraremos juntos en futuros posts?

Nos leemos!

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

Posted Sat, Oct 16 2010 16:15 by lopez | with no comments

Ejecutando AjGenesis usando Mono y Ubuntu

En estos días, estoy trabajando en el desarrollo de una nueva versión (reescritura completa) de una aplicación Java, con una interfaz web. Es un trabajo de equipo, donde la mayoría de los integrantes son fans de Ubunto: son más productivos trabajando en Linux. El proyecto tiene generación de código, usando (… jeje.. adivinien… ;-) AjGenesis, mi proyecto de código abierto, que genera el código y texto que nosotros necesitamos. Ahora, el equipo quiere ejecutar AjGenesis pero no en Windows.

AjGenesis está escrito en VB.NET, y compilado para .NET 2.0. Yo temía que tuviera que hacer cambios para ejecutarlo bajo Ubuntu usando Mono project. Pero estaba equivocado. Ubunto tiene Mono pre-instalado. Pero AjGenesis usa el runtime de VB.NET. Bien, Mono tiene VB.NET compiler. Lancé la terminal en mi Ubunto (albergado en Virtual Box con host Windows 2008), y ejecuté:

sudo apt-get install mono-vbnc

(Imagino que este paso es necesario, pero debería tratar de ejecutar AjGenesis con sólo Mono instalado. El bueno de @MartinSalias me avisa que ejecuta en MacOS sin bajar nada).

Después, bajé los compilados binarios de AjGenesis (compilados desde el trunk). Y bajé algunos ejemplos, comoe AppExampleStep06.zip (descripto en Building an Application using AjGenesis (Part 6)) (Armando una Aplicación usando AjGenesis (Part 6)).

Puedo ejecutar AjGenesis.Console.exe usando

mono AjGenesis.Console.exe

Luego, marqué el .exe como ejecutable:

chmod +xr- AjGenesis.Console.exe

y ahora, puedo ejecutarlo directamente:

El directorio Build fue creado! Notable!

Luego de este experimento, tomé el código del proyecto usando subversion, y ejecuté el script de generación de código. El único problema fue una de las tareas, escritas en AjBasic, que estaba creando directorios en minúsculas y luego usándolos con letras en mayúsculas. Arreglé ese error, y ahroa, la generación de código está corriendo. Los miembros del equipo podrán desarrollar tanto en Windows como en Ubunto, usando Java, Tomcat, Maven, y AjGenesis.

Nos leemos!

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

Posted Fri, Oct 15 2010 11:01 by lopez | with no comments

AjAgents: una nueva implementación

Hace ya un tiempo, escribí sobre mi proyecto AjAgents, describiéndolo, implementando algoritmos genéticos y otras demos mínimas. El proyecto se basa en el envío de mensajes a “agentes” (podría llamarlos actores, o algo así). Los agentes reciben los mensajes y los procesan uno por uno. Cada instancia de un agente no necesita manejar la concurrencia: los mensajes son encolados en una cola interna por cada agente (podría más adelante, reimplementar esto usando una cola compartida entre varios agentes, o consumida por un pool de threads fijo que vaya consumiendo trabajo pendiente de esa cola, derivándo cada uno al agente destino).

Pero mis anteriores implementaciones se basaban en el uso de Reflection, o, alguna vez, en la librería CCR de concurrency de Microsoft Robotics (ver mis anteriores posts para más detalles). Ahora, tomo otro camino: un agente es un wrapper alrededor de un objeto normal. El código fuente puede ser bajado desde mi  proyecto AjCodeKatas Google Code, en el directorio trunk/AjAgents.

La interface principal, clave, es:

    public interface IAgent<T>
    {
        void Post(Action<T> action);
    }

La nueva idea: usar una clase genérica. T es el tipo de nuestro tipo “clásico” (que no es un agente), y Post es el método para invocar una acción sobre el objet interno: una acción que es una “rutina” que recibe un parámetro instancia de tipo T. De esta manera, le indicamos al objeto interno. Si éste tiene un método:

inner.DoSomething(parameter);

puede ser invocado como un mensaje desde el agente como:

agent.Post(x => x.DoSomething(1));

Esta es la implementación base de agente:

    public class Agent<T> : IAgent<T>
    {
        private T instance;
        private AgentQueue<T> queue;
        private bool running;
        public Agent(T instance)
        {
            this.instance = instance;
        }
        public void Post(Action<T> action)
        {
            if (!this.running)
                this.Start();
            this.queue.Enqueue(action);
        }
        private void Start()
        {
            lock (this)
            {
                if (this.running)
                    return;
                this.queue = new AgentQueue<T>();
                Thread thread = new Thread(new ThreadStart(this.Execute));
                thread.IsBackground = true;
                thread.Start();
                this.running = true;
            }
        }
//...
    }

Noten que el método Post encola la acción en la cola interna del agente. El proceso de esta cola está en el método .Execute (que no se muestra arriba). La cola es una cola bloqueante (sé que hay algo así en .NET 4.0, pero escribí mi implementación para poder ejecutar todo esto en 3.x, o aún 2.x):

    public class AgentQueue<T>
    {
        private Queue<Action<T>> queue = new Queue<Action<T>>();
        private int maxsize;
        public AgentQueue()
            : this(100)
        {
        }
        public AgentQueue(int maxsize)
        {
            if (maxsize <= 0)
                throw new InvalidOperationException("AgentQueue needs a positive maxsize");
            this.maxsize = maxsize;
        }
        public void Enqueue(Action<T> action)
        {
            lock (this)
            {
                while (this.queue.Count >= this.maxsize)
                    Monitor.Wait(this);
                this.queue.Enqueue(action);
                Monitor.PulseAll(this);
            }
        }
        public Action<T> Dequeue()
        {
            lock (this)
            {
                while (this.queue.Count == 0)
                    Monitor.Wait(this);
                Action<T> action = this.queue.Dequeue();
                Monitor.PulseAll(this);
                return action;
            }
        }
    }

Apliqué TDD para el desarrollo de todas estas clases, un ejemplo de test:

        [TestMethod]
        public void InvokeIncrement()
        {
            ManualResetEvent handle = new ManualResetEvent(false);
            Counter counter = new Counter();
            Agent<Counter> agent = new Agent<Counter>(counter);
            agent.Post(c => { c.Increment(); handle.Set(); });
            handle.WaitOne();
            Assert.AreEqual(1, counter.Count);
        }

Mucho del código fue derivado (bueno… casi copy and paste ;-) de mi trabajo previo con AjSharp (channels, agentes, queues, goroutines… ).

Tengo preparada una demo “prueba de concepto” usando esta nueva implementación de AjAgents. Es mi “clásico” ejemplo de Web Crawler. Pero eso es tema para otro post.

Nos leemos!

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

Posted Thu, Oct 14 2010 10:34 by lopez | 2 comment(s)

ALT.NET Hispano VAN sobre Patrones de Diseño

Gracias a la gente de la comunidad ALT.NET Hispano, el próximo sábado 16 de Octubre (ese sábado que viene) voy a presentar el tema de patrones de diseño en una VAN (des-conferencia virtual). La idea es comentar lo que es un patrón, un patrón de diseño, su descripción, algunos temas adicionales como diseño y reúso, “Programar contra la interfaz” y composición vs herencia, y después, rápidamente, pasar a ver código. Tenemos dos horas, más o menos, disponibles, así que veremos algunos patrones (lista definitiva a confirmar), como:

- Facade
- Interpreter
- Adapter
- Decorator
- Visitor
- Strategy
- Abstract Factory
- Builder
- State
- Composite
- …

usando diferentes aproximaciones: código simple, y/o código de alguna parte del framework .NET, y/o código de proyecto de código abierto, o algún Code Kata que haya escrito. Como otras veces, el horario del sábado es 18hs GMT, 15 hs acá en Buenos Aires. Para los que puedan asistir, seguramente quedará publicada en video, como otras VANs que pueden consultar en el historial de las reuniones, en el sitio de ALT.NET Hispano.

Mis enlaces sobre el tema patrones, en general:

http://delicious.com/ajlopez/patterns

Recuerdo acá, una vez más, lo que el flamante Microsoft MVP @jorgegamba escribió sobre estas reuniones:

Hay que aclarar que no se requiere ningún tipo de registro, simplemente acudir el día y la hora indicados a la dirección Web http://snipr.com/virtualaltnet, eso sí, deberán tener instalado el programa cliente de Live Meeting; hay más instrucciones sobre cómo hacer esto y otras indicaciones en la página wiki Descripción de Reuniones.

(Robé… eh… :-)… la imagen de arriba del post Design Patterns for not-so dummies )

Nos leemos!

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

Posted Wed, Oct 13 2010 15:03 by lopez | 3 comment(s)

Armando una aplicación usando AjGenesis (Parte 6)

En el post anterior de esta serie, agregué mucho código para manejar la generación de proyectos y soluciones .NET. Esos archivos son complicados. En contraste, generar algo similar para Java, no estan complejo, porque solo necesitamos generar las estructuras de directorios, y algun archivo para Ant o para Maven. Pero en .NET, tenemos que armar los archivos de proyectos (que no son simples) así como los de las soluciones.

El código de este post pueden bajarlo de AppExampleStep06.zip. Necesitan los binarios de AjGenesisTrunkBinaries.zip. Como siempre el project AjGenesis, incluyendo los ejemplos de esta serie de posts, puede ser bajado desde http://ajgenesis.codeplex.com.

Una de las partes complicadas, en el anterior post, fue agregar los archivos .cs y .vb al archivo del proyecto. Escribí entonces:

for each Entity in Project.Model.Entities
  TransformerManager.Transform("Templates/CSharp/EntityClass.tpl", "${Project.BuildDirectory}/${Project.Name}.Entities/${Entity.Name}.cs", Environment)
  PrjEntities.Includes.Add(CreateFileCs("${Entity.Name}"))
end for

La list Includes dentro del objeto PrjEntities mantiene la información de los archivos a agregar en la generación del archivo de proyecto. En vez de agregar los archivos de esta forma, ahora, en este post, agrego los archivos que están en los directorios. Es decir, podemos ahora tener archivos manuales, escritos por nosotros, dentro del directorio resultado de la generación, y también serán reconocidos dentro de los archivos de proyecto C# y Visual Basic. De esta manera, podemos ir extendiendo los proyectos generados con nuestros propios archivos, que serán conservados en los proyectos despues de la generación de los archivos automáticos. En nuevo código está dentro de  Utilities/DotNetUtilities.ajg:

Sub AddVbFilesToProject(project)
  di = new System.IO.DirectoryInfo(project.Directory)
  prefix = ""
  AddVbFiles(project, name, di)
End Sub
Sub AddVbFiles(project, prefix, di)
  for each fi in di.GetFiles("*.vb")
    name = fi.Name.Substring(0, fi.Name.Length - 3)
    project.Includes.Add(CreateFileVb(prefix & name))
  end for
  
  sdirs = di.GetDirectories()
  
  for each sdir in sdirs
    AddVbFiles(project, prefix & sdir.Name & "\", sdir)
  end for
End Sub
Sub AddCsFilesToProject(project)
  di = new System.IO.DirectoryInfo(project.Directory)
  prefix = ""
  AddCsFiles(project, name, di)
End Sub
Sub AddCsFiles(project, prefix, di)
  for each fi in di.GetFiles("*.cs")
    name = fi.Name.Substring(0, fi.Name.Length - 3)
    project.Includes.Add(CreateFileCs(prefix & name))
  end for
  
  sdirs = di.GetDirectories()
  
  for each sdir in sdirs
    AddCsFiles(project, prefix & sdir.Name & "\", sdir)
  end for
End Sub

Otra “feature” que agregué en este paso: en el anterior post, cada vez que corríamos la generación de código, creaba y pisaba los archivos generados, aunque el contenido fuera al fin igual. Ahora, uso una rutina en Utilities/TransformUtilities.ajg:

Sub TransformFile(tpl, target, tm, env)
  if System.IO.File.Exists(target) then
    target2 = target & ".tmp"
    tm.Transform(tpl, target2, env)
    content1 = System.IO.File.ReadAllText(target)
    content2 = System.IO.File.ReadAllText(target2)
    if content1 <> content2 then
      System.IO.File.Copy(target2, target, true)
    end if
    System.IO.File.Delete(target2)
  else
    tm.Transform(tpl, target, env)
  end if
End Sub

Que sólo pisa el archivo final si es distinto del anterior. Entonces, en vez de invocar directamente al objeto predefinido TransformerManager:

  TransformerManager.Transform("Templates/CSharp/EntityClass.tpl", "${Project.BuildDirectory}/${Project.Name}.Entities/${Entity.Name}.cs", Environment)

las tareas invocak a la nueva rutina, comparando el archivo anterior con el nuevo, para ver si reemplaza o no el archivo destino:

  TransformFile("Templates/CSharp/EntityClass.tpl", "${Project.BuildDirectory}/${Project.Name}.Entities/${Entity.Name}.cs", TransformerManager, Environment)

Próximos pasos, para los siguientes posts: incluir identidad en las entidades del modelo, generar una base de datos desde el modelo, generar código de DAOs, y hasta volver a generar Java, quizás con Ant.

Nos leemos

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

Posted Thu, Oct 7 2010 9:35 by lopez | 2 comment(s)

Presentación de Open App Challenge de Telefónica Negocios

En estos días se ha lanzado, acá en Argentina, el concurso Open App Challenge:

http://www.openapp.com.ar

Lanzado por Telefónica de Argentina, sector Negocios, los acompañan la gente de Red Valleys, Fundación Telefónica, y NEC. Leo ahí en el sitio:

Telefónica Negocios organiza junto con la Fundación Telefónica, Red Valleys y NEC el Concurso Nacional de Aplicaciones Web que resuelvan procesos de las Pymes. Dirigido a emprendedores tecnológicos, desarrolladores, proveedores de contenidos, diseñadores, estudiantes y graduados de carreras sistemas. El objetivo del concurso es impulsar la promoción y fomento de la innovación. Con esta iniciativa pretendemos apoyar la generación de nuevos emprendimientos y herramientas para las pymes de nuestro país.

El concurso se desarrolla en cuatro etapas:

Etapa 1 (la actual): Los participantes podrán registrarse en el sitio web del concurso y cargar la idea del proyecto.

Etapa 2: Se conformará un comité compuesto por los organizadores que evaluará los proyectos y elegirá hasta 20 finalistas que pasarán a la próxima etapa.

Etapa 3: Los 20 proyectos seleccionados en la etapa anterior recibirán un espacio virtual un Data Center adondo podrán diseñar/implementar la aplicación. Esta etapa puede tiene una duración de tres meses.

Etapa 4: El jurado evaluará y probará los proyectos con el fin de seleccionar los 10 mejores proyectos que se harán acreedores al premio.

Pueden leer las bases del concurso en detalle desde

http://www.openapp.com.ar/site/bases.php

El lunes pasado, 4 de Octubre, se hizo la presentación oficial del programa, en la Universidad Tecnológica Nacional, sucursal Buenos Aires. Ayer, martes 5 de Octubre, asistí a un evento de presentación, gracias a una invitación de @bolleroo y @danibron. Si hasta me incluyeron en el jurado!! :-)

http://www.openapp.com.ar/site/jurado.php

(hasta me sacaron foto y todo, vieron? Ayer también, me tomaron otra foto más, para calma y desesperación de mis biógrafos :-).

Antes de las presentaciones, estuve charlando con Néstor Nocetti de Globant, que me contó de la nueva sucursal que están armando en Resistencia Chaco. Aproveché para traer el tema de su adquisición de hace unos años de Accendra. Ahí me enteré que la gente de Iglesias y cía. tiene algún framework de generación de código para .NET (han visto la luz! :-).

Me encontré por primera vez en persona, con Daniel Dron @danidron que está haciendo con http://www.laplatavalley.org/ una sociedad sin fines de lucro que fomenta los emprendimientos tecnológicos, desde La Plata, pcia. de Buenos Aires. Además participa activamente de la @RedValleys ver http://www.redvalleys.org/. Espero poder organizar con ellos alguna charla en las reuniones de presentación de Open App, por lo menos en Tandil (donde tengo que visitar a @cwalzer y cía), y en La Plata.

Encontré entre los asistentes a la gente de NEC, pero no conversé todavía con ellos. Tengo que estudiar cuál es la oferta que tienen en data centers para soportar aplicaciones.

Luego de la presentación inicial de Daniel Dron, habló Alexis Krossler, el director de Telefónica Negocios. Mencionó varios porcentajes interesantes (no tomé notas, escribo de memoria, espero conseguir los números, y entonces los publico): 80% de los clientes de Telefónica, tienen una PC. El 100% usa alguna explicación de negocio. Hay porcentajes más bajos de e-commerce. Y todavía no hay grandes porcentajes en el consumo de aplicaciones tipo SaaS (Software as a Service). Vean que el concurso apunta a desarrollos en línea, para PyMEs (Pequeñas y Medianas Empresas), por volumen de clientes. Son las empresas que necesitan más aplicaciones, y no tienen la capacidad o la necesidad de tener una infraestructura propia dedicada a sus sistemas informáticos: ideales clientes a SaaS.

Luego tomó la palabra Lorena Marante (espero haber escrito bien), de omlatam. Publicista, especialista en marketing online, comentó sobre redes sociales, como un caso especial de medios sociales. Recordó que redes sociales hubo siempre: la familia, la empresa, pueden tomarse como ejemplos de redes sociales. Mencionó principalmente a Facebook y Twitter, y destacó que ellos abrieron el juego (yo diría de forma distinta) al desarrollo de aplicaciones (en el caso de Facebook, dando toda una plataforma sobre la que programar, en el caso de Twitter, con la exposición de una API, principalmente). Al final, mencionó una frase de Tom O’Reilly, sobre lo que hay que hacer en estos tiempos de oportunidad. Habría varias cosas que comentar. Algo que mencionó, y que recomiendo a cualquiera que desarrolle aplicaciones, ver cómo Twitter se fue adaptando a las costumbres de los usuarios. Escribí hace un tiempo una traducción al español de otro post: Cómo nació Twitter.

(Hace poco tuve, en el CodeCamp de Argentina, el mismo problema que Krossler y Marante: mal elección de los colores de letras y fondo en la presentación).

Néstor Nocetti, de Globant, que apoyó a ésta y otras presentaciones de Red Valleys, mostró un video sobre Globant, la empresa que confundó en el 2003.

Y luego, llegó el plato fuerte: @gabimenta, que desplegó su capacidad de hacer una presentación entretenida. Menta mentó (no pun intended :-) las cualidades del sistema operativo de su Mac, destacando que ahora estamos desarrollando aplicaciones para la gente: ya no es más desarrollar para un gerente de sistemas, para otro programador, para un técnico. Ahora (desde hace unos años) es la época de hacer aplicaciones para la gente: atractivas, sencillas, no necesitan ser “uy que bruto, qué tecnología”, sino que sean fáciles de usar y útiles para la gente. Mostró algunas estrategias, como la de Nike, que pone avisos de zapatillas para que uno las pida online, aún antes de su fabricación. Comentó que usa Twitter, aclarando que no es que gana dinero con Twitter, sino que es un canal más entre otros para promover sus actividades. Interesante punto: destacó que Twitter y otros medios, no son para publicidad de una empresa, son para que una empresa comunique lo que está haciendo. Avalando esa postura, describió alguna de las campañas en las que intervino con sus clientes (durante 15 años, Menta estuvo trabajando para Apple, creo, y en este siglo se lanzó por su cuenta). Para destacar cómo cada vez más las aplicaciones son para la gente, mostró parte de su presentación en Ted Buenos Aires, con fotos de sus hijos, Gregorio, Valentino, y Mateo (jeje… mencionó que tiene un hijo preferido, no lo vamos a “mandar al frente” acá). ¿Por qué? Para mostrar cómo las nuevas generaciones interactuan con las nuevas tecnologías. Gregorio, el menor (6 años) ya opera con pantallas táctiles desde los 4 años, y le huye al teclado. Valentino, el del medio (9 años) ya usa netbook, además de libros, y navega en la web como paseando por su casa. Y el mayor, Mateo, es multitasking: mientras atiende el celular, trabaja con su computadora, estudia, y ve TV. Gaby comentaba: “Estudia historia, y mientras ve el Cartoon Network: la nota de historia, 8/10, ¿qué le voy a decir, que apague la tele?”. Me hizo recordar a lo que mencionaba en otro posts estos días, una frase que encontré mencionada en una reunión de Microsoft MVPs: la luchar por las tres pantallas: computadora, teléfono, TV. Pero creo que todos los que desarrollamos software hemos visto cómo estos últimos años la interfaz de las aplicaciones más conocidas, son, como dijo Menta, dedicadas a la gente. No es un tema menor, para tener presente en muchos proyectos.

(Tips para comentar en mi blog no técnico: Menta no usó bullet points, apenas si una presentación. Se basó en mostrar lo que estaba viendo en su Mac, mostrando videos que le parecieron interesantes, describiendo cómo organiza lo que consume en ventanas, y finalmente, saltó a una presentación basada en fotografías y diseños, pero sin seguir un orden lineal, adaptándose al feedback en tiempo real de la audiencia, y a las limitaciones de tiempo).

También encontré en la audiencia a @patygallardo, la primera vez que la encuentro personalmente.

Interesante propuesta de Telefónica, que según me cuenta @bolleroo ya tiene más de 20 aplicaciones anotadas, en apenas dos semanas del lanzamiento. De mi parte, espero poder difundir este tipo de iniciativas. Un tema sobre el que tengo que volver a escribir es Software as a Service y Cloud Computing (los que leen este blog, yo y mi Tía Carlota, saben que algo estoy trabajando con AjContab y derivados): por este blog comentaré algunos temas técnicos y de diseño de ese tipo de aplicaciones, así como de Cloud Computing, Azure, y demás. Sobre las oportunidades que tanto SaaS como Cloud Computing nos dá a los que queremos armar un producto y solución alrededor de eso, escribiré en mi post personal, no técnico, en la sección de emprender y de Internet (no los voy a “castigar” con la de filosofía :-).

Mientras, mis enlaces sobre SaaS y Cloud Computing:

http://delicious.com/ajlopez/saas

http://delicious.com/ajlopez/cloudcomputing

 

Felicitaciones desde acá a la gente de Telefónica Negocios, por el lanzamiento de esta propuesta. Sé que estuvieron trabajando duro para esta iniciativa.

Nos leemos!

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

Posted Wed, Oct 6 2010 11:45 by lopez | 6 comment(s)

Escribiendo un Intérprete en .NET (Parte 7)

Vuelta al ruedo! Un nuevo post en esta serie. En los anteriores posts, estuve escribiendo un intérprete en .NET, usando TDD (Test-Driven Development). Ya tengo un parser, un lexer, algunas expresiones y solamente un comando. Es hora de agregar un nuevo comando a mi intérprete. El nuevo comando es IfCommand:

Pueden bajarse el código fuente desde Interpreter07.zip.

La clase IfCommand implementa la interfaz ICommand. Fue armada usando TDD: escribiendo los test, haciendo que compilen, primero en rojo, luego en verde, refactor. Mis primeros tests:

        [TestMethod]
        public void CreateIfCommand()
        {
            IExpr ession condition = new ConstantExpr ession(0);
            ICommand thenCommand = new SetCommand("a", new ConstantExpr ession(1));
            ICommand elseCommand = new SetCommand("b", new ConstantExpr ession(2));
            IfCommand command = new IfCommand(condition, thenCommand, elseCommand);
            Assert.AreEqual(condition, command.Condition);
            Assert.AreEqual(thenCommand, command.ThenCommand);
            Assert.AreEqual(elseCommand, command.ElseCommand);
        }
        [TestMethod]
        public void EvaluateIfCommandWithZeroAsCondition()
        {
            IExpr ession condition = new ConstantExpr ession(0);
            ICommand thenCommand = new SetCommand("a", new ConstantExpr ession(1));
            ICommand elseCommand = new SetCommand("b", new ConstantExpr ession(2));
            IfCommand command = new IfCommand(condition, thenCommand, elseCommand);
            BindingEnvironment environment = new BindingEnvironment();
            command.Execute(environment);
            Assert.IsNull(environment.GetValue("a"));
            Assert.AreEqual(2, environment.GetValue("b"));
        }

La implementación de IfCommand:

    public class IfCommand : ICommand
    {
        private IExpr ession condition;
        private ICommand thenCommand;
        private ICommand elseCommand;
        public IfCommand(IExpr ession condition, ICommand thenCommand)
            : this(condition, thenCommand, null)
        {
        }
        public IfCommand(IExpr ession condition, ICommand thenCommand, ICommand elseCommand)
        {
            this.condition = condition;
            this.thenCommand = thenCommand;
            this.elseCommand = elseCommand;
        }
        public IExpr ession Condition { get { return this.condition; } }
        public ICommand ThenCommand { get { return this.thenCommand; } }
        public ICommand ElseCommand { get { return this.elseCommand; } }
        public void Execute(BindingEnvironment environment)
        {
            object result = this.condition.Evaluate(environment);
            bool cond = !IsFalse(result);
            if (cond)
                this.thenCommand.Execute(environment);
            else if (this.elseCommand != null)
                this.elseCommand.Execute(environment);
        }
        private static bool IsFalse(object obj)
        {
            if (obj == null)
                return true;
            if (obj is bool)
                return !(bool)obj;
            if (obj is int)
                return (int)obj == 0;
            if (obj is string)
                return string.IsNullOrEmpty((string)obj);
            if (obj is long)
                return (long)obj == 0;
            if (obj is short)
                return (short)obj == 0;
            if (obj is double)
                return (double)obj == 0;
            if (obj is float)
                return (float)obj == 0;
            return false;
        }
    }

IfCommand evalúa una expresión, que retorne un objeto. Este objeto podría no ser un booleano. Tomé la decisión de evaluar null, 0, string vacío como false (algo parecido a lo que hace PHP). El único lugar donde necesito evaluar un objeto cualquiera como verdadero o false es, ahora, en este método  IfCommand.Execute. Así, esta lógica de evaluación está ahora en un método privado. Planeo refactorearlo, moverlo a otra clase, en cuanto lo necesite desde otros lugares, como cuando implemente el comando WhileCommand y otras expresiones.

Después de escribir IfCommand, necesitaba parsear comandos, no sólo expresiones. No tenía un método .ParseCommand() en la clase Parser. Mis primeros tests (hay más en el código):

        [TestMethod]
        public void ParseAndEvaluateSimpleIfCommand()
        {
            Parser parser = new Parser("if (a) b=1; else b=2;");
            ICommand command = parser.ParseCommand();
            Assert.IsNotNull(command);
            Assert.IsInstanceOfType(command, typeof(IfCommand));
            BindingEnvironment environment = new BindingEnvironment();
            command.Execute(environment);
            Assert.AreEqual(2, environment.GetValue("b"));            
        }

Luego, implementé nuevos métodos en Parser:

Parser.ParseCommand() tienen una implementación ingenua. Solamente dos clases de comandos son soportados: comandos if, y comandos de seteo de variables:

        public ICommand ParseCommand()
        {
            Token token = this.NextToken();
            if (token == null)
                return null;
            if (token.TokenType == TokenType.Name && token.Value.Equals("if"))
                return ParseIfCommand();
            this.PushToken(token);
            return ParseSetCommand();
        }
        private ICommand ParseSetCommand()
        {
            string name = this.ParseName();
            this.ParseToken(TokenType.Operator, "=");
            IExpr ession expr = this.ParseExpr ession();
            this.ParseToken(TokenType.Separator, ";");
            return new SetCommand(name, expr);
        }
        private ICommand ParseIfCommand()
        {
            IExpr ession condition;
            ICommand thencmd;
            ICommand elsecmd;
            this.ParseToken(TokenType.Separator, "(");
            condition = this.ParseExpr ession();
            this.ParseToken(TokenType.Separator, ")");
            thencmd = this.ParseCommand();
            Token token = this.NextToken();
            if (token != null && token.TokenType == TokenType.Name && token.Value.Equals("else")) 
            {
                elsecmd = this.ParseCommand();
                return new IfCommand(condition, thencmd, elsecmd);
            }
            if (token != null)
                this.PushToken(token);
            return new IfCommand(condition, thencmd);
        }

Todos los tests quedaron en verde:

Buen code coverage:

Próximos pasos: agregar más comandos (while, for, etc…), declaraciones de funciones, manejo de números reales, etc.

Nos leemos!

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

Posted Tue, Oct 5 2010 10:22 by lopez | 1 comment(s)