TheBronx/shurscript

Implementar sincronización de preferencias

Closed this issue · 10 comments

Aun no sé si será necesario modificar el "core" o bastará con un componente que sobreescriba las funciones SHURSCRIPT.GM.setValue y SHURSCRIPT.GM.getValue para enviar y recibir las preferencias del servidor.

Otra duda, ¿es necesario seguir guardando las preferencias en local?

Estoy en ello.
De momento problemillas que voy viendo:

1. en el core.js se usaba una abreviatura GM en lugar de SHURSCRIPT.GreaseMonkey. Parece inocente pero esto hacía que desde el componente sync.js sobreescribir SHURSCRIPT.GreaseMonkey no tuviese efecto, ya que a todos los módulos se les asignaban las funciones a través de GM, que apuntaba al SHURSCRIPT.GreaseMonkey original, sin sobreescribir.

2. las llamadas ajax con jQuery no puedo hacerlas síncronas (lo dice la documentación de jQuery, no deja async=false para llamadas Cross Domain). Esto significa que cuando un módulo hace getValue no puedo responderle a tiempo. Por tanto he optado por hacer que el componente sync.js mantenga una copia de las preferencias que hay en el servidor, así cuando un módulo pida un valor, poder responderle en el acto. Y cuando haga setValue, modificar la copia local y mandarla al server sin preocuparnos de que la comunicación sea asíncrona.

3. estoy viendo cómo resolver otro problemilla, obtener la "apiKey" a tiempo. Como dijimos el server nos asigna una aleatoria cuando lo pedimos, y la guardamos en el nombre de una carpeta en las suscripciones de forocoches. El problema es, hacer una petición al server, o a las suscripciones lleva tiempo, y no podemos andar esperando porque sin la API KEY no hay preferencias, y sin preferencias el resto de componentes y módulos van a cascar si cargan antes de que obtengamos la respuesta/s. ¿Cómo paramos la carga de componentes y módulos? ¿Se puede hacer de otra forma?

Sobre el 3, la key se puede generar en el cliente y luego mandarla al servidor junto con las preferencias actuales (Si las podemos mantener al final).

Podemos generarla en el cliente, pero, ¿dónde la guardamos? Si la guardamos en las suscripciones tenemos el mismo problema, el tiempo que tardamos en hacer esa lectura puede ser suficiente como para que algún componente/módulo no pueda cargar.
A parte, generarla en el cliente no garantiza que sea única, por eso la genera el server (si la key es larga la probabilidad de colisión es mínima pero es otro detalle).

Otra opción es usar un MD5 del id de usuario o algo así, de forma que no haya que guardar nada ni pedirle nada al servidor. Por contra sería menos "seguro" que una clave aleatoria.

De todas formas no sé hasta qué punto afectará este problema al script, solo se me ha ocurrido, aun no me he tropezado con él xD Lo mismo luego resulta que el server responde antes de que ningún módulo cargue...
O quizá merezca la pena implementar algún sistema de "bloqueo", de forma que hasta que el sync.js no reciba la key (sea de donde sea que la reciba) el core no inicie ningún otro componente ni módulo. ¿Sería sencillo?

La generas, la guardas en las suscripciones, en el server, etc. pero mientras vas trabajando con la que acabas de generar.

No creo que tengamos problemas de colisión xD

Si la generas a partir de un hash, cualquiera, sabiendo el ID del otro usuario, podría acceder a sus preferencias. O me equivoco? Me refiero, que pueden modificar el código para que le pida las preferencias de este usuario en vez de las del suyo en 2 minutos. Si la generamos aleatoria (o a partir de una semilla variable), pues sí, podrían ir probando por fuerza bruta hasta encontrar unas preferencias en el server, pero aún así, no podrían saber a quien pertenecen. Dicha clave se almacena en sus suscripciones, por lo tanto sin su usuario y contraseña de Forocoches nadie podría acceder a ella.

Sistema de bloqueo se podría, claro. Supongo que en el callback de la llamada se inicia la carga de los módulos y listo. Hablo sin tener el código delante, pero no creo que hubiera ningún problema.

Perdón por no responder antes, se me cancelaron las vacaciones xD

En cualquiera de los dos casos, sea una clave segura o insegura, habrá que utilizarla para pedir las preferencias al server, y eso sí que sí será asíncrono. Si algún módulo pide una preferencia mientras el server responde ¿qué le damos?

Podemos hacer una cadena de carga, de forma que los componentes carguen uno tras otro. Cada vez que un componente esté listo, notifica al core para que inicie el siguiente. Cuando no queden componentes, inicia la carga de módulos.
Habría que modificar el sistema de arranque del core para que funcione a través de eventos. Algo así:

cargarComponentes() {
    componente = componentes.getNext();
    if (componente!=undefined) componente.cargar( callback=cargarComponentes() );
    else cargarModulos();
}

Solo necesitamos que cargue el componente de las preferencias primero, no? El resto junto con los módulos pueden cargarse igual que ahora pero al acabar de cargarse las preferencias. Lo digo para no retrasar más de lo necesario la carga de módulos. Añadir un callback a la petición al server y al volver lanzar el loadModules.

Sí, pero los componentes que no hagan nada asíncrono simplemente harán "callback()" al final. Es decir, que esos componentes cargarán exactamente igual que cargan ahora, uno tras otro, sin retrasos... no?
Es decir, sería como ahora, igual de rápido/lento salvo en el caso del sync.js que nos ocupa.

Está claro, pero por eso mismo no lo veo necesario, pero bueno, nos vendrá bien si más adelante añadimos más "componentes prioritarios"

Hazlo como quieras, tu mandas ;)

On Fri, Dec 13, 2013 at 5:07 PM, TheBronx notifications@github.com
wrote:

Sí, pero los componentes que no hagan nada asíncrono simplemente harán "callback()" al final. Es decir, que esos componentes cargarán exactamente igual que cargan ahora, uno tras otro, sin retrasos... no?

Es decir, sería como ahora, igual de rápido/lento salvo en el caso del sync.js que nos ocupa.

Reply to this email directly or view it on GitHub:
#15 (comment)

Acabo de hacer una prueba, he cambiado el core.js ligeramente:

  • cada vez que un módulo llama a SHURSCRIPT.core.createComponent el core guarda el id en un array de componentes (SHURSCRIPT.core.components). Esto nos permite luego ir cargándolos por orden.
  • antes cada componente se ejecutaba por su cuenta. Unos no tenían "función de entrada", y otros como preferences.js tenían un "start()" que era llamado desde el core (a pelo). Ahora los componentes pueden implementar un método load( callback ) que el core se encarga de llamar, en orden y con bloqueo, o bien seguir como antes, yendo a su bola...

He cambiado el start() de preferences.js por un load(callback) con la correspondiente llamada callback() al terminar.

He modificado sync.js para que lea y guarde en las suscripciones la API key generada por el server.
También lee y guarda preferencias en el server con esa API key.
Está muy muy verde, pero se puede probar.

Ayer necesité hacer otro cambio en el core: 12172bd
Básicamente hacer que setValue acepte además de una clave y su valor, un tercer parámetro: un callback que se ejecute una vez la preferencia ha sido guardada correctamente.
Evidentemente esto no tiene mucho sentido en el caso de guardado en local, pero es imprescindible cuando se usa el guardado asíncrono en la nube.
Lo que he hecho ha sido darle un poco de información sobre el tema al core, para que se encargue de ejecutar el callback él mismo cuando sync.js no esté funcionando (no haya reemplazado las funciones GreaseMonkey con las Cloud). El core no tendría por qué saber este detalle, pero puesto que es él quien usa GreaseMonkey.setValue pues tendrá que hacerlo.