Mondi su mondi, sistemi di sistemi.

Usare CloudKit con SproutCore

Wednesday, April 28th, 2010

Dopo aver introdotto CloudKit, vediamo come usarlo con SproutCore, configurandolo come backend per il tutorial al posto dei più clas­sici PHP, Rails, Django e compagnia bella.

Il tutorial consiste nel creare una todo list, i cui record stanno sotto <app>/tasks. Dato che SproutCore e CloudKit seguono a grandi linee gli stessi principi REST, i punti su cui intervenire non sono molti: più che altro si tratta di fare in modo che vengano usati correttamente gli etag.

Fetch dei record

Se chiediamo /tasks CloudKit non restituisce i dati dei record ma solo i loro link e alcuni dati descrittivi come il numero dei record e l’offset1. In questo tutorial, però, faremo in modo che restituisca tutti i dati in un colpo solo. Quindi, al posto di girare semplicemente la richiesta, faremo un GET verso /tasks/_resolved (in gras­setto le parti modificate rispetto al tutorial):


fetch: function(store, query) {

  if (query === Todos.TASKS_QUERY) {
    SC.Request.getUrl('/tasks/_resolved').json()
      .notify(this, 'didFetchTasks', store, query)
      .send();
    return YES;
  }

  return NO;
},

ottenendo una risposta simile a questa:


{
    "documents": [{
        "document": "{
             \"campo\": \"valore\",
             ...
        }",
        "etag": "3b990fe0-2913-012d-e490-0019e33a5348",
        "uri": "/tasks/3",
        "last_modified": "Tue, 13 Apr 2010 10:14:13 GMT",
    },
    ...],
   "offset": 0,
   "total": 3
}

La nostra data source sarà semplicemente quello di spacchettare documents che, ragioni ignote, tratta ogni documento come una stringa e non un oggetto JSON, aggiungendo contemporaneamente l’etag che verrà usato nelle operazioni di modifica:


didFetchTasks: function(response, store, query) {
    var records = [];
    response.get('body').documents.forEach(function(item, index, documents) {
    var record = SC.json.decode(item.document);
    records[index] = {
        "guid": item.uri,
    	"description": record.description,
    	"isDone": record.isDone,
	"etag": item.etag
    }
}, this);
if (SC.ok(response)) {
    store.loadRecords(Todos.Task, records);
    store.dataSourceDidFetchQuery(query);
  } else store.dataSourceDidErrorQuery(query, response);
},

Inserimento dei record

Nella creazione dei record SproutCore esegue subito il POST con i dati, lasciando a un delegato il compito di aggiornare il dato store. Anche in questo caso dovremo aggiungere l’etag:

…
var record = store.readDataHash(storeKey);
record.etag = response.get('body').etag;
store.dataSourceDidComplete(storeKey, record, url);
…

Aggiornamento dei record

Adesso pos­siamo sfruttare gli etag salvati prima per impostare il valore dell’header If-Match:

updateRecord: function(store, storeKey) {
  if (SC.kindOf(store.recordTypeFor(storeKey), Todos.Task)) {
    var record = store.readDataHash(storeKey);
    SC.Request.putUrl(store.idFor(storeKey)).json()
      .header('If-Match', record.etag)
      .notify(this, this.didUpdateTask, store, storeKey)
      .send(record);
    return YES;

  } else return NO ;
},

Lo stesso vale per la cancellazione:

destroyRecord: function(store, storeKey) {
  if (SC.kindOf(store.recordTypeFor(storeKey), Todos.Task)) {
    SC.Request.deleteUrl(store.idFor(storeKey)).json()
      .header('If-Match', store.readDataHash(storeKey).etag)
      .notify(this, this.didDestroyTask, store, storeKey)
      .send();
    return YES;

  } else return NO;
},

Tutto qui?

Beh, sì! Abbiamo solo dato un’occhiata al tutorial e sicuramente per cose più realistiche CloudKit è insufficiente ma, come ricordavo l’altra volta, vedo il suo utilizzo nell’ambito della prototipazione dove, da un lato, per la GUI, abbiamo SproutCore; dall’altro, per il backend, abbiamo CloudKit.

Rispetto al tutorial siamo dovuti intervenire sulla definizione della data source, mentre nell’originale la stessa implementazione va bene con tutti i backend ma non ci vuole molto per sostituirla in base alle esigenze2.

  1. CloudKit non supporta delle query arbitrarie, quindi l’unico modo per filtrare i record è quello di usare l’offset
  2. A mio parere, fra l’altro, gli etag dovrebbero essere gestiti direttamente da SproutCore

Cloudkit

Wednesday, April 7th, 2010

L’uso di Ajax rende desiderabile, se non neces­sario, il poter mettere in piedi un server REST in tempi rapidi.

In questo post mi sono imbattuto in CloudKit, un’applicazione in Ruby che permettere di metterne in piedi uno in un batter d’occhio, agganciandoci pure un minimo di backend, in questo caso Tokyo Cabinet.

L’installazione è semplicis­sima e l’unica cosa che ho cambiato rispetto alla ricetta originale è che ho usato MacPorts per Tokyo Cabinet al posto di compilarlo da zero.

Una volta che Cloudkit è attivo, pos­siamo subito cominciare a progettare l’API che il nostro servizio REST esporrà verso l’esterno, magari con un approccio TDD usando una delle innumerevoli librerie in JavaScript per i test1.

Quando poi le cose saranno più stabili potremo sostituire Cloudkit con un backend più adatto, senza toccare una riga nella parte Ajax.

Strumenti come Cloudkit sono vitali per raccordare le diverse fasi di sviluppo perché, anche se abbiamo familiarità con backend più strutturati e siamo in grado di attivarli con facilità, spesso non sono sufficientemente agili per aiutarci nelle fasi fluide dello sviluppo, dove le cose cambiano rapidis­simamente ed è inutile essere troppo rigorosi.

OR Mappers: il caching

Wednesday, April 1st, 2009

Fra i vantaggi che sono solitamente ascritti agli ORM c’è il caching. Si sfrutta la pos­sibilità di creare in memoria una rappresentazione persistente di righe nel database, sotto forma di un grafo di oggetti, per evitare di dover andare a ripescare dal database medesimo i dati ogni volta che serve.

È un’idea sensata, ovviamente, ma credo che abbia un uso più limitato di quanto si pensi, soprattutto in tempi come questi fatti di architetture distribuite, dove la cache può diventare un’arma a doppio taglio.

Ad esempio, quando seguivo con più attenzione la mailing list di WebObjects c’era spesso una domanda ricorrente “Ho n istanze della stessa applicazione, collegate allo stesso database; come faccio a ‘far vedere’ le modifiche dell’istanza A all’istanza B?”

Per risolvere questo problema nel tempo sono state sviluppate diverse soluzioni, l’ultima di cui sono a conoscenza è composta da er.extensions.remoteSynchronizer e er.jgroups (che usa, appunto, JGroups).

La comples­sità aumenta considerevolmente e abbiamo comunque risolto solo una parte del problema, visto che se un processo aggiorna il database al di fuori del contesto delle applicazioni A e B le loro cache risultano comunque invalidate.

Inoltre, il problema potrebbe essere considerato risolto solo se ci limitiamo a sistemi tradizionali client/server, ma su web cambia tutto: è un’architettura fondamentalmente stateless, dove le cache intermedie non vanno molto d’accordo con questa caratteristica (un’altra manifestazione dell’end-to-end argument).

Se aggiungiamo questo para­metro al calcolo dei costi/benefici ci accorgiamo che il vantaggio del caching risulta molto più ambiguo.

Guardian API

Wednesday, March 11th, 2009

Anche il Guardian ha deciso di offrire un servizio di API, dopo che il New York Times ha fatto la stessa cosa. Inoltre ha reso disponibili una serie di “dati crudi” per chi vuole farne un mash up.

Forse non avremo più i giornali di carta (io li leggo ancora) ma c’è la ragionevole speranza che le testate storiche riescano a trovare il modo di non buttare al vento il patrimonio accumulato.

So che qualche blogger mi darà del pas­satista ma rimango convinto che il ruolo del giornalista tradizionale, e del lavoro redazionale che ci sta intorno, sia qualcosa non sostituibile con i blog, almeno non completamente. 

Links:

OHM

Monday, March 12th, 2007

OHM è una libreria in Python per pre­sentare oggetti attraverso delle API HTTP in stile REST. Ad es. dato un oggetto article, localizzato con l’URL /article/1, sotto /article/1/last_modified troveremo la data dell’ultima modifica.

Sto scrivendo qualcosa di simile per un progetto in WebObjects attualmente in corso.

Disambiguare le URL

Sunday, February 4th, 2007

Simon Willison esamina alcuni svantaggi nell’usare più URL per una stessa risorsa, ad es. http://developer.yahoo.com/python/ e http://developer.yahoo.com/python/index.html, oppure del.icio.us/giorgio_v e del.icio.us/giorgio_v/.

I problemi che queste URL pos­sono generare sono:

  • Le cache diventano meno efficaci
  • L’aggregazione dei link è meno efficace

Altri link utili: