En este capítulo se realiza una breve descripción del análisis de las tecnologías evaluadas para llevar a cabo la desconexión de una aplicación web.
Tras la elección de Django como framework, se adoptó como estrategia inicial, para desarrollo de la aplicación de esta tesina, intentar ejecutar un intérprete de Python en el navegador web para una posterior ejecución de Django. Esto precisa que la versión del intérprete posea al menos los paquetes de la librería estándar re, sys, time, urllib, datetime, mimetypes, entre otros, que son utilizados por el framework.
Además de la posibilidad de ejecución del intérprete en el navegador, se necesitó un sistema de almacenamiento persistente en el cliente tanto para el código de la aplicación (framework + aplicación) como para los datos (típicamente un RDBMS).
Otro aspecto importante que se tuvo en cuenta, además del intérprete, el almacenamiento local y la base de datos, fue que los frameworks web están diseñados para ser ejecutados en un entorno cliente-servidor. La interacción con una aplicación web se realiza generalmente mediante links, formularios y AJAX, y todas estas técnicas se traducen en alguna primitiva HTTP. En ausencia del servidor se debió realizar una adaptación de su funcionamiento.
También se analizaron otro tipo de consideraciones, como la seguridad. Transferir los datos de una aplicación en línea a una que se transporta en un navegador puede tener implicancias en la integridad de la información, ya que no es posible lograr un grado de aseguramiento al de un servidor web para una máquina potencialmente desconocida.
Además fue importante tener en cuenta que el acceso a los datos en una aplicación web está restringido por la propia aplicación. El usuario no tiene acceso a la base de datos, sino a la visión que la aplicación le da sobre ésta. Se puede decir que cada usuario o grupo tiene asociada una perspectiva de los datos.
Si se plantea que la transferencia de una aplicación web del servidor al cliente implica la copia de su base de datos, un usuario con suficientes conocimientos podría tener acceso a información que de otra manera no tendría (cuentas de usuario, registros de actividad, información económica o financiera, etc.). Por esto fue importante analizar qué datos puede ver cada usuario, grupo o rol en el sistema. La aplicación desconectada debería poseer una técnica de enmascaramiento sobre los datos (lo que podría repercutir en el desempeño) o trabajar con una base de datos reducida.
De lo anterior se infiere que no todas las aplicaciones desconectadas son idénticas, sino que en función del usuario tendrán más o menos funcionalidad y datos asociados. Además, en una aplicación desconectada no se requiere autenticación, o al menos, no de la misma manera que en la aplicación en línea, donde la autenticación suele encontrarse asegurada mediante SSL.
Otro aspecto considerado fue la posibilidad de sincronizar las instancias de aplicaciones desconectadas con la aplicación web original.
Como ya se introdujo en los apartados teóricos, JavaScript es el lenguaje de programación presente en todas las implementaciones de los navegadores web.
Javascript y Python parecen lenguajes bastante diferentes en su sintaxis, sin embargo comparten ciertas características como ser orientados a objetos y permitir la definición de clausuras [AtulVarma2009]. A partir de la versión 1.7 y 1.8, JavaScript incluye semántica funcional en los arreglos, generadores e iteradores, getters y setters, características que los acercan aún más [SteveLeeJs17Py09], tal como lo expresa Guyon Morée en la publicación titulada “Javascript Pythonico, es Python con llaves [¶]” [GuyonMoreePythonBraces09].
| [SteveLeeJs17Py09] | Steve Lee, Open Source Eduspaces, Mozilla’s Javascript 1.7 includes some Python goodness, http://eduspaces.net/stevelee/weblog/450964.html |
| [GuyonMoreePythonBraces09] | Guyon Morée, Pythonic Javascript, it’s Python with braces!, útlimo acceso Septiembre 2009, http://www.gumuz.nl/weblog/pythonic-javascript-its-python-braces/ |
| [AtulVarma2009] | Atul Varma, Python For Javascript Programmers, ultimo acceso Septiembre 2009, http://hg.toolness.com/python-for-js-programmers/raw-file/tip/PythonForJsProgrammers.html |
| [¶] | En Python existe un huevo de pascua relacionado con las características nuevas que se incluyen en el lenguaje, que se activa mediante la sentencia from __future__ import braces y produce la excepción SyntaxError: not a chance (Not a chance se traduce coloquialmente como “imposible, olvídalo!“). |
JavaScript no posee mecanismos de almacenamiento local. Si bien un navegador almacena muchos recursos de este tipo en su caché, lo hace con el objetivo de mejorar la performance y su permanencia en el equipo del cliente no está garantizada. Para lograr que una aplicación escrita en JavaScript pueda ejecutarse desconectada es necesario almacenar los recursos que componen la aplicación en el cliente mediante alguna técnica que no sea la caché.
Uno de los objetivos de Google Gears es el almacenamiento local y lo implementa mediante el módulo Local Server. El programador a través de su API genera repositorios de almacenamiento de recursos en línea (URLs) para proveerlos a través de un servidor web interno cuando el navegador no cuente con conexión.
Además de almacenamiento local, Gears provee una base de datos, satisfaciendo las necesidades para la creación de aplicaciones desconectadas que se plantearon anteriormente. Gears, al contrario que Silverlight, es de código abierto y en la especificación HTML 5 (actualmente en desarrollo) [W3CHTML5OffWebApp09] se incluyen varios de los componentes que éste provee [ScottLoganbillHTML5Gears09]. Es decir, varios de los componentes de Gears serán incluidos nativamente en los navegadores que adopten este estándar.
| [ScottLoganbillHTML5Gears09] | Scott Loganbill, How HTML 5 Is Already Changing the Web, último acceso Septiembre 2009, http://www.webmonkey.com/blog/How_HTML_5_Is_Already_Changing_the_Web |
| [W3CHTML5OffWebApp09] | World Wide Web Consortium, Apartado sobre Aplicaciones Web Desconectadas en el borrador sobre la especificación HTML5, último acceso Septiembre 2009 (Revision 1.2852), http://www.w3.org/TR/html5/desconectado.html#desconectado |
Por lo tanto, la combinación de JavaScript y Gears constituye la alternativa más promisoria para la implementación de aplicaciones desconectadas escritas en Django por lo que se utilizaron para llevar a cabo el desarrollo de la presente tesina.
Tras el análisis del proyecto Gears On Rails [GearsOnRails09] que persigue objetivos similares a los de esta tesina, pero basado en el framework Ruby On Rails, se descubrió un documento que plantea un esquema básico para implementar una versión de Django deconectado [DjangoOffline09], el documento divide al framework en sus componentes y analiza cuáles son necesarios implementar:
API de modelos: no
Los modelos se definen únicamente en el servidor y se utiliza la definición para el cliente. Los modelos, en su estructura, deben estar sincronizados.
Soporte para base de datos: sí
Sólo se requiere soporte para SQLite.
Managers de modelos: sí
Despacho de URLs: sí
Middlewares: no
Formularios: no
Basados en los componentes anteriores, también realiza un análisis de las partes transportables de manera automatizada al cliente:
- Modelos: sí
- URLs: sí
- Vistas: no
El enfoque del documento no contempla los recaudos de seguridad que se deben tener en cuenta al momento de transferir la base de datos al cliente, ni especifica cómo tratar el manejo de elementos activos (links, formularios, AJAX). Sin embargo, pone de manifiesto que no se necesita reimplementar la totalidad del framework en JavaScript ni la totalidad del proyecto, reduciendo el tiempo de desarrollo y logrando mayor cohesión entre las partes.
El primer componente del framework que se analizó fue el ORM, el cual debe ser migrado a JavaScript para conservar la semántica de acceso a datos de las aplicaciones escritas en Django. Se analizaron los ORM existentes en JavaScript que trabajan sobre Gears.
Uriel Katz implementó Gears ORM, que luego reimplementó bajo el nombre de JStORM [UrielKatzJStORM09], que permite definir la estructura de las tablas y realizar consultas. Por ejemplo para definir una tabla:
var Person = new JStORM.Model({
name:"Person",
fields:
{
firstName:new JStORM.Field({type:"String",maxLength:25}),
lastName:new JStORM.Field({type:"String",maxLength:25}),
},
connection:"default"
});
Presenta la particularidad de evaluación perezosa de las consultas. Cada consulta devuelve una instancia de Query, similar a los QuerySet persentes en Django. Desafortunadamente la definición del criterio de selección es de bajo nivel, como se observa en el siguiente ejemplo:
var katzFamily = Person.filter("lastName = ?","Katz");
katzFamily.each(function(person)
{
console.log(person.firstName);
});
El autor se centró en algunas características como conexiones con múltiples bases de datos, que aún no están presentes en Django, pero abandonó el proyecto sin implementar elementos esenciales como las claves foráneas y las relaciones muchos a muchos.
| [UrielKatzJStORM09] | Uriel Katz, Introducing JStORM, último acceso Septiembre 2009, http://www.urielkatz.com/archive/detail/introducing-jstorm/ |
JazzRecord es otro ORM analizado, que implementa la API ActiveRecord (la misma de ORM de Ruby On Rails). Se encuentra en un estado mucho más maduro que JStORM, incluyendo características como soporte para varias bases de datos, como Adobe AIR o Titanium PR1, validación de datos a nivel modelo, etc. Un ejemplo de utilización para definir una tabla es:
var Programmer = new JazzRecord.Model({
table: "programmer",
columns: {
name: "text",
income: "float"
},
validate: {
atUpdate: function() {
this.validatesIsString("name", "You must have a name!");
this.validatesIsFloat("income" "We will glady pay you a null salery!");
},
atSave: function() {
this.validatesIsString("name", "You must have a name!");
this.validatesIsFloat("income" "We will glady pay you a null salery!");
}
}
});
Las consultas se realizan mediante finders [NickCarterJazzModels09], que equivalen a los QuerySet. JazzRecord fue descartado como candidato a ORM del cliente en la presente tesina por la diferencia de filosofía con Django (convención (Rails) vs. definición (Django)).
Luego del análisis de los ORM existentes se decidió implementar el ORM de Django (API de base de datos) en JavaScript 1.7 como primera tarea para la desconexión de una aplicación. Contar con la misma API de acceso a datos en el cliente y en el servidor, permite mantener la coherencia de definiciones de datos en alto nivel y simplificar la tarea de mantenimiento. Los modelos definidos en el servidor, dentro de los proyectos basados en Django, pueden ser analizados mediante técnicas de metaprogramación disponibles en Python. A partir de esta introspección se puede generar el código JavaScript para la definición del modelo en el cliente de manera automática.
Otro elemento importante para lograr una aplicación desconectada, es el manejo de los elementos activos (links, formularios y AJAX). DOM permite capturar los eventos click, que se generan cuando se activa un enlace, y submit, cuando se envía un formulario. Para AJAX se puede realizar un enmascaramiento del elemento XMLHttpRequest.
La selección de elementos en DOM es verborrágica (cuando los elementos carecen de id) y el manejo de eventos es primitivo (por ejemplo, no implementa el patrón Listener de manera consistente), por lo que se decidió utilizar la librería Prototype, que además de simplificar el manejo de eventos y presentar técnicas avanzadas de selección (de elementos), implementa un sistema de clases.
Al comenzar a implementar el ORM de Django sobre el sistema de clases de Prototype, se observó que había ciertas diferencias de sintaxis que no eran producto de las diferencias entre Python y JavaScript. La construcción de clases en Python se realiza en dos fases (creación de instancia __new__ e inicialización __init__), mecanismo que en Prototype no existe (Prototype toma la orientación a objetos de Ruby). Esta inicialización en dos fases se utiliza en el ORM y formularios de Django, por lo que se decidió como primera medida modificar Prototype para que admitiera la orientación a objetos de Python.
Otro elemento ausente en Prototype es un sistema de módulos y paquetes [#], necesario para implementar muchos elementos de Django. Por ello se decidió en una instancia posterior descartar Prototype e implementar las característica necesarias en una nueva librería, agregando además la mayoría de las funciones integradas del intérprete de Python [♠], como isinstance, issubclass, map, int, bool, etc.
| [#] | También ausente en JavaScript. |
| [♠] | Conocidos como builtins. |
Esta nueva librería se bautizó como Protopy y su objetivo es facilitar la migración de código Python a JavaScript (en particular en esta tesina, la migración del framework y proyectos Django).
En la siguiente figura se muestra la estructura de Protopy y su interacción con JavaScript, DOM y el proyecto Django desconectado:
Protopy, librería desarrollada en JavaScript para soporte de Python sobre JavaScript 1.7
En los siguientes capítulos se detalla el desarrollo de esta librería y la migración de Django sobre Protopy.
| [GearsOnRails09] | Gears On Rails, GoogleCode, ultimo acceso Septiembre 2009, http://code.google.com/p/gearsonrails/ |
| [DjangoOffline09] | Django desconectado, Google Code, ultimo acceso Septiembre 2009, http://code.google.com/p/django-offline/wiki/Goals |
| [NickCarterJazzModels09] | Nick Carter, Model - JazzRecord JavaScript ORM Documentation último acceso Septiembre 2009, http://www.jazzrecord.org/docs/model#finders |