Mondi su mondi, sistemi di sistemi.

Usare CloudKit con SproutCore

Wednesday, April 28th, 2010

Dopo aver intro­dotto CloudKit, vediamo come usarlo con SproutCore, con­fi­gu­ran­dolo come bac­kend per il tuto­rial al posto dei più clas­sici PHP, Rails, Django e com­pa­gnia bella.

Il tuto­rial con­si­ste nel creare una todo list, i cui record stanno sotto <app>/tasks. Dato che SproutCore e CloudKit seguono a grandi linee gli stessi prin­cipi REST, i punti su cui inter­ve­nire non sono molti: più che altro si tratta di fare in modo che ven­gano usati cor­ret­ta­mente gli etag.

Fetch dei record

Se chie­diamo /tasks CloudKit non resti­tui­sce i dati dei record ma solo i loro link e alcuni dati descrit­tivi come il numero dei record e l’offset1. In que­sto tuto­rial, però, faremo in modo che resti­tui­sca tutti i dati in un colpo solo. Quindi, al posto di girare sem­pli­ce­mente la richie­sta, faremo un GET verso /tasks/_resolved (in gras­setto le parti modi­fi­cate 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;
},

otte­nendo una rispo­sta 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à sem­pli­ce­mente quello di spac­chet­tare documents che, ragioni ignote, tratta ogni docu­mento come una stringa e non un oggetto JSON, aggiun­gendo con­tem­po­ra­nea­mente l’etag che verrà usato nelle ope­ra­zioni 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 crea­zione dei record SproutCore ese­gue subito il POST con i dati, lasciando a un dele­gato il com­pito di aggior­nare il dato store. Anche in que­sto caso dovremo aggiun­gere l’etag:

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

Aggiornamento dei record

Adesso pos­siamo sfrut­tare gli etag sal­vati prima per impo­stare 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 can­cel­la­zione:

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 tuto­rial e sicu­ra­mente per cose più rea­li­sti­che CloudKit è insuf­fi­ciente ma, come ricor­davo l’altra volta, vedo il suo uti­lizzo nell’ambito della pro­to­ti­pa­zione dove, da un lato, per la GUI, abbiamo SproutCore; dall’altro, per il bac­kend, abbiamo CloudKit.

Rispetto al tuto­rial siamo dovuti inter­ve­nire sulla defi­ni­zione della data source, men­tre nell’originale la stessa imple­men­ta­zione va bene con tutti i bac­kend ma non ci vuole molto per sosti­tuirla in base alle esi­genze2.

  1. CloudKit non sup­porta delle query arbi­tra­rie, quindi l’unico modo per fil­trare i record è quello di usare l’offset
  2. A mio parere, fra l’altro, gli etag dovreb­bero essere gestiti diret­ta­mente da SproutCore

Cloudkit

Wednesday, April 7th, 2010

L’uso di Ajax rende desi­de­ra­bile, se non neces­sa­rio, il poter met­tere in piedi un ser­ver REST in tempi rapidi.

In que­sto post mi sono imbat­tuto in CloudKit, un’applicazione in Ruby che per­met­tere di met­terne in piedi uno in un bat­ter d’occhio, aggan­cian­doci pure un minimo di bac­kend, in que­sto caso Tokyo Cabinet.

L’installazione è sem­pli­cis­sima e l’unica cosa che ho cam­biato rispetto alla ricetta ori­gi­nale è che ho usato MacPorts per Tokyo Cabinet al posto di com­pi­larlo da zero.

Una volta che Cloudkit è attivo, pos­siamo subito comin­ciare a pro­get­tare l’API che il nostro ser­vi­zio REST esporrà verso l’esterno, magari con un approc­cio TDD usando una delle innu­me­re­voli libre­rie in JavaScript per i test1.

Quando poi le cose saranno più sta­bili potremo sosti­tuire Cloudkit con un bac­kend più adatto, senza toc­care una riga nella parte Ajax.

Strumenti come Cloudkit sono vitali per rac­cor­dare le diverse fasi di svi­luppo per­ché, anche se abbiamo fami­lia­rità con bac­kend più strut­tu­rati e siamo in grado di atti­varli con faci­lità, spesso non sono suf­fi­cien­te­mente agili per aiu­tarci nelle fasi fluide dello svi­luppo, dove le cose cam­biano rapi­dis­si­ma­mente ed è inu­tile essere troppo rigorosi.

OR Mappers: il caching

Wednesday, April 1st, 2009

Fra i van­taggi che sono soli­ta­mente ascritti agli ORM c’è il caching. Si sfrutta la pos­si­bi­lità di creare in memo­ria una rap­pre­sen­ta­zione per­si­stente di righe nel data­base, sotto forma di un grafo di oggetti, per evi­tare di dover andare a ripe­scare dal data­base mede­simo i dati ogni volta che serve.

È un’idea sen­sata, ovvia­mente, ma credo che abbia un uso più limi­tato di quanto si pensi, soprat­tutto in tempi come que­sti fatti di archi­tet­ture distri­buite, dove la cache può diven­tare un’arma a dop­pio taglio.

Ad esem­pio, quando seguivo con più atten­zione la mai­ling list di WebObjects c’era spesso una domanda ricor­rente “Ho n istanze della stessa appli­ca­zione, col­le­gate allo stesso data­base; come fac­cio a ‘far vedere’ le modi­fi­che dell’istanza A all’istanza B?”

Per risol­vere que­sto pro­blema nel tempo sono state svi­lup­pate diverse solu­zioni, l’ultima di cui sono a cono­scenza è com­po­sta da er.extensions.remoteSynchronizer e er.jgroups (che usa, appunto, JGroups).

La com­ples­sità aumenta con­si­de­re­vol­mente e abbiamo comun­que risolto solo una parte del pro­blema, visto che se un pro­cesso aggiorna il data­base al di fuori del con­te­sto delle appli­ca­zioni A e B le loro cache risul­tano comun­que invalidate.

Inoltre, il pro­blema potrebbe essere con­si­de­rato risolto solo se ci limi­tiamo a sistemi tra­di­zio­nali client/server, ma su web cam­bia tutto: è un’architettura fon­da­men­tal­mente sta­te­less, dove le cache inter­me­die non vanno molto d’accordo con que­sta carat­te­ri­stica (un’altra mani­fe­sta­zione dell’end-to-end argu­ment).

Se aggiun­giamo que­sto para­me­tro al cal­colo dei costi/benefici ci accor­giamo che il van­tag­gio del caching risulta molto più ambiguo.

Guardian API

Wednesday, March 11th, 2009

Anche il Guardian ha deciso di offrire un ser­vi­zio di API, dopo che il New York Times ha fatto la stessa cosa. Inoltre ha reso dispo­ni­bili una serie di “dati crudi” per chi vuole farne un mash up.

Forse non avremo più i gior­nali di carta (io li leggo ancora) ma c’è la ragio­ne­vole spe­ranza che le testate sto­ri­che rie­scano a tro­vare il modo di non but­tare al vento il patri­mo­nio accumulato.

So che qual­che blog­ger mi darà del pas­sa­ti­sta ma rimango con­vinto che il ruolo del gior­na­li­sta tra­di­zio­nale, e del lavoro reda­zio­nale che ci sta intorno, sia qual­cosa non sosti­tui­bile con i blog, almeno non completamente. 

Links:

OHM

Monday, March 12th, 2007

OHM è una libre­ria in Python per pre­sen­tare oggetti attra­verso delle API HTTP in stile REST. Ad es. dato un oggetto article, loca­liz­zato con l’URL /article/1, sotto /article/1/last_modified tro­ve­remo la data dell’ultima modifica.

Sto scri­vendo qual­cosa di simile per un pro­getto in WebObjects attual­mente in corso.

Disambiguare le URL

Sunday, February 4th, 2007

Simon Willison esa­mina alcuni svan­taggi 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 pro­blemi che que­ste URL pos­sono gene­rare sono:

  • Le cache diven­tano meno efficaci
  • L’aggregazione dei link è meno efficace

Altri link utili: