Mi cursada - UNQ‎ > ‎Clases‎ > ‎Bitácoras‎ > ‎Bitácoras 2011 S1‎ > ‎

Lunes 02/05/11 - Aplicaciones web con Pharo & Seaside

publicado a la‎(s)‎ 2 may. 2011 16:10 por Javier Fernandes   [ actualizado el 9 may. 2011 16:15 por gisela decuzzi ]

Smalltalk & Pharo & Seaside

En esta clase vamos a ver un enfoque bastante radical o diferente a los que veníamos viendo para la construcción de aplicaciones web. Para eso vamos a ver un framework llamado web Seaside, para el smalltalk "Pharo".
Pharo es un proyecto "fork" de squeak, y según nuestra opinión, uno de los proyectos con más actividad en la actualidad de smalltalks.
Eso nos da cierto soporte, e ideas nuevas al smalltalks como los traits, etc.


Primeros Pasos con Seaside

Abrimos entonces una imagen de ejemplo que ya contiene seaside.
Vimos que existe una ventana llamada "Seaside Control Panel" que permite controlar el webserver embebido. Ah si, claro, seaside trae un webserver embebido, llamado "Comanche" (de apache).
Entonces vimos como levantar y bajar seaside, y con un browser interactuamos con el:

  • http://localhost:8080/browse : muestra un listado de todas las aplicaciones seaside
  • http://localhost:8080/config : permite controlar la configuración de seaside.

Nota de color:

Seaside además trae una herramienta llamada "Pier", que es un "Content Management System". Ver


Primer Componente

Entonces vamos a comenzar a trabajar definiendo nuestra propia aplicación.

// TODO: ver como creó un nuevo component. De donde heredó, etc.

Listo, ya tenemos nuestro componente, pero cómo usarlo ? Cómo hacer que sea accesible al usuario, a través de wicket ?

Para eso tenemos que:
  • Agregar un método de clase "canBeRoot" = true en la clase de mi componente.
  • Luego, ir a la configuración de seaside http://localhost:8080/config
  • Y tenemos que agregar un "Dispatcher", que es básicamente como un "alias", donde le decimos a seaside que cuando el usuario pida cierta URL, eso muestre nuestro componentes. Para eso
    • Dispatcher -> "Add"
    • //TODO:

Vemos entonces que el dispatching se hace a un "componente" y no necesariamente a una "página", como en wicket.
Entonces acá vemos que Seaside es un framework "orientado a componente". Pero a diferencia de wicket, todo es un componente. Y no existe la idea central de navegación entre páginas. O, la página, como concepto principal de la aplicación.


Ejemplo Más Interesante

Pasamos entonces a un ejemplo más completo/interesante que el simple "hola mundo"
Vemos entonces que el objeto que recibíamos como parámetro del renderOn, era de tipo WARenderCanvas
Miramos un poquito esta clase, para ver qué podemos hacer.
Cada uno de los métodos de esta clase, se los llama "brushes". Así es la metáfora de seaside.
Con esto métodos, vamos a ir "construyendo" o declarando cómo va a ser el contenido del canvas, y por ende, al final, el html a generar.

Implementamos entonces el ejemplo de un Contador.

remderContentOn: html
    html text: html class name.
    html break.
    html anchor: 'Contador
   
Vemos entonces que "text", "break" y "anchor" son estos famosos "brushes".
Y entonces vemos que estos métodos, nos devuelven, de hecho un objeto, al cual podemos enviarle mensajes, para así especificarles detalles, que luego se utilizaran para generar el html.

Ejemplo:

remderContentOn: html
    html text: html class name.
    html break.
    html anchor
        callback: [];
        with: 'Contador


Vemos que el caracter punto y coma ';' nos permite encadenar mensajes al mismo objeto. Así en este ejemplo arriba estamos enviando los mensajes "callback" y "width" al mismo objetos respuesta de "anchor".

Modelo de Componentes


Acá se ve una particularidad de la idea de componente en seaside.
El anchor, por ejemplo, no es un objeto, sino que resulta de enviar el mensaje "anchor" a WACanvas.

Entonces, el modelo de componentes en seaside se dá más por el uso de las clases que nos dá seaside y sus brushes, y a la descripción de la pantalla en forma programática.
Esto en contraposición con otras tecnologías declarativas, como vamos a ver en flex (a través de un xml especial).
Y también a diferencia de los modelos basados en "transformaciones". Como el templating (evaluación de pedacitos de html con variables que se reemplazan luego), o como el XSLT, que permite transformar un xml a partir de otro xml que tiene una descripción de las transformaciones a hacer.


Callbacks y estado conversacional

Avanzamos con el ejemplo
Hicimos entonces un nuevo componente CIUContador
Con un atributo "count" (getter y setter)

renderContentOn: html
    html text: self count asString.
    html break.
    html anchor
        callback: [self answer];
        with: 'Volver


Y desde el componente anterior hacemos que el link nos muestre el Contador:

remderContentOn: html
    html text: html class name.
    html break.
    html anchor
        callback: [self call: CIUContador new];
        with: 'Contador


Ahora desde el componente inicial, ya podemos ir a nuestro nuevo componente.
Peero, cada vez que vamos y volvemos se instancia un nuevo componente, porque estamos haciendo CIUContador new.

Entonces, para modificar el contador vamos a completar el nuevo componente con dos nuevos links para incrementar y decrementar el contador.

renderContentOn: html

        html anchor callback: [self incrementar]; with: '++'.
    html text: self count asString.
        html anchor callback: [self decrementar]; with: '--'.

    html break.
    html anchor
        callback: [self answer];
        with: 'Volver


Ahora con este cambio, vemos que, al igual en wicket, seaside maneja transparente, automática y mágicamente, el request del browser al server y se encarga de llamar a nuestro bloque que le pasamos como "callback".
En nuestro ejemplo invocamos los métodos incrementar/decrementar que modifica el estado (la variable count). Y simplemente seaside vuelve a mostrar la misma instancia del componente.


El "Odioso" Problema del Back Button

Vimos que ahora con el comportamiento de "incrementar/decrementar" estamos manteniendo estado entre los diferentes requests.
Ahora, qué pasa si empezamos a usar el botón "back" del navegador ?
Cosas raras!
Porque el navegador no vuelve a pedir la página anterior, sino que muestra lo que el se acordaba (caché).
Esto hace que lo que vemos en el browser esté desincronizado con el estado en el server (la instancia del control)
Para solucionar eso agregamos un método "states" al componente.

states
        ^Array with: self
   
Este método nos permite devolver un array con objetos a recordar "por seaside", cuando alguien "va para atrás".
En este caso hace que se acuerde de todo el objeto.


Vimos que este problema es muy conocido en el desarrollo web. Y que existen diferentes técnicas para resolverlo, como [tratar] de invalitar la caché del browser, etc.

Continuations

 Modificamos entonces en el ejemplo la clase CIUEjemplos para agregar un link a un nuevo componente CIUCalculador:

remderContentOn: html

    html text: html class name.
    html break.
    html anchor
        callback: [self call: CIUContador new];
        with: 'Contador


    html break.

    html anchor
        callback: [self call: CIUCalculadora new];
        with: 'Calculador


Y entonces creamos un nuevo componente CIUCalculadora, pero esta vez heradamos de ????

Entonces implementamos el método "go"

go
     |sumando1 sumando2|
   sumando1 := (self request: 'Ingrese el primer sumando') asNumber.
   sumando2 := (self request: 'Ingrese el segundo sumando') asNumber.
   self inform: (sumando1 + sumando2) asString.

Como vemos, no tiene el canvas, con lo cual no puede "escribir html".

Y vemos entonces en el browser algo bastante mágico al hacer click en el link Calculadora !

  • Al usuario se le presenta una pantalla con un input y el mensaje "Ingrese el primer sumando". Al hacer submit ...
  • se le mostró una nueva segunda pantalla con otro input y el mensaje "Ingrese el segundo sumando". y al hacer submit..
  • se le mostró una tercer pantall con el resultado de la suma.
Vemos entonces que cada pantalla es exáctamente cada una de las lineas del método "go".
Esas líneas de código mágicas, utilizan mensajes especiales como "request" e "inform",  que se podrían pensar como pasos de una secuencia (o wizard). Y esos pasos serían de hecho pedidos al usuario, que requieren su interacción.
Seaside, a través de un feature llamado Continuations, nos permite escribir entonces todo el comportamiento en un único método, pero el se encarga de ir generando diferentes páginas como pasos, y mantener el estado y la navegación.

Lo que va haciendo es pausar la ejecución de cada linea, y cuando el usuario sigue al siguiente paso, continúa la ejecución. Y de ahí viene el nombre.

Jugamos aún más con Continuations

Modificamos entonces el nuevo componente para que como primer paso muestre nuestro contador.
La idea es que ahora el primer sumando no se obtenga mediante el ingreso manual del usuario (request), sino a través de usar nuestro componente "contador"

go

     |sumando1 sumando2|
   sumando1 := self call: CIUContador new.
   sumando2 := (self request: 'Ingrese el segundo sumando') asNumber.
   self inform: (sumando1 + sumando2) asString.

Modificamos entonces el Contador para que el link que antes era "Volver" ahora devuelva el valor actual del contador

renderContentOn: html
        html anchor callback: [self incrementar]; with: '++'.
    html text: self count asString.
        html anchor callback: [self decrementar]; with: '--'.

    html break.
    html anchor
        callback: [self answer: self count]; with: 'Listo'.


Y ahora al ejecutar el ejemplo, vemos que como primer paso nos lleva al contador, donde a través de los links incrementar/decrementar alteramos el contador, hasta que clickeamos "Listo" y eso nos lleva al siguiente paso. Finalmente nos muestra la suma de la Calculador.

CIUAutoCalculadora

Creamos un nuevo componente CIUAutoContador con una variable de instancia "contadores" y el método initialize

initialize
    super initialize.
    self contadores:
        //TODO: completar la inicialización

El renderContentOn

renderContentOn: html
    contadores
        do: [ :c | html render: c. html break ]

    html text: ('El total es:', self suma asString).

Y el método suma:

suma

    ^ self contadores inject: 0 into: [:a :c | a + c count].


Acá vemos cómo se pueden componener fácilmente los componentes seaside.

Diseñando en Seaside

Por último usamos una herencia entre ambos contadores CIUAutoContador y CIUContador.
Pero el punto es que los componentes en seaside con objetos, y se definen por clases, entonces tenemos las mismas herramientas para diseñar que cuando trabajamos con clases y objetos en cualquier otra parte de nuestro sistema.



Pendientes para la clase que viene

  • Componentes Decoradores
  • Formularios



Nota Sobre TPS

A partir de esta clase ya ustedes van a contar con una idea y pantallazo de las ideas de diseño detrás de wicket, jsp+servlets (model2) y seaside.
Esto los habilita a que puedan elegir uno de estos frameworks, para programar el TP 3.
La idea es, a partir de este jueves ya pasar al TP 3 que va a ser el más importante de la cursada.

Por otro lado, para este jueves deben tener ya una propuesta de tema de investigación para el TP 4.
La idea es cerrar ya los grupos y los temas elegidos.

Entonces, resumen

  • Jueves
    • Cerrar TP2
    • Seaside
    • Charlar un ratito sobre las ideas para el TP4
      • No lo cerramos ahí, pero lo encaminamos
  • Lunes que viene:
    • Cerramos TP3: qué van a hacer
    • Cerramos TP4: idem
    • No va a haber teoría nueva, porque nos vamos a concentrar con el TP3
Comments