CEIBO

Domingo 15/junio/2008

Juego de Cartas Web 2.0 ¡En linea!

Archivado en: General — Hernan Galante @ 11:54 pm

Estas son algunos de los screenshots que hemos tomado del juego de cartas. Está en línea en este URL. Obviamente, esta en una fase de prueba y puede presentar alguna inestabilidad a corregir. A continuación presentamos algunos ScreenShots del mismo:

Carga del Juego de Cartas
Fig 1. Carga del juego de Cartas

Login del Juego de Cartas
Fig. 2 Login del Juego de Cartas

Registro de usuario
Fig. 3 Registro de nuevo usuario

Configuración del avatar
Fig. 4 Configuración del avatar

Configuración del pelo del avatar
Fig. 5 Configuración del pelo del avatar

Bienvenida al Juego
Fig. 6 Bienvenida al Juego

Jugando entre dos participantes
Fig. 7 Jugando entre dos participantes un juego de Solitario

Participante entrante al juego compartido saludando al oponente 
Fig. 8 Participante entrante al juego compartido saludando al oponente

Hasta aquí hemos presentado unas imágenes del juego que está en versión beta. Aún, por ejemplo, no se ha implementado la persistencia, por lo cual, la registración del juego está en memoria. En caso de reiniciar el servidor, las registraciones se perderían por el momento.

¡Espero que os guste!

Eligiendo la persistencia adecuada

Archivado en: General — Hernan Galante @ 11:53 pm

En casi todos los proyectos de software tradicional una de las primeras estructuras que se eligen es la base de datos. Se elige en que motor va a guardarse los datos, antes de saber de que datos hablamos, cantidad, volumen y si la solución adecuada es apropiada. Muchas herramientas de programación incluso, no permiten o no dan soporte prácticamente para trabajar con otra alternativa.
En los proyectos Smalltalk en general, la persistencia, es una de las últimas cosas que se plantean y estudian.

¿Porqué estudiar la persistencia a último momento?

Esta pregunta es interesante contestarla para quienes estén familiarizados con la acción inversa. Todos sabemos que en un proyecto orientado a objetos, el uso de una base de datos puede ser una tarea compleja. Para hacer una analogía, sería como pensar que cada vez que guardamos nuestro vehículo en nuestra cochera, de ante mano debemos desarmarlo hasta los tornillos para poder guardarlo. Y para volver a usarlo como tal, debemos rearmarlo nuevamente. Por lo que podemos deducir claramente que no es natural esta táctica. Alguien le puso un nombre en inglés a esto como “impedance mismatch”. Podría traducirse como “acople de impedancia”, pero a efectos de nuestro objetivo no tiene más significado de que estamos conectando dos tecnologías incompatibles. Para compatibilizarlas, generalmente se usan frameworks de persistencia, que no son otra cosa que “adaptadores”. Como en la electricidad, que usamos transformadores para pasar de 220 a 110v, aquí es lo mismo. Estos frameworks suelen tener su complejidad por supuesto.

En los proyectos de lenguajes tradicionales orientados a objetos, ya usan un estándar o estándares que la misma industria les recomienda, como Spring o Hibernate. Este ultimo resuelve exactamente esta incompatibilidad, para lo cual, la mayoría piensa que ha sido una buena elección y sin dudarlo solo acata elegir el motor de base de datos. La realidad, es que en la mayoría de los proyectos usan herramientas inapropiadas sin analizar sin más detalles si realmente la requieren o si con una solución más simple pudieran obtener una solución perfectamente adecuada. La pregunta que se harán muchos es ¿Que está mal al elegir un framework gigante como hibernate para guardar los datos? Bien, la respuesta es simple, si el sistema probablemente use miles de usuarios y miles de datos, la alternativa sería correcta, pero sería mejor post-poner el uso de la persistencia hasta que el modelo esté maduro. Recién allí, nos inclinamos a hacer persistir el modelo, ya maduro, en la base; optimizando y haciendo lo ajustes necesarios. Si esto lo hacemos mucho antes de lo necesario, traerá complicaciones, que se traducen en más tiempo y al final es dinero malgastado. Esto, debería ser así para cualquier proyecto, pero no, la industria en general opta por complicarse de antemano, tener que echarle mano a scripts SQL para migrar cuando el modelo “inmaduro” va cambiando drásticamente su forma o directamente para evadir esto empiezan a dejar residuos en la base de datos. Claro, hay que aclararlo, una base de datos es una entidad absolutamente pasiva, y ni siquiera sabe que datos son usados y que datos podrían ir a parar a la basura. Como los datos son pasivos, al contrario que los objetos que son entidades activas. Estos últimos pueden aplicar la capacidad de un “Garbage Collector” o recolector de basura, que puede decidir por si solo si ciertos objetos ya no están siendo usados, por lo tanto, pueden borrarse. Si imaginamos esto en una base de objetos, entonces, tenemos que las bases de objetos tienen información más confiable que una base de datos por simple naturaleza.
Muchas veces he visto, por ejemplo, que se usa un tremendo y complejo framework de persistencia para solo “persistir” unos 10 objetos en el modelo, lo que se traduce a unas pocas tablas. Y ni hablar que quizás ese sistema solo tenga unos pocos usuarios. Es decir, tranquilamente se puede optar por sistemas de persistencia más simples y menos complejos. ¿Y que soluciones nos traería algo así? Bueno, para seguir dando ejemplos para entender, hace uno tiempo vi como un producto en el mercado tenía esta “sobredimensión” y el producto competencia en el que trabajé solo mantenía la información que requería en un archivo de formato propio (que no era otra cosa que un sistema de persistencia basado en diccionarios, muy simple). A la hora de competir, este sistema nuevo no paga ningún licenciamiento y requería muchísimos menos requisitos para funcionar. El otro sistema requería instalar por separado el motor, y luego los clientes. Como se imaginarán, no solo a nivel técnico hablamos de menos complejidades, a nivel negocio, era más rentable aquel que tenía menos. O sea, menos es más muchas veces. Como moraleja, deberíamos asumir que no hay que intentar matar mosquitos a cañonazos, o mejor dicho, no intentemos solucionar todo a cañonazos. Aunque algunos políticos ese problema sistémico no lo han entendido, esperemos que la gente que desarrolla sistemas si lo entienda.

¿Qué soluciones serían alternativas a usar una Base de datos relacional?

Dependiendo de la magnitud del proyecto y las de exigencias de este, podemos ir desde persistir en archivos, en el image de Smalltalk, en esquemas de persistencia, usar frameworks de persistencia a Base de datos, o base de objetos.

¿Que soluciones son opciones para este proyecto?

Dentro del marco de investigación para lo que necesitamos, van a desde sistemas de base de objetos como Magma o Goods, a un framework de persistencia sobre Base de datos.

Las solución que se elija debe contemplar:

  • Múltiples usuarios
  • Las 4 leyes básicas de persistencia: ACID (Atomicity, Consistency, Isolation, Durability) o en castellano, Atomicidad (ante cualquier eventualidad ninguna acción queda a medias), Consistencia (Es decir, que cada operación que se realice sobre la información asegura que siempre quedará integra), Aislamiento (Una operación no afectará a otras operaciones) y Durabilidad (es decir, que la información se guardará y permanecerá en el tiempo)
  • Pool de Sesiones, generalmente en un proyecto web, donde existen múltiples clientes no vamos a abrir una conexión por cada conexión web que se realice. El tema principal es que no siempre estamos persistiendo y podrían quedar sesiones abierta contra la base sin demasiado sentido más que malgastar los recursos computacionales preciados. Es por ello que se piensa en un pool de conexiones habilitadas para realizar las operaciones, y ciertos planes de contingencia.
  • Loqueo optimista, es de preferencia para un sitio web. El loqueo optimista asume que siempre hay conflictos y traza su política es base a esto. En el pesimista, pretende estar solo, y si va a haber conflicto debes loquear el objeto antes de usarlo.

Resumen

Se estudiarán varias alternativas de persistencia para el proyecto, la que mejor se adecue será la opción final a usar. Por lo pronto, por cada opción haremos una entrada en el blog para recordar los puntos positivos, los puntos negativos y los puntos interesantes de la cada opción.

El avance de la configuración de avatares en el juego de barajas 3D

Archivado en: Diseño — Hernan Galante @ 11:28 pm

Estos son algunas de las capturas de pantalla para la configuración de avatares en el juego de barajas 3D.

Estos son algunas de las capturas de pantalla para la configuración de avatares en el jeugo de barajas 3D.
Fig 1. Elección de gafas para el avatar

Registro de un nuevo usuario
Fig 2. Registro de un nuevo usuario

Configuración de las cejas del avatar
Fig 3. Configuración de las cejas del avatar

Configuración de la nariz del avatar
Fig 4. Configuración de la nariz del avatar

Estaremos publicando más capturas de pantalla a medida que vaya avanzando las distintas herramientas del juego.

Domingo 27/abril/2008

¿Como empezar con SWT?

Archivado en: General,Programación — Hernan Galante @ 7:49 pm

Introducción

Una de las principales tareas a realizar cada vez que comenzamos con un nuevo framework o herramienta es saber rápidamente algunos detalles de como manejarnos. Ejemplo de esto son entender la arquitectura básica, saber como realizar las tareas básicas (arrancar, suspender, o detener el proceso) y como obtener las actualizaciones. Mínimamente con estos tres conceptos claros sobre una herramienta podemos usarla y explotarla satisfactoriamente. En este artículo pretendemos dar estos tres conceptos al lector.

Arquitectura del SWT

Smalltalk Web Toolkit es un framework para la construcción de aplicaciones web. Sus principales diferencias con respecto a otros frameworks es su arquitectura basada en Comet y un ambiente Smalltalk cliente hecho en JavaScript a través de un traductor de código Smalltalk-JavaScript. Estas dos diferencias hacen que las aplicaciones web rompan la desincronización del protocolo HTTP (gracias a Comet) pudiendo el server enviar contenido a sus clientes conectados, y gracias al traductor, todo el JavaScript necesario se escribe en Smalltalk sin tener que manipular en un proyecto varios lenguajes y tecnologías, pudiendo el desarrollador enfocarse más en el núcleo de su problema y no tanto en el entorno de como resolverlo.

Tareas Básicas

Algunas de las tareas básicas que requerimos conocer para hacer uso del framework son las siguientes:

1) Como arrancar el ambiente y el servicio
2) Como monitorear el servicio
3) Un “Hola Mundo”

Arrancando Squeak (Linux)

Squeak es un Smalltalk multiplataforma, más allá del ejemplo que citaremos aquí puede correr en muchos otros operativos.

Running Squeak
Fig. 1 - Corriendo el ambiente

Una vez dentro del ambiente, debemos arrancar el servicio. Para ejecutar una expresión Smalltalk la seleccionamos (de manera que todo el texto quede pintado) y luego podemos ejecutarlo con la combinación de teclas ALT+d ó ALT+p.

Arrancando SWT

La manera más fácil de saber si el framework está corriendo es apuntar nuestro navegador a su dirección default: http://127.0.0.1:2222. Si nos muestra la página (Fig. 2) con todas las aplicaciones disponibles, entonces el framework está corriendo.

Presentation web page
Fig. 2 - Página principal de SWT con todas las aplicaciones

En caso de que no esté corriendo, presentamos una serie de expresiones útiles a ser evaluadas en un Workspace de Smalltalk.
Estas expresiones nos permiten parar, arrancar o resetear el servicio.

SWTApplicationRunner start.
SWTApplicationRunner stop.
SWTApplicationRunner reset.
SWTApplicationRunner cleanUp.

¿En que casos debemos hacer uso del reset?
En caso de que se esté produciendo algún error inesperado grave, el #reset para el framework, mata la instancia actual del servicio y lo arranca de nuevo.

¿Cuál es la diferencia con el #cleanUp y el #reset?
El #cleanUp para el servicio, y es un proceso más profundo, hace una limpieza de las instancias de todos los aplicativos y del traductor de Smalltalk-JavaScript. Aunque no vuelve a arrancarlo explícitamente al servicio. Este método es usado cuando queremos hacer alguna actualización o mantenimiento.

Monitoreando el Servicio

El framework loguea actualmente todos sus pasos importantes, la principal salida es el Transcript de Smalltalk. Como vemos en la figura a continuación, en este Transcript nos muestra que se ha “salvado” la imagen del ambiente, y que luego hemos arrancado el servicio con el #start. Allí nos muestra de donde está tomando los recursos (imágenes, sonidos, etc) y en donde está atendiendo el servicio.

SWTApplicationRunner start
Fig. 3 - Arrancando SWT

También podemos ver el log en la consola de Linux, como en la pantalla que continua

Linux console log
Fig. 4 - Log en la consola de Linux

El clásico “Hola Mundo”

No es el objetivo de este artículo como programar sobre SWT, pero explicaremos un simple “Hola Mundo” que nos puede servir como base para comenzar a entender al framework.
La arquitectura del framework para construir aplicaciones se base en una parte cliente (la que correrá sobre el navegador) y la parte servidora (la que correrá sobre el servidor web y será la encargada de manipular a los clientes y proveerlos de la información, eventos y actualizaciones). Las clases en cuestión son: SWTClientApplication y SWTServerApplication. Para crear un nuevo aplicativo solo necesitaremos crear una subclase para el cliente y otra para el server. Tomaremos como ejemplo al aplicativo de “Ping/Pong” el cual es una especie de “Hola Mundo”.

Ping-Pong
Fig. 5 - El aplicativo Ping/Pong

El código del Ping/Pong es bastante simple, la idea detrás del aplicativo es que el cliente muestre una página muy simple, y al cliquear en el botón se envíe un mensaje #ping al servidor, y este le conteste al cliente, con un mensaje #pong.

Existe otro articulo llamado Mini aplicaciones sobre Smalltalk Web Toolkit (SWT) que abarca a este ejemplo de “Hola Mundo” en profundidad.

Actualizando SWT

¿Como mantener actualizado el código en SWT? Para responder esta pregunta, vamos a explicar como un proyecto en Squeak, en general, también se actualiza y mantiene. Existe un servicio, que es un repositorio de código para Squeak llamado SqueakSource.

Squeak Sourcepage
Fig. 6 - SqueakSource

En este sitio hay docenas de proyectos open source para la comunidad. SWT es un proyecto más publicado de esta manera. El sitio oficial dentro de SqueakSource es http://www.squeaksource.com/SWT.html. La mejor forma de mantenerse informado es apuntar nuestro lector de RSS favorito a http://www.squeaksource.com/SWT/feed.rss. Cada nueva versión que se publique será publicada de forma automática. Para actualizar nuestro ambiente Squeak a esta nueva versión usaremos una herramienta llamada Monticello. (Fig. 7)

Actualizando con Monticello 1
Fig. 7 - Creando un nuevo paquete en Monticello

¿Como actualizar usando Monticello?
Monticello se abre desde el menú principal de Squeak (dando click sobre el escritorio de Squeak se despliega un menú). En el menú, elegir las opciones “Open” y luego “Monticello browser”.

Menu para abrir Monticello
Fig. 8 - Secuencia para abrir Monticello

Una vez que tenemos abierto el Monticello, debemos crear un nuevo paquete (Package), nos pedirá el nombre, al cual nombraremos como SWT o como nos sea útil. Luego, que tenemos el nuevo paquete, le vamos a indicar donde está el repositorio, dando click en el botón que dice “+Repository”. Elegimos la opción HTTP, e ingresamos la dirección en el cuadro http://www.squeaksource.com/SWT.

Actualizando Monticello 2
Fig. 9 - Después de crear el paquete, creamos el repositorio, y seleccionamos el HTTP

Una vez creado el repositorio, lo seleccionamos y presionamos el botón “Open”. Esto hará que se conecté al repositorio, y nos abra otra ventana donde nos mostrará las versiones disponibles para cargar desde el repositorio en nuestro ambiente.

Actualizando Monticello 3
Fig. 10 - El repositorio mostrando los distintos frameworks y sus versiones para ese proyecto

Para actualizar nuestra versión simplemente elegimos la versión, y luego apretamos el botón “Load” (cargar).
Cuando esta operación termine nuestro ambiente estará con la versión que hemos seleccionado. Si una versión es dependiente de otra versión, nos preguntará si también lo deseamos cargar.

Resumen

En este artículo hemos visto como arrancar el ambiente, como actualizarlo y como hacer los mantenimientos básicos.

¿De donde bajar Smalltalk Web Toolkit?

Archivado en: General,Programación — Hernan Galante @ 7:49 pm

¿De donde se puede bajar la versión?

Se ha publicado una versión del framework actual en la siguiente dirección:

http://smalltalk.consultar.com/varios/SWT-full.zip

¿Que contiene el archivo Zip?

El Zip contiene los archivos necesarios para correr el ambiente para SWT en las plataformas de Linux y Windows.

Contiene los siguientes directorios:

  • Files: Contiene las bibliotecas javascript para SWT
  • Packages-cache: Es utilizado por la herramienta de paquetes de Squeak
  • Resources: Son todos los recursos (imágenes, sonidos, etc.) que usan las distintas aplicaciones sobre SWT
  • Sm: Es utilizado por la herramienta de paquetes de Squeak

Contiene los siguientes archivos:

  • Archivos independientes de la plataforma:
    • Squeak3.8.1-6747full.38.changes: Es el archivo de cambios del ambiente
    • Squeak3.8.1-6747full.38.image: Es el archivo imagen de Squeak.
    • SqueakV3.sources: Es el archivo que contiene los fuentes de Squeak.
    • Splash.bmp: Es la imagen visual que se ve al arrancar el ambiente.
  • Archivos exclusivos para Linux:
    • Squeak-3.9-8.i686-pc-linux-gnu.tar.gz: Contiene la máquina virtual para instalar en Linux
  • Archivos exclusivos para Windows:
    • SqueakFFIPrims.dll: Librería para primitivas exclusivas para Windows
    • Squeak.ini: Configuración de la máquina virtual sobre Windows
    • Squeak.exe: Ejecutable para Windows

¿Cómo lo corro desde Linux?

En un Linux, recordamos que esta versión ha sido probada en Ubuntu 7.10, instalar la máquina virtual que se encuentra en el archivo Zip, Squeak-3.9-8.i686-pc-linux-gnu.tar.gz. Dentro de este archivo encontrarán las instrucciones para llevar a cabo la instalación de la máquina virtual sobre Linux.

¿Cómo lo corro en Windows?

En Windows correr el archivo Squeak.exe y este nos abrirá el archivo .image que encuentre dentro del mismo directorio.

En Windows encontraremos un error, dado que esta versión está siendo desarrollada para Linux.  Para ello hay que desactivar (simplemente comentando el código) en

Alogger>>logMessages:
....
   "Try to log to stdout, using OSProcess"
   "Smalltalk at: #OSProcess ifPresent: [:osProcessClass |
    osProcessClass thisOSProcess stdOut
     nextPutAll: msg;
     nextPut: Character lf.
   ]."
....

¿Otras plataformas?

Dado que Squeak es un ambiente Smalltalk multiplataforma, podemos encontrar en su sitio oficial muchas máquinas virtuales para correr la imagen (que no depende de la plataforma ni del sistema operativo). Con solo seguir las instrucciones de como instalar para una plataforma en especial para Squeak, también lo estamos haciendo para SWT. Sólo deberemos descomprimir la imagen (archivo extensión .image) y correrlo con el ejecutable o binario de nuestra plataforma y SWT estará corriendo.

¿Sobre que Sistemas operativos ha sido probada esta distribución?

La versión está preparada para Linux. Pero también ha sido probada sobre Windows. Ver en las preguntas anteriores los detalles de como instalar.

Miércoles 19/marzo/2008

Ejemplo: Un examen en línea con Smalltalk Web Toolkit (SWT)

Archivado en: Programación — Hernan Galante @ 4:08 pm

Introducción

En este framework, basado en la arquitectura Comet, tenemos dos clases principales para hacer un aplicativo, SWTClientApplication y SWTServerApplication. Para comenzar vamos a realizar un pequeño ejemplo sobre lo que sería una aplicación de Exámenes en línea. Donde se muestra una pregunta a la vez, y varias posibles soluciones. Al final de contestar las preguntas da el puntaje obtenido. Este tutorial se dividirá en dos partes, en la primer parte haremos este aplicativo con un modelo en el servidor más inactivo y el cliente web con más mucha más actividad y funcionalidad. En la segunda parte, haremos uso del MVC distribuido que ofrece el SWT, donde mostraremos como mejorar considerablemente la cantidad de mensajes y la forma ineficiente con respecto a la primer parte.

Ejemplo: Exámenes en línea

El modelo

Para comenzar vamos a desarrollar un modelo simple del aplicativo. Un examen se compone de una pregunta y múltiples respuestas, donde cada respuesta puede ser la más acertada para responder o simplemente es incorrecta. Por estándar vamos a tener cinco respuestas por cada pregunta, y cada respuesta tiene un valor. Para hacer los cálculos más simples, vamos a poner un rango de puntuación entre 0 y 10 puntos.

La respuesta es un objeto simple que tiene la respuesta que contiene con su puntaje asociado, y una pregunta es un objeto con una pregunta y una colección de respuestas.

Para ello, vamos a crear un workspace con las expresiones que queremos que funcionen:

| question answers |

answers := OrderedCollection new.

answers

add: (SWTAnswerModel answer: ‘Object’ score: 0);

add: (SWTAnswerModel answer: ‘Collection’ score: 0);

add: (SWTAnswerModel answer: ‘Set’ score: 10);

add: (SWTAnswerModel answer: ‘Bag’ score: 0);

add: (SWTAnswerModel answer: ‘SWTModel’ score: 0);

yourself.

question := SWTQuestionModel question: ‘¿De quién hereda Dictionary?’ answers: answers.

Como vemos en workspace anterior ya tenemos un pequeño borrador de como sería nuestro modelo. Ahora, cada vez que un usuario responde vamos a agregarle que la pregunta sepa la respuesta que eligió el usuario, para ello, agregamos una variable de instancia que nos permita recordar que respondió a esa pregunta. La variable la vamos a denominar answerChoosed.

| question answers |

answers := OrderedCollection new.

answers

add: (SWTAnswerModel answer: ‘Object’ score: 0);

add: (SWTAnswerModel answer: ‘Collection’ score: 0);

add: (SWTAnswerModel answer: ‘Set’ score: 10);

add: (SWTAnswerModel answer: ‘Bag’ score: 0);

add: (SWTAnswerModel answer: ‘SWTModel’ score: 0);

yourself.

question := SWTQuestionModel question: ‘¿De quien hereda Dictionary?’ answers: answers.

question answerChoosed: (answers at: 3).

Si el usuario no sabe contestar la pregunta, pondremos una respuesta por defecto, que valdrá cero puntos, y es No sabe y/o No contesta. Esto lo vamos a hacer en el getter de #answerChoosed

SWTQuestionModel>>answerChoosed

” Returns the answer choosed by the user to the receiver.”

answerChoosed isNil ifTrue: [ self answerChoosed: SWTAnswerModel notAnswered ].

^answerChoosed

Ahora bien, en nuestro modelo ya tenemos preguntas, posibles respuestas y la respuesta que se elige. Esto funciona para solo una pregunta, pero un examen, consta de muchas preguntas, y dado, que generalmente una certificación, tiene al menos unas 40 preguntas, vamos a modelar nuestro objeto examen. Este objeto tiene una colección de preguntas, y sabe calcular el puntaje final. Vamos a denominar SWTExamModel.

Para calcular el puntaje final lo hacemos así:

En el examen

SWTExamModel>>score

” Returns the score of the receiver.”

^self questions inject: 0 into: [ :finalScore :question | question score + finalScore ]

En la pregunta

SWTQuestionModel>>score

” Returns the score of the receiver.”

^self answerChoosed score

Hasta aquí, ya tenemos un modelo funcional, tenemos un examen, que se compone de preguntas, a su vez, las preguntas tiene posibles respuestas, y el usuario por cada pregunta elige su respuesta. El objeto respuesta sabe que puntaje tiene. Entonces el examen solo delega en sus preguntas saber los puntajes obtenidos, y estas a su vez, usan de colaborador a nuestras respuestas.

Nuestro examen aún le falta algo más, si ya respondí una pregunta, tengo que esperar la siguiente, así que vamos a decirle al examen, que nos de una pregunta más, para hacerlo más interesante aún, que sea al azar, y lógicamente, como es azar, no quiero responder más de una vez una pregunta, por lo tanto, el examen deberá quitar las que ya se respondieron. Para ello, agregamos este comportamiento a examen. Vamos a tener variables de instancia a questions, que son las preguntas totales, y otra colección con las preguntas ya usadas, questionsAnswered.

SWTExamModel>>questionsNotAnswered

” Returns the collection of questions not answered yet of the receiver.”

| newQuestions |

newQuestions := self questions copy.

newQuestions removeAll: self questionsAnswered.

^newQuestions

Obtener una nueva pregunta:

SWTExamModel>>newQuestion

” Returns a new question for the exam.”

| newQuestion randomIndex maximumInteger |

randomIndex := Random seed: 2345678901.

[ maximumInteger := self questionsNotAnswered size.

maximumInteger = 0 ifTrue: [ ^nil ].

newQuestion := self questionsNotAnswered at: (randomIndex nextInt: maximumInteger).

self questionsAnswered includes: newQuestion] whileTrue: [].

self currentQuestion: newQuestion.

self questionsAnswered add: self currentQuestion.

^self currentQuestion

Para probar lo que hemos hecho hasta el momento, vamos a hacer un workspace donde tengamos un examen con 3 preguntas y las contestaremos. Por ultimo, evaluaremos el puntaje final.

| exam questions answers questionToAnswer |

questions := OrderedCollection new.

answers := OrderedCollection new.

answers

add: (SWTAnswerModel answer: ‘Object’ score: 0);

add: (SWTAnswerModel answer: ‘Collection’ score: 0);

add: (SWTAnswerModel answer: ‘Set’ score: 10);

add: (SWTAnswerModel answer: ‘Bag’ score: 0);

add: (SWTAnswerModel answer: ‘SWTModel’ score: 0);

yourself.

questions add: (SWTQuestionModel question: ‘¿De quien hereda Dictionary?’ answers: answers).

answers := OrderedCollection new.

answers

add: (SWTAnswerModel answer: ‘un objeto’ score: 0);

add: (SWTAnswerModel answer: ‘una clase’ score: 0);

add: (SWTAnswerModel answer: ‘una variable global’ score: 10);

add: (SWTAnswerModel answer: ‘El nombre del producto’ score: 0);

add: (SWTAnswerModel answer: ‘una nueva distro de Linux’ score: 0);

yourself.

questions add: (SWTQuestionModel question: ‘Que es Smalltalk?’ answers: answers).

answers := OrderedCollection new.

answers

add: (SWTAnswerModel answer: ‘la clase del 3′ score: 0);

add: (SWTAnswerModel answer: ‘la clase Class’ score: 0);

add: (SWTAnswerModel answer: ‘Integer class’ score: 10);

add: (SWTAnswerModel answer: ‘un error’ score: 0);

add: (SWTAnswerModel answer: ‘nil’ score: 0);

yourself.

questions add: (SWTQuestionModel question: ‘que devuelve 3 class class?’ answers: answers).

exam := SWTExamModel questions: questions.

questionToAnswer := exam newQuestion.

questionToAnswer answerChoosed: (questionToAnswer answers at: 1).

questionToAnswer := exam newQuestion.

questionToAnswer answerChoosed: (questionToAnswer answers at: 3).

questionToAnswer := exam newQuestion.

questionToAnswer answerChoosed: (questionToAnswer answers at: 3).

exam score

El resultado obtenido nos debería devolver 20 puntos. Pues la primer pregunta la contestamos incorrectamente, y las dos subsecuentes correctamente.

La interfaz web con SWT sin MVC distribuido

La interfaz de nuestro examen, será bien simple también, solo tenemos que mostrar nuestra pregunta, y las posibles respuestas para que las pueda elegir el usuario. La forma de elegir será a través de un botón por ahora. Cada vez que responde una pregunta, tenemos que continuar a la siguiente, hasta que termine y mostrar el puntaje final obtenido. Para lograr esto, vamos a modelar nuestra página a través de una clase que hereda de SWTClientApplication. La vamos a denominar SWTClientExamApplication, y desde el servidor vamos a tener a su controlador, llamado SWTServerExamApplication.

En el cliente, vamos a tener la pregunta como modelo, y en el server al examen.

SWTServerExamApplication>>exam

” Returns the exam of the receiver.”

exam isNil ifTrue: [ self initializeExam ].

^exam

SWTServerExamApplication>>initializeExam

” Initialize the exam.”

| questions answers |

questions := OrderedCollection new.

answers := OrderedCollection new.

answers

add: (SWTAnswerModel answer: ‘Object’ score: 0);

add: (SWTAnswerModel answer: ‘Collection’ score: 0);

add: (SWTAnswerModel answer: ‘Set’ score: 10);

add: (SWTAnswerModel answer: ‘Bag’ score: 0);

add: (SWTAnswerModel answer: ‘SWTModel’ score: 0);

yourself.

questions add: (SWTQuestionModel question: ‘De quien hereda Dictionary?’ answers: answers).

answers := OrderedCollection new.

answers

add: (SWTAnswerModel answer: ‘un objeto’ score: 0);

add: (SWTAnswerModel answer: ‘una clase’ score: 0);

add: (SWTAnswerModel answer: ‘una variable global’ score: 10);

add: (SWTAnswerModel answer: ‘El nombre del producto’ score: 0);

add: (SWTAnswerModel answer: ‘una nueva distro de Linux’ score: 0);

yourself.

questions add: (SWTQuestionModel question: ‘Que es Smalltalk?’ answers: answers).

answers := OrderedCollection new.

answers

add: (SWTAnswerModel answer: ‘la clase del 3′ score: 0);

add: (SWTAnswerModel answer: ‘la clase Class’ score: 0);

add: (SWTAnswerModel answer: ‘Integer class’ score: 10);

add: (SWTAnswerModel answer: ‘un error’ score: 0);

add: (SWTAnswerModel answer: ‘nil’ score: 0);

yourself.

questions add: (SWTQuestionModel question: ‘que devuelve 3 class class?’ answers: answers).

self exam: (SWTExamModel questions: questions).

SWTServerExamApplication>>newQuestion

” Returns a new question of the receiver.”

^self exam newQuestion.

Una parte muy importante, dado que el cliente, es un mini-Smalltalk, pues todo lo que estamos escribiendo en nuestra clase cliente, a la hora de funcionar, esto se traduce a Javascript. El framework posee un traductor de Smalltalk a Javascript bastante complejo, y como Smalltalk y Javascript son asimétricos, es decir, no todo el potencial del Smalltalk puede ser traducido a funcionalidad en Javascript, debemos tenerlo en cuenta a la hora de poner comportamiento en el cliente.

Todas las clases de nuestro modelo que sean usadas en el cliente, debemos enumerarlas en un mensaje de clase #jsClassesToInclude. Para que el traductor, genere estas clases del modelo en nuestro mini-Smalltalk en Javascript. Para nuestro ejemplo, vamos a hacer uso de las preguntas y las respuestas, por consiguiente, redefinimos este mensaje:

SWTClientExamApplication class>>jsClassesToInclude

^ {SWTQuestionModel. SWTAnswerModel }

Ahora en el cliente, haremos nuestra vista

SWTClientExamApplication>>newQuestion

” Returns the a new question of the receiver and updates the questions panel of the receiver.”

self isolatedAsynchronousRPCMethods:

[ self question: self serverSide newQuestion.

self updateQuestionsPanel ]

Ahora, lo que haremos son los widgets de la vista, esto se hace redefiniendo un método #initializeWidgets.

SWTClientExamApplication>>initializeWidgets

” Initialize the widgets of the receiver.”

| root |

root := self rootWidget.

root

addWidget: (questionsPanel := SWTPanel new).

En este método hemos definido una variable de instancia que guardara un SWTPanel, que es quien contendrá las preguntas, y a medida que el usuario las contesta, entonces, usaremos este panel para actualizar los datos en el navegador.

SWTClientExamApplication>>updateQuestionsPanel

” Update the questions panel of the receiver.”

questionsPanel clearWidgets.

self question isNil

ifTrue:

[ ^questionsPanel

addWidget: (SWTHeader contents: 'Puntaje final: ' , self finalScore)].

questionsPanel

addWidget: (SWTText

contents: ‘Pregunta: ‘ , (self question perform: #question));

addHorizontalRule.

(self question perform: #answers) do:

[:answer |

questionsPanel addWidget: (SWTButton caption: (answer perform: #answer)

onClick: [:event | self answerChoosed: answer])]

Un detalle que debemos notar de este #updateQuestionsPanel, es que si la pregunta es nula, es decir, ya no tenemos más preguntas para mostrar, entonces mostramos el puntaje final obtenido del usuario.

Para ello definimos el mensaje:

SWTClientExamApplication>>finalScore

” Returns the final score of the receiver.”

^self serverSide score

A la hora que se conecta el cliente con el server, vamos a hacer que este se inicialize con la primer pregunta para que la muestre, para engancharnos de este evento, redefiniremos un mensaje que nos provee el framework, el #justConnected. De esta manera nos aseguramos que solo la pregunta será nula, cuando el examen haya finalizado, dado que cuando se conecta por primera vez el cliente, se inicializará con la primer pregunta del examen.

SWTClientExamApplication>>justConnected

” On just connected the receiver.”

super justConnected.

self newQuestion.


Lo que hemos hecho es decirle, una vez que te conectás con el servidor, envíame una pregunta, e inicializamos el MainPanel, que es nuestro panel donde mostramos las preguntas.

¡Muy bien! Ahora ya tenemos nuestro examen listo ¡Es hora de probarlo!

Screenshot tutorial pregunta

Y la pantalla final del puntaje:

Screenshot puntaje final non MVC

Resumen

Este ejemplo es muy improductivo e ineficiente, porque, como se puede observar en la ultima imagen, usa demasiados mensajes al servidor, y no usa el MVC distribuido que nos proporciona el framework. En nuestra segunda sección, haremos que este mismo ejemplo, sea hecho con el MVC distribuido, mejorando considerablemente la cantidad de mensajes que usan el servidor a través de eventos.

¡La cantidad de peticiones que realizó fueron 30! En la siguiente sección vamos a explorar y refactorizar ciertos mensajes haciendo uso no solo del MVC distribuido sino también de un modelo en el servidor más activo que en la primer parte.

La interfaz web con SWT con MVC distribuido

Una vez que sabemos como podemos hacerlo funcionar, ahora, vamos a hacerlo funcionar bien.

Vamos a usar el mismo modelo, con algunas modificaciones, pero usando el Model-View-Controller distribuido que provee el framework, el cual, está optimizado para Internet, dado que un mal uso del servidor, con muchos usuarios, sería muy caótico, además de desaprovechar el framework.

Creando nuevas clases

Para empezar vamos a redefinir todo la parte web en otras clases. Las vamos a denominar SWTClientMVCExamApplication y SWTServerMVCExamApplication a las nuevas vistas con MVC.

SWTClientApplication subclass: #SWTClientMVCExamApplication

instanceVariableNames: ‘exam questionsPanel’

classVariableNames:

poolDictionaries:

category: ‘TutorialSWT-SWT’

Y el servidor

SWTServerApplication subclass: #SWTServerMVCExamApplication

instanceVariableNames: ‘exam’

classVariableNames:

poolDictionaries:

category: ‘TutorialSWT-SWT’

Modificando el modelo

A nuestro modelo anterior, tenemos que agregarle la parte escencial del MVC, que es que nos avise cuando un evento se produce en él. Las dos únicas situaciones importante en el modelo, que son de interés para nuestra vista, son cuando se gestiona una nueva pregunta (y esto es porque el usuario ya respondió la anterior o porque comienza el examen) y cuando se termina el examen. Estas son las dos situaciones donde necesitamos que nos avise el modelo, y a las cuales, la vista se suscribirá para tomar acción en ellas.

Para ello modificamos el modelo haciendo que la pregunta actual que está siendo mostrada por el sistema al usuario la guardemos en una variable de instancia del examen, cosa que antes no hacíamos, entonces nuestra clase de examen quedaría de esta manera:

SWTModel subclass: #SWTExamModel

instanceVariableNames: ‘questions questionsAnswered currentQuestion’

classVariableNames:

poolDictionaries:

category: ‘TutorialSWT-Core’

y los mensajes que modificaremos serán #newQuestion y

SWTExamModel>>newQuestion

” Returns a new question for the exam.”

| newQuestion randomIndex maximumInteger |

randomIndex := Random seed: 2345678901.

[ maximumInteger := self questionsNotAnswered size.

maximumInteger = 0 ifTrue: [ self triggerEvent: #examFinished. ^nil ].

newQuestion := self questionsNotAnswered at: (randomIndex nextInt: maximumInteger).

self questionsAnswered includes: newQuestion] whileTrue: [].

self currentQuestion: newQuestion.

self questionsAnswered add: self currentQuestion.

self triggerEvent: #newQuestion.

^self currentQuestion


Como se puede observar en el código del método, lo que hemos agregado es el uso de la variable de instancia #currentQuestion a través de sus mensajes de acceso, y hemos puesto los eventos, el primero denominado #examFinished, que se dispara cuando ya terminaron las preguntas, y el otro #newQuestion, que se dispara cuando hay una nueva pregunta disponible.

Vamos a agregar el mensaje al examen #answerChoosed:, donde se la pasa la respuesta seleccionada y genera una nueva pregunta.

SWTExamModel>>answerChoosed: anAnswer

self currentQuestion answerChoosed: anAnswer.

self newQuestion.


Se puede apreciar que ya en este nuevo modelo, levemente modificado, un circuito, donde una vez que se contesta una pregunta, se inicia otra, si se termino, avisa, y si está comenzando, con solo llamar a generar una nueva pregunta damos comienzo al examen. Con este pequeño flujo de trabajo, vamos a rediseñar nuestro Servidor y nuestro Cliente para bajar las 30 peticiones que hace el cliente al servidor.

La vista con MVC

Esta nueva vista, al momento que se conecta con el servidor, le pedirá su modelo (un exámen), registrará los eventos y luego dará comienzo al examen.

SWTClientMVCExamApplication>>justConnected

” On just connected the receiver.”

super justConnected.

self exam: self serverSide exam.

self registerEvents.

self serverSide startExam


La suscripción a los eventos del modelo

SWTClientMVCExamApplication>>registerEvents

” Register the events with the model of the receiver.”

self exam

when: #examFinished send: #showFinalScore to: self;

when: #newQuestion send: #showQuestion to: self.


Y en el lado de la clase, vamos a decirle al framework que solo vamos a pasar el examen hacia la vista como modelo.

SWTClientMVCExamApplication class>>jsClassesToInclude

^ {SWTExamModel}

En este método, cuando se produce un evento u otro, llama a dos mensajes distintos dentro de la vista: #showFinalScore y #showQuestion.

Si terminó el examen, entonces se ejecuta:

SWTClientMVCExamApplication>>showFinalScore

” Shows the final score in the questions panel of the receiver.”

self isolatedAsynchronousRPCMethods:

[ questionsPanel

clearWidgets;

addWidget: (SWTHeader contents: 'Puntaje final: ' , self serverSide score printString) ]

En este método, estamos haciendo que esta operación del cliente sea ejecutada en modo batch de una sola vez contra el servidor. Esto es unas de las características que brinda el framework para reducir el tráfico de datos.

Cada vez que hay una nueva pregunta se ejecuta:

SWTClientMVCExamApplication>>showQuestion

” Shows the questions panel of the receiver.”

questionsPanel clearWidgets.

questionsPanel

addWidget: (SWTText contents: ‘Pregunta: ‘ , (self serverSide currentQuestionString));

addHorizontalRule.

(self serverSide currentQuestionAnswersString) inlineDo:

[:answerString |

questionsPanel addWidget: ((SWTButton caption: answerString)

onClick: [:event | self serverSide answerChoosed: event source caption ])]

En esta método, lo que está realizando es que cada vez que se ejecuta, pide al servidor la pregunta, las posibles respuestas y arma la web.

Armando el servidor con su respectivo examen

En el servidor, vamos a definir #startExam, que lo unico que hará es decirle al modelo que comienze el examen por medio de la generación de una nueva pregunta

SWTServerMVCExamApplication>>startExam

” Start the current exam.”

self exam newQuestion

Los dos siguientes mensajes retornan los textos necesarios para la pregunta y sus posibles respuestas

SWTServerMVCExamApplication>>currentQuestionString

” Returns the current question strings.”

^self exam currentQuestion question

El cliente enviará al servidor el siguiente mensaje cuando la respuesta se haya seleccionado:

SWTServerMVCExamApplication>>answerChoosed: anAnswerString

” Set the answer choosed from the button caption clicked.”

self exam

answerChoosed: (self exam currentQuestion answers detect: [ :answer | answer answer = anAnswerString ])

¡Muy bien! Ahora ya tenemos nuestro nuevo examen listo ¡Es hora de probarlo!

Screenshot puntaje final MVC

Como podemos analizar de ver en la imagen anterior, la cantidad de pedidos al servidor se vieron reducidos en un 60%, lo que es un gran ahorro de tráfico para cualquier tipo de aplicativo.

Resumen

Como hemos visto a través de este ejemplo, el framework permite hacer aplicativos web en forma muy sencilla, sin ningún uso otras tecnologias más que Smalltalk y usando objetos. Y como se puede comprobar, el uso de un MVC distribuido es eficaz y nos permite tener muchas más ventajas, como multiples vista (N cantidad de navegadores sobre un mismo modelo), optimización de tráfico de datos a través de la red y una actualización sin necesidad de que el cliente lo tenga que peticionar al servidor.

Miércoles 5/marzo/2008

Diseño de baraja en 3D

Archivado en: Diseño,General — Diego Gomez Deck @ 11:00 am

Ya tenemos bastante avanzado el diseño de una de la barajas que incluiremos en el juego.

cartas3d.png

Pueden descargar los PNGs desde forjamari. Más información en http://igosoftware.wordpress.com/

Domingo 20/enero/2008

Mini aplicaciones sobre Smalltalk Web Toolkit (SWT)

Archivado en: Programación — Hernan Galante @ 2:24 am

Introducción

En este framework, basado en la arquitectura Comet y con un mini-Smalltalk en los navegadores sobre Javascript, tenemos dos clases principales para hacer un aplicativo, SWTClientApplication y SWTServerApplication. La primera es la clase que modela la parte cliente, que corre en el navegador y sobre Javascript, y de la cual heredará nuestra clase que será la responsable de presentar la interfaz al usuario; la segunda, es la clase en el servidor, que corre en Smalltalk, y de la cual se heredará para darle comportamiento al aplicativo controlando al cliente o los clientes. El framework también nos provee de un esquema de Model-View-Controller distribuido. Vamos a realizar dos simples ejemplos para mostrar el uso de este framework.

Ejemplos de uso

Vamos a hacer dos aplicaciones muy sencillas, la primera es un ejemplo de como usar el framework en una aplicación donde el servidor y cliente solo se comunican en forma básica, y en la segunda, es una adaptación de la primera pero usando algunas funcionalidades más.

Ping/Pong

La idea detrás de este aplicativo es que el cliente envíe un “ping” al servidor, y este le responda con un “pong”. Es muy simple y vamos a explorar las cualidades más básicas del framework al escribir esta aplicación.

Creando nuestras clases

Para comenzar vamos a crear nuestra clase cliente, heredando de SWTClientApplication y a la cual vamos a denominar SWTPingPongClientApplication.

Para el cliente:

SWTClientApplication subclass: #SWTPingPongClientApplication

instanceVariableNames:

classVariableNames:

poolDictionaries:

category: ‘SWT-Examples’

Para el server:

SWTServerApplication subclass: #SWTPingPongServerApplication

instanceVariableNames:

classVariableNames:

poolDictionaries:

category: ‘SWT-Examples’

Dándole un nombre al aplicativo

Con las clases definidas, vamos a darle un nombre a nuestro aplicativo, esto lo hacemos redefiniendo un mensaje de clase en la parte server a través de #applicationName, nuestro aplicativo se llamará “Ping/Pong”.
El principal propósito de esto, es que el framework nos presenta una lista de aplicativos a ejecutar por defecto en el navegador, si este lo apuntamos a la dirección por defecto http://127.0.0.1:2222, y el título que usa para hacer la presentación de cada aplicativo, es este mensaje.

SWTPingPongClientApplication class>>applicationName

^ ‘Ping Pong’

Poniendo los controles web

Como mencionamos anteriormente, la parte cliente es la responsable de la presentación en el navegador, por lo tanto, vamos a crear los elementos necesarios. Para realizar esto, tenemos toda una jerarquía de Widgets o controles web, que se clasifican a partir de SWTWidget.
El framework nos provee de ciertos mensajes que debemos definir, unos de estos es #initializeWidgets. En este mensaje se define la construcción de la página web, armando los controles que irán, como se componen y que eventos y propiedades tienen. Para nuestro caso, lo que queremos que vea el usuario es un titulo con el nombre de nuestro aplicativo y vamos a colocar un botón donde el usuario envia el “Ping” al servidor.

SWTPingPongClientApplication>>initializeWidgets

“Initialize the receiver’s widgets”

| root |

root := self rootWidget.

root addWidget: (SWTHeader level: 2 contents: self class applicationName).

root addWidget: (SWTButton caption: ‘server ping’ onClick: [:event | self serverSide ping]).

Como podemos apreciar en el método anterior, lo hacemos es crear un control raíz, o root widget, y a partir de este armamos nuestra página. En este caso, hemos agregado primero un título de nivel 2, o como se lo conoce en HTML, un tag H2 con el nombre de nuestra aplicación usando el método más arriba definido. Por último, se agregó un botón con la leyenda “server ping”, y que, al ser clickeado envia al servidor el mensaje #ping.

Implementando el ping en el servidor

Una vez hecha la parte cliente, vamos a implementar el mensaje #ping, que estamos enviando desde el navedor al presionar el botón. Desde el cliente, cuando enviamos #serverSide, nos retorna un objeto remoto a nuestra aplicación del lado del servidor. Es por ello, que vamos a implementar el comportamiento del “ping” en el server.

SWTPingPongServerApplication>>ping

Transcript show: ‘ping!’; cr.

self clientSide pong.

Lo que hemos definido en el servidor, es una vez que recibimos el #ping, escribimos el mensaje en el Transcript de Smalltalk (es una ventana de salida de mensajes) y luego, le devolvemos un “pong” al cliente.
Como podemos apreciar, para enviar mensajes hacia un lado u otro usamos un mensaje, para obtener la parte cliente desde el servidor es #clientSide, y para obtener la parte del servidor desde el cliente es #serverSide. De forma tan simple, podemos enviar comunicación para un lado o para el otro a través del canal establecido por la arquitectura de Comet. Recordemos que el HTML es un protocolo asimétrico, es decir, solo la comunicación la establece el cliente, y el servidor se limita solo a contestar las peticiones. En la arquitectura comet que usa este framework, esa diferencia no existe, ya que la comunicación entre el cliente y el servidor no se corta. En HTML una vez que el servidor haya cumplido con las peticiones del cliente la comunicación se corta. En Comet el servidor también puede enviar información al cliente sin que este último la haya tenido que solicitar. Esto es muy útil en aplicativos como sitios de remate, salas de conversación o chat, sitios de la bolsa de valores, y para cualquier aplicativo web donde la información cambie en el servidor y los clientes no tengan que hacer las peticiones para mantenerse informados, sino que sean informados en el instante en que se producen los eventos en el servidor.

Implementando el pong en el cliente

Ahora, por último, nos falta implementar el mensaje #pong en el cliente, y lo que vamos a hacer, es que cuando lo reciba muestre un mensaje al usuario con la expresión “Pong!”

SWTPingPongClientApplication>>pong

self inform: ‘pong!’

Probando el Ping/Pong

Para hacerlo, tenemos dos maneras, la primera es apuntando a nuestro navegador a la dirección: http://127.0.0.1:2222.
Allí nos presenta un listado de aplicativos que podemos usar, el nuestro, aparecerá listado con el nombre que le indicamos en el applicationName.
La otra forma, es directamente apuntando al aplicativo, en este caso: http://127.0.0.1:2222/SWTPingPongClientApplication.
Listo, ya estamos preparados para probar nuestro aplicativo. A continuación presentamos un screenshot del mismo.

Screenshot del ping/pong funcionando

Resumen del Ping/Pong

Hemos visto que se debe heredar de dos clases fundamentales del framework y que estas nos proveen unos mensajes que redefinimos para implementar nuestro comportamiento, esto en el cliente es el #applicationName y el #initializeWidgets.
En solo estos dos mensajes más un comportamiento básico de dos mensajes ping/pong hemos armado nuestro primer aplicativo. Como se vió también en este ejemplo, el framework nos da soporte para tener controles (SWTWidgets) y de comportamiento básico en la web, como lo es mostrar un mensaje (#inform:).

Echo Request / Echo Reply

Este aplicativo es muy parecido al ejemplo anterior, pero con la diferencia, que un echo envia un objeto como parámetro al server y este lo único que hace es reponderlo nuevamente. Es como un ping/pong, pero ahora, con una pelota.
Para realizarlo, vamos a repetir la misma secuencia que con el ejemplo anterior, pero esta vez, enviando un mensaje al servidor y este nos lo va a devolver.

Para el cliente:

SWTClientApplication subclass: #SWTEchoClientApplication

instanceVariableNames:

classVariableNames:

poolDictionaries:

category: ‘SWT-Examples’

Para el server:

SWTServerApplication subclass: #SWTEchoServerApplication

instanceVariableNames:

classVariableNames:

poolDictionaries:

category: ‘SWT-Examples’

Y le vamos a definir el nombre del aplicativo:

SWTEchoClientApplication class>>applicationName

^‘Echo!’

Poniendo los controles web

Vamos crear un titulo, un lugar donde poner un texto y al presionar el enter, enviará el texto al servidor.

initializeWidgets

“Initialize the receiver’s widgets”

| root input |

root := self rootWidget.

input := SWTInputText contents: .

input

onKeyPress: [:event | event keyCode = 13

ifTrue: [self serverSide echoMessage: event source contents]].

root

addWidget: (SWTHeader level: 2 contents: self class applicationName);

addWidget: (SWTText contents: ‘Ingrese un mensaje y presione enter para enviarlo al servidor: ‘);

addWidget: input

Lo que hemos hecho en este mensaje, es utilizar las propiedades que nos brinda el SWTInputText, al presionar una tecla, y si esta tecla es un enter (codificado como 13), se envia un mensaje al server (#echoMessage:), con un argumento, este no es más que el contenido del control que generó el evento. En este caso, ese contenido es el texto que ingresamos.

Ahora, vamos a definir, nuestro #echoMessage: en el servidor, que lo que hace, no es otra cosa que devolverlo al cliente de la siguiente manera:

SWTEchoServerApplication>>echoMessage: aMessage

self clientSide echoMessage: aMessage

En el cliente, ahora necesitamos implementar nuestro mensaje #echoMessage:, que no hará otra cosa más que mostrar un cartel en el navegador con el mensaje.

SWTEchoClientApplication>>echoMessage: aMessage

self inform: aMessage

Probando el Echo

Para hacerlo, como sabemos, hay dos maneras, pero vamos a usar la forma directa: http://127.0.0.1:2222/SWTEchoClientApplication.

Y el resultado es:

Screenshot Echo Application

Como podemos apreciar, enviamos un mensaje, le dimos enter, y el cartel nos muestra que escribimos.

Resumen del Echo

En este aplicativo, hemos aplicado lo mismo que para el Ping/Pong, pero con la diferencia que usamos un nuevo control, y sus eventos para generar un ping/pong pero con un mensaje que nosotros indicamos.

Conclusiones

Estos dos mini-aplicativos que presentamos no tenían otra intención que hacer un pequeño uso del framework SWT.
Las aplicaciones que se pueden lograr en esta plataforma son mucho más complejas y ricas. La idea que hay detrás es abstraerse, por medio del framework, de la cantidad de complicaciones que genera un aplicativo web y enfocarse en la resolución adecuada de nuestro problema. Para ello SWT nos provee del lado cliente un mini-Smalltalk hecho sobre Javascript, hay comunicación constante entre servidor y cliente a través de la arquitectura de Comet, hay un framework de Model-View-Controller distribuido que nos permite hacer aplicaciones más desacopladas y optimizadas para funcionamiento sobre la red, y una capa de controles web, que podemos expandir hacia donde necesitemos. Como se puede apreciar, es mucho trabajo el que realiza el framework en aras de conseguir una abstracción adecuada para desarrollar aplicaciones más complejas sobre la web.

Domingo 6/enero/2008

Introducción a Smalltalk Web Toolkit (SWT)

Archivado en: Programación — Hernan Galante @ 11:42 am

Introducción

Desde la aparición de Internet se han desarrollado gran cantidad de soluciones y tecnologías para el desarrollo en web, con los últimos avances en las mejoras sobre los navegadores a partir de tecnologías como AJAX y controles web más productivos, se acuñó el término web 2.0 para demarcar una nueva era en el desarrollo de aplicaciones. La intención de este articulo es presentar una introducción a Smalltalk Web Toolkit, un framework para la construcción de aplicativos web complejos usando Smalltalk, en particular, está desarrollado sobre Squeak.

Arquitectura

El framework se basa en que una aplicación tiene 2 partes bien definidas, una parte cliente, que corre en los navegadores en Javascript, y una parte servidora, que corre sobre Smalltalk con un web server basado en Comet. La parte cliente, no es programada en Javascript, sino que se desarrollo todo sobre Smalltalk. El framework provee un traductor a Javascript, denominado ST2JS. Este traductor respeta prácticamente toda la semántica Smalltalk y provee un juego de clases base para crear un mini-ambiente. Por lo tanto, al desarrollar se asume que en el cliente cuenta con un Smalltalk corriendo sobre cualquier navegador que soporte a Javascript. La parte servidora, es un servidor web con Comet. Esta es una técnica de programación web que se utiliza para la entrega de datos entre el cliente y el servidor a través del protocolo HTTP, pero la diferencia es que rompe con la asimetría del HTTP tradicional, que solo se limita a contestar las peticiones del cliente, sino que ahora es simétrico, pues el servidor puede entregar contenido al cliente sin que este lo haya solicitado. Esto también se conoce como “HTTP Push”, “Server Push” o “Reversed Ajax” en inglés.

La comunicación entre los dos mundos

La comunicación entre el cliente y el servidor es transparente para el desarrollador, ya que el framework realiza la conexión entre ambos a través del uso de estándares web como RPC, con XML y usando JSON para serializar los objetos que pasan del cliente al servidor y viceversa. No todos los objetos son pasados por copia al cliente y no todo regresa al servidor, como sabemos, es muy costoso pasar tanta información sobre la red, es por ello, que el framework realiza ciertas optimizaciones sobre esta capa de comunicación. Los objetos del modelo de nuestro aplicativo son referencias remotas, por lo tanto, en el cliente tenemos objetos que representan a los objetos que están del lado del servidor, y solo los objetos que pasan por copia son aquellos objetos que son parte del soporte de la comunicación.

ST2JS

En mucho de los aplicativos web que se desarrollan para la era web 2.0 tienen mucho código en Javascript, pero como el SQL, y los mapeos de objetos a relacional, lo que se intenta es no escribir en un lenguaje distinto al que estamos usando para desarrollar las reglas de negocio del aplicativo. Este traductor respeta prácticamente toda la semántica de Smalltalk y la traduce a Javascript. Es decir, en Smalltalk podemos tener clases, instancias, mensajes de instancia, mensajes de clase, bloques, esto el traductor lo traduce a otros recursos, pues Smalltalk y Javascript no son semánticamente simétricos, pero que al final, terminarán haciendo lo que se describe en Smalltalk.

Características del ST2JS

Mapeo de nombres

El traductor sigue una serie de reglas para convertir los nombres Smalltalk (nombre de clase, de métodos, de variables de instancia) a nombre válidos en Javascript.

  • Nombre de clases: Los nombres de clases se traducen a Javascript con un prefijo “ST.” para evitar colisiones con otras clases de Javascript.
  • Nombre de variables de instancia: En Smalltalk es válido tener el mismo nombre de una variable de instancia y un mensaje que lo accede, pero en Javascript no es posible, por lo tanto, el traductor agrega un guión bajo al nombre de la variable de instancia.
  • Nombre de mensajes: Hay casos triviales y complejos a la hora de traducir los mensajes.

    Smalltalk

    Javascript
    Comentario
    self foo^self foo self.foo()return (self.foo()); Es el caso más simple
    self bar: 2 self.bar_(2) El “:” se transforma a “_”
    self bar: ‘a’ foo: true self.bar_foo_(’a’, true) Las partes del mensaje se concatenan
    #+ __add__  
    #- __sub__  
    #class __class__  
    2 asString ST.asString(2)  
    2 >= 1
    (2).__greaterThanOrEquals__(1);
     
    #//
    __integerQuotient__
     
    #@ __at__  
    1 = 2 ST.equals(1,2);  
Mensajes inline

Los mensajes inline, son atajos a operaciones continuas y repetitivas y del cual se conoce su resultado de antemano. En vez de realizar la operación completa, cuando se encuentra una operación inline se retorna su resultado sin el costo computacional de tener que realizarlo una y otra vez, por lo que se gana en performance. En este caso, hay ciertos mensajes inline que se utilizan con una frecuencia muy alta en el código Smalltalk y para no tener que traducir constantemente a código que ya se conoce cual será su resultado o a que funciones o estructuras de control serán traducidas, entonces se hacen inline. Algunas de estas son, como por ejemplo, las bifurcaciones (#ifTrue, ifFalse), las repeticiones (#whileTrue, #whileFalse), las iteraciones (#do:), los chequeos en colecciones (#isEmpty) :

  • #inlineIfTrue:
  • #inlineIfTrue:ifFalse:
  • #inlineIfFalse:
  • #inlineDo:
  • #inlineWithIndexDo:
  • #inlineWhileTrue:
  • #inlineWhileFalse:
  • #inlineIsEmpty
  • #inlineNotEmpty
  • #to:by:do:

Se puede ver la lista completa de mensajes inline en el framework en el mensaje: S2STranslator>>initializeSelectorMapping. Ejemplo de mensaje inline: El #do: en Smalltalk

S2SArrayExtension>>do: elementBlock

“Evaluate aBlock with each of the receiver’s elements as the argument.”


self
inlineDo:[:each | elementBlock value: each. ].

El #do: de Smalltalk en Javascript:

ST.Array.prototype.do_ = function(aBlock) {

var self = this;

var indexLimiT;indexLimiT = self.size();

for (var index = 1; index <= indexLimiT; index++) {aBlock.value_(self.at_(index));}

return (self);

};

El código anterior se obtuvo evaluando:

S2STranslator instance methodSourceFor: Array selector: #do:

Insertando código Javascript en forma directa

El traductor nos permite insertar código Javascript en forma directa desde el código Smalltalk. Esto tiene utilidad no solo para escribir el mismo traductor, sino para operaciones especiales donde es más fácil o donde se desee optimizar o insertar directamente código Javascript. Como también tenemos la posibilidad de escribir SQL directo en un mapeador de Objectos a relacional, lo mismo sucede en ST2JS.

Ejemplo
El mensaje en Smalltalk que inyecta código Javascript
at: anInteger

“Answer my element at index anInteger. at: is used by a knowledgeable client to access an existing element”

^self jsLiteral: ‘this[anInteger - 1]‘

El mensaje traducido a una función en Javascript evaluando:

S2STranslator instance methodSourceFor: S2SArrayExtension selector: #at:

En Javascript:

Array.prototype.at_ = function(anInteger){

var self = this;

return (this[anInteger - 1]);

};

Bloques de Smalltalk a Javascript

Javascript puede tener funciones que contienen funciones, y son válidas en un contexto únicamente, además, contiene funciones anónimas, que son funciones sin nombre que las podemos ejecutar encapsulándolas entre paréntesis. Los bloques Smalltalk son bloques de código que puede ser evaluados en un contexto, entonces los bloques son traducidos a Javascript en funciones anónimas. Este mapeo es directo, excepto en dos casos. El primero es cuando self referencia al objeto receptor del mensaje, y no a la función Javascript, es decir, el self dentro del bloque se refiere al método que lo contiene. Pero el this en javascript se comporta igual, emulando la funcionalidad en Smalltalk. La segunda, es el retorno en un bloque, dado en smalltalk por el caracter ^ (caret), este causa el retorno del método y no solo del bloque.

Ejemplo, en Smalltalk con bloque con un argumento e, y su posterior ejecución con 7.

ClaseSmalltalk>>metodoX

[ :e | self testMethod + e ] value: 7

Se traduce en Javacript a:

ST.ClaseSmalltalk.prototype.metodoX = function(){
var self = this;
(function (e) {return self.testMethod().__add__(e)}).value_(7);
return (self);
};

Si el método incluye un retorno dentro del bloque:

ClaseSmalltalk>>metodoX

[ :e | ^self testMethod + e ] value: 7

Se traduce complejamente a:

ST.ClaseSmalltalk.prototype.metodoX = function() {
var self = this;
var _ret_ = new ST.ReturnValue();
try {(function (e) {throw _ret_.value_(self.testMethod().__add__(e));}).value_(7);
return (self);}
catch (_e_) {if (_e_ === _ret_) return _e_._value;
throw _e_;}
};

Y si retorna el resultado de la ejecución del bloque:

ClaseSmalltalk>>metodoX

^[ :e | self testMethod + e ] value: 7

Se traduce a

ST.ClaseSmalltalk.prototype.metodoX = function() {
var self = this;
return ((function (e) {return self.testMethod().__add__(e)}).value_(7));
};

Semántica de herencia
Mensajes a super

Los mensajes a super se traducen como mensajes a self, pero con un mensaje de nombre super_. En Smalltalk “super” indica que busca en la clase padre la ejecución del método en cuestión, a lo que es traducido a Javascript se dividen en dos casos. El primero, es directo en el caso de que el método exista solo una vez en una clase padre, pero si existiese en varias clases padre dentro de la jerarquía, el más compleja su búsqueda, y por lo tanto más lenta.

Primer caso

Si en la jerarquía de clases hay sólo una superclase con un método de ese nombre, se define la función super_ (en Javascript). Supongamos que tenemos dos clases en Smalltalk: SuperClassExample que tiene una subclase SubClassExample y ambas implementan #testMethod. En Javascript quedaría
La Subclase:

ST.SubClassExample.prototype.testMethod = function() {
var self = this;
self.super_testMethod();return (self);
};

La implementación al super:

ST.SubClassExample.prototype.super_testMethod() = ST.SuperClassExample.prototype.testMethod()

Y el método en la superclase:

ST.SuperClassExample.prototype.testMethod = function() {var self = this;return (self);};

Segundo caso:

Cuando existe más de una superclase con ese método, la implementación de super_ es un poco más complicada:

// super: slow case (more than one super implementation)
ST.SWTFlasher.prototype.super_initialize = function() {
var functions = [ST.SWTPanel.prototype.initialize, ST.Object.prototype.initialize];
if (typeof this.super_initialize_depth == “undefined”)this.super_initialize_depth = 0;
var result = functions[this.super_initialize_depth++].apply(this, arguments); this.super_initialize_depth–; return result;
};

Esta implementación es más lenta que la anterior, pero es compatible con la semántica del Smalltalk.

Ejemplos de uso del traductor desde un workspace en Smalltalk

El mensaje en Smalltalk a ser traducido:

S2SBaseTestCase>>testInspect

| array |

array := {‘foo’. 1@2. true}.

Uso del Traductor ST2JS en un Workspace:

S2STranslator instance methodSourceFor: S2SBaseTestCase selector: #testInspect.

Traducción generada:

ST.BaseTestCase.prototype.testInspect = function() {
var self = this;
var array;array = [''foo'', (1).__at__(2), true]; return (self);
};

Otras expresiones interesantes:

  • Traducir toda una clase
S2STranslator instance sourceForClass: S2SBaseTestCase
  • Generar todo el codigo fuente Javascript (Solo todas las subclases de S2SObjectExtension)

S2STranslator instance allJsSource

La comunicación entre el servidor y el cliente

La comunicación entre el servidor y el cliente, desde el framework, se hace a través de dos mensajes, #serverSide y #clientSide. El primero nos permite “viajar” desde el cliente hacia el servidor, y el segundo, en el caso inverso. Es decir, si estamos en la vista, y requerimos realizar una operación en el servidor a través del envío de un mensaje, se realiza de la siguiente manera

self serverSide miMensajeAlElServidor

y si estamos en el servidor y deseamos enviar un mensaje a el cliente, es:

self clientSide miMensajeAlCliente

Para ilustrarlo,

Arquitectura del SWT

Model-View-Controller Distribuido

El MVC distribuido que provee el SWT, tiene dos propósitos, el primero es poder hacer las interfaces en los navegadores separando el comportamiento de las vistas del modelo, y por el otro lado, tener una comunicación entre el servidor y el cliente optimizado de manera que no haya comunicación innecesaria para evitar el alto costo que tiene la transferencia de datos en la red. En una aplicación MVC, el M(odelo) está en el servidor (Smalltalk), y la V(ista) y el C(ontrolador) están en el cliente (Smalltalk en Javascript corriendo en el navegador). En una aplicación típica de MVC, la vista se conecta a eventos del modelo o a aspectos de este. Recordemos que los aspectos describen una parte o subconjunto de información del modelo que le será visible al usuario. Entonces, cada suscripción implica un envío de mensajes, si el modelo está remoto eso implica hacer una comunicación a través de la red. Un ejemplo simple, en donde una clase Cliente, tiene atributos como #nombre, #apellido, #fechaDeNacimiento y #dni. Para crear una vista con estos aspectos, la vista y el controlador, deberán suscribirse a eventos de cada atributo, lo que implican 4 peticiones al servidor. Si tenemos en cuenta, que antes de suscribirse hay que pedir la referencia remota de la instancia de Cliente, entonces tenemos N + 1 peticiones para crear la vista del objeto con N atributos. El caso empeora cuando cargamos un objetos y descargamos otro, lo que implica las N + 1 peticiones anterior más M cantidad de peticiones para desuscribirse de los eventos del objeto a descargar, siendo M la cantidad de atributos del objeto a descargar. Por lo tanto, esto generaría un alto nivel de transferencia de datos, haciendo muy lenta la velocidad del cliente. La solución adecuada a esto sería reducir a solo dos peticiones toda la operación entre el modelo y la vista. Es decir, en la primer petición se obtiene la referencia del objeto, y en la segunda, en batch todas las suscripciones y desuscripciones necesarias. Cuando se hace una petición al servidor a través del #serverSide, y se espera un valor de respuesta, este envio, se puede hacer asincrónico. El framework permite encolar varios envios asincrónicos en una sola petición, por ejemplo:

self isolatedAsynchronousRPCMethods:

[ "Limpia los widgets viejos, causando que el browser se desconecte de los eventos al modelo anterior."

mainPanel clearWidgets.

"Crea la nueva vista al nuevo modelo, causando la conexion a los eventos del modelo nuevo."

mainPanel addWidget: aRemoteModel defaultView. ].

Eventos extendidos

Otro patrón típico de las aplicaciones MVC es que la vista envía un mensaje (por ejemplo el mensaje #nombre) cuando se entera que el modelo cambió (por estar suscripto al evento de nombre #nombre). Si tenemos en cuenta que el Modelo está en el servidor, y que las Vistas/Controladores están en los browsers, veremos que cuando el modelo cambia (y el evento se propaga a los browsers, usando la conexión Comet) todas las vistas enviarán un mensaje al Server para obtener el nuevo valor del modelo. Para optimizar ese caso, se expandió el mecanismo de eventos suscribiendo a un Aspecto (SWTAspect) al modelo.

self model

when: self eventName

send: #modelChanged:

to: self

withResultsOfSelectors: {self getter}

onConflictSend: #conflict:.

La clave está en la última parte del mensaje: “withResultsOfSelectors: {self getter}”. Cuando ocurre un evento de nombre “self eventName”, el modelo enviará el mensaje “#modelChanged:” a “self”, incluyendo en el envío el resultado de enviar el mensaje “self getter” al modelo. De esta forma, el evento se propaga con la información extra necesaria evitando que los clientes (browsers) tengan que hacer un nuevo pedido para obtener el nuevo valor.

¿Como desarrollamos en este MVC?

El modelo de nuestro aplicativo debe heredar de la clase SWTModel. De esta manera, todas las instancias de las subclases de esta jerarquía que pasen a la vista, se hacen a través de un modelo remoto (SWTRemoteModel), por lo cual, ya se baja la cantidad de transferencia de datos a la vista al solo pasar una referencia y no la copia del objeto. Por cada cambio que se produzca en el modelo, y sea necesario que este cambio deba ser recibido por los suscriptores, enviamos un #triggerEvent: con el nombre del evento. Un ejemplo sería:

Cliente>>nombre: unNuevoNombre

nombre := unNuevoNombre.

self triggerEvent: #nombreClienteCambio

Con este simple ejemplo, por cada vez que cambia el nombre del cliente, hacemos que se dispare un evento informando a todos los suscriptores de este cambio. La siguiente pregunta es, ¿como nos suscribimos al evento? La respuesta es sencilla, al inicializar la vista, suscribimos a los elementos de la vista que queremos que reflejen el cambio. Para esto hacemos:

ClienteVista>>initializeWidgets

self model

when: #nombreClienteCambio

send: #refrescarNombreCliente

to: self.

De esta manera por cada evento que se produce en el modelo, disparamos la acción que necesitemos en su vista.

ClienteVista>>refrescarNombreCliente

clienteTextInput contents: self model nombre

Screencast de ejemplos

Algunos ejemplos, y también pruebas de conceptos, de este framework están en:

  • Ejemplo del Ping/Pong
    • Este ejemplo es bastante simple, es una especie de Hola mundo para SWT, mostrando las cualidades de construcción en SWT.
  • Live Wiki
    • Prueba de concepto de un Wiki, pero donde se edita directamente sobre el texto que se selecciona y todos los usuarios conectados a la misma página pueden ver los cambios apenas son ingresados.
  • Supermercado en linea
    • Prueba de concepto para compras sociales, en donde hacen las compras varios personas a la vez, pudiendo interactuar con el carrito de compras, conversar y ver el estado actual de los precios y el stock del supermercado
  • Planilla de cálculo sobre web
    • Prueba de concepto sobre una planilla de cálculo que puede estar siendo trabajada por varios usuarios al mismo tiempo y viendo los cambios al mismo tiempo

Referencias

Miércoles 26/diciembre/2007

Bienvenidos a Ceibo

Archivado en: General — raquelmartinez @ 12:51 pm

Ceibo es el nombre que hemos elegido para este proyecto. ¡Seguramente ha influido mucho que haya argentinos en él!.

Pues bien, Bienvenidos al blog que utilizaremos para desarrollar este proyecto: “Desarrollo de Videojuegos en Software Libre. Tipología: Inteligencia, Habilidad, Puzzles.

La Vicepresidencia Segunda y Consejería de Economía, Comercio e Innovación de la Junta de Extremadura ha adjudicado el concurso abierto “Desarrollo de Videojuegos en Software Libre” a la U.T.E compuesta por Fomento y Medio Ambiente de Extremadura y ConsultAr.com.

Se trata de un videojuego de cartas web con la baraja española. Habrá juegos monojugador (solitarios) y multijugador (Burro, Mus, Cuatrola, Tute, Brisca, Escoba), no necesita ningún tipo de instalación ni agregado alguno y es totalmente extensible.

Estado actual

Para el desarrollo del motor y de los juegos de cartas se utilizará el lenguaje y entorno de programación Squeak, responsable de proveer la infraestructura para las partidas de los diferentes juegos y de infraestructuras de comunicación en tiempo real entre los participantes incluyendo un chat por cada partida.

La interfaz gráfica de usuario será completamente dimensionable. El usuario accederá desde un portal principal donde se mostrará toda la información disponible de forma resumida. Desde la página de entrada, el usuario podrá ver la lista de juegos disponibles, los usuarios conectados, las partidas abiertas, los torneos… Además puede darse de alta y configurar su avatar.

El motor de juegos incluirá funcionalidades para creación y juego de torneos.

Como uno de los objetivos es aumentar la sensación de interacción entre las personas, los usuarios pueden configurar la apariencia del avatar que los represente dentro de los juegos.

El motor incluirá la opción de descarga de juegos individuales para su uso desconectado.

Otra característica es que los juegos podrán utilizar temas para cambiar su aspecto y también incluirán efectos visuales y de sonido.

Usaremos este espacio para ir informando del avance del proyecto. Iremos publicando diferentes notas sobre las nuevas características y funcionalidades del juego y además intentaremos que este blog se convierta en una herramienta más para el proyecto.

Desde aquí os invitamos a que participéis, se admiten quejas y sugerencias.
¡Manos a la obra!

El tema Rubric. Create a free website or blog at WordPress.com.

Seguir

Recibe cada nueva publicación en tu buzón de correo electrónico.