Dion Almaer e Pamela Fox, Google
Giugno 2007
Nota dell'editor:l'API Google Gears non è più disponibile.
- Introduzione
- Informazioni sull'app
- Utilizzo dei feed dell'API Google Base Data
- Aggiungere Google Gears all'app
- Esecuzione del debug dell'app offline
- Conclusione
Introduzione
Combinando Google Base con Google Gears, mostriamo come creare un'applicazione che può essere utilizzata offline. Dopo aver letto questo articolo, avrai una maggiore familiarità con l'API Google Base e saprai come utilizzare Google Gears per archiviare e accedere alle preferenze e ai dati degli utenti.
Informazioni sull'app
Per comprendere questa app, devi prima familiarizzare con Google Base, che è fondamentalmente un grande database di elementi che coprono varie categorie come prodotti, recensioni, ricette, eventi e altro ancora.
Ogni elemento è annotato con un titolo, una descrizione, un link alla fonte originale dei dati (se esistente) e attributi aggiuntivi che variano in base al tipo di categoria. Google Base sfrutta il fatto che gli articoli della stessa categoria condividono un insieme comune di attributi. Ad esempio, tutte le ricette hanno ingredienti. Gli elementi di Google Base vengono visualizzati occasionalmente anche nei risultati di ricerca della Ricerca Google o della ricerca di prodotti Google.
La nostra app demo, Base with Gears, ti consente di archiviare e visualizzare le ricerche comuni che potresti eseguire su Google Base, ad esempio trovare ricette con "cioccolato" (buonissime) o annunci personali con "passeggiate sulla spiaggia" (romantiche). Puoi considerarlo un "lettore di Google Base" che ti consente di iscriverti alle ricerche e visualizzare i risultati aggiornati quando torni nell'app o quando l'app cerca feed aggiornati ogni 15 minuti.
Gli sviluppatori che vogliono estendere l'app potrebbero aggiungere altre funzionalità, ad esempio avvisare visivamente l'utente quando i risultati di ricerca contengono nuovi risultati, consentire all'utente di aggiungere ai preferiti (contrassegnare con una stella) gli elementi preferiti (offline e online) e consentire all'utente di eseguire ricerche di attributi specifici per categoria come Google Base.
Utilizzo dei feed dell'API Google Base Data
È possibile eseguire query su Google Base in modo programmatico con l'API Google Base Data, conforme al framework dell'API Google Data. Il protocollo Google Data API fornisce un protocollo semplice per la lettura e la scrittura sul web ed è utilizzato da molti prodotti Google: Picasa, Fogli di lavoro, Blogger, Calendar, Notebook e altri ancora.
Il formato Google Data API si basa su XML e sul protocollo di pubblicazione Atom, pertanto la maggior parte delle interazioni di lettura/scrittura è in XML.
Un esempio di feed Google Base basato sull'API Google Data è:
http://www.google.com/base/feeds/snippets/-/products?bq=digital+camera
Il tipo di feed snippets
ci fornisce il feed di elementi disponibile pubblicamente. -/products
ci consente di limitare il feed alla categoria di prodotti. Il parametro bq=
ci consente di limitare ulteriormente il feed ai soli risultati contenenti la parola chiave "fotocamera digitale". Se visualizzi questo feed nel browser, vedrai XML contenente nodi <entry>
con risultati corrispondenti. Ogni voce contiene gli elementi tipici di autore, titolo, contenuti e link, ma anche attributi aggiuntivi specifici per categoria (ad esempio "prezzo" per gli articoli della categoria Prodotti).
A causa della limitazione tra domini di XMLHttpRequest nel browser, non ci è consentito leggere direttamente un feed XML da www.google.com nel nostro codice JavaScript. Potremmo configurare un proxy lato server per leggere l'XML e restituirlo in una posizione sullo stesso dominio della nostra app, ma vorremmo evitare del tutto la programmazione lato server. Fortunatamente, esiste un'alternativa.
Come le altre API Google Data, l'API Google Base Data ha un'opzione di output JSON, oltre all'XML standard. L'output del feed che abbiamo visto in precedenza in formato JSON si troverà a questo URL:
http://www.google.com/base/feeds/snippets/-/products?bq=digital+camera&alt=json
JSON è un formato di interscambio leggero che consente l'annidamento gerarchico e vari tipi di dati. Ancora più importante, l'output JSON è codice JavaScript nativo e può quindi essere caricato nella pagina web semplicemente facendo riferimento a un tag script, aggirando la limitazione tra domini.
Le API Google Data ti consentono anche di specificare un output "json-in-script" con una funzione di callback da eseguire una volta caricato il JSON. In questo modo, l'output JSON è ancora più facile da utilizzare, in quanto possiamo aggiungere dinamicamente i tag script alla pagina e specificare diverse funzioni di callback per ciascuno.
Pertanto, per caricare dinamicamente un feed JSON dell'API Base nella pagina, potremmo utilizzare la seguente funzione che crea un tag script con l'URL del feed (a cui vengono aggiunti i valori alt
callback
) e lo aggiunge alla pagina.
function getJSON() { var script = document.createElement('script'); var url = "http://www.google.com/base/feeds/snippets/-/products?bq=digital+camera"; script.setAttribute('src', url + "&alt=json-in-script&callback=listResults"); script.setAttribute('type', 'text/JavaScript'); document.documentElement.firstChild.appendChild(script); }
Pertanto, la nostra funzione di callback listResults
ora può scorrere il JSON passato come unico parametro e visualizzare le informazioni su ogni voce trovata in un elenco puntato.
function listTasks(root) { var feed = root.feed; var html = ['']; html.push('<ul>'); for (var i = 0; i < feed.entry.length; ++i) { var entry = feed.entry[i]; var title = entry.title.$t; var content = entry.content.$t; html.push('<li>', title, ' (', content, ')</li>'); } html.push('</ul>'); document.getElementById("agenda").innerHTML = html.join(""); }
Aggiunta di Google Gears
Ora che abbiamo un'applicazione in grado di comunicare con Google Base tramite l'API Google Data, vogliamo consentire a questa applicazione di essere eseguita offline. È qui che entra in gioco Google Gears.
Esistono varie scelte di architettura quando si tratta di scrivere un'applicazione che può essere utilizzata offline. Ti porrai domande su come dovrebbe funzionare l'applicazione online rispetto a offline (ad es. funziona esattamente allo stesso modo? Alcune funzionalità sono disattivate, ad esempio la ricerca? Come gestirai la sincronizzazione?)
Nel nostro caso, volevamo assicurarci che gli utenti dei browser senza Gears potessero comunque utilizzare l'app, offrendo al contempo agli utenti che hanno il plug-in i vantaggi dell'utilizzo offline e di un'interfaccia utente più reattiva.
La nostra architettura è la seguente:
- Abbiamo un oggetto JavaScript che si occupa di memorizzare le tue query di ricerca e di restituire i risultati di queste query.
- Se hai installato Google Gears, ottieni una versione di Gears che memorizza tutto nel database locale.
- Se non hai installato Google Gears, ottieni una versione che memorizza le query in un cookie e non memorizza affatto i risultati completi (da cui la reattività leggermente più lenta), in quanto i risultati sono troppo grandi per essere memorizzati in un cookie.
if (online) {}
in tutto il negozio. L'applicazione esegue un controllo Gears e poi viene utilizzato l'adattatore corretto.
Utilizzo di un database locale di Gears
Uno dei componenti di Gears è il database SQLite locale incorporato e pronto per l'uso. Esiste una semplice API di database che ti sembrerà familiare se in precedenza hai utilizzato API per database lato server come MySQL o Oracle.
I passaggi per utilizzare un database locale sono piuttosto semplici:
- Inizializza gli oggetti Google Gears
- Recupera un oggetto factory di database e apri un database
- Inizia a eseguire richieste SQL
Esaminiamoli rapidamente.
Inizializzare gli oggetti Google Gears
La tua applicazione deve leggere i contenuti di /gears/samples/gears_init.js
direttamente o incollando il codice nel tuo file JavaScript. Una volta che hai <script src="..../gears_init.js" type="text/JavaScript"></script>
, hai accesso allo spazio dei nomi google.gears.
Ottieni un oggetto Database Factory e apri un database
var db = google.gears.factory.create('beta.database', '1.0'); db.open('testdb');
Questa singola chiamata ti fornirà un oggetto di database che ti consentirà di aprire uno schema di database. Quando apri i database, questi vengono limitati dalle stesse regole delle norme relative all'origine, quindi il tuo"testdb" non entrerà in conflitto con il mio "testdb".
Inizia a eseguire le richieste SQL
Ora possiamo inviare richieste SQL al database. Quando inviamo richieste "select", riceviamo un insieme di risultati che possiamo scorrere per trovare i dati desiderati:
var rs = db.execute('select * from foo where name = ?', [ name ]);
Puoi operare sul set di risultati restituito con i seguenti metodi:
boolean | isValidRow() |
void | next() |
void | close() |
int | fieldCount() |
string | fieldName(int fieldIndex) |
variant | field(int fieldIndex) |
variant | fieldByName(string fieldname) |
Per ulteriori dettagli, consulta la documentazione dell'API del modulo di database. (Nota del redattore: l'API Google Gears non è più disponibile).
Utilizzo di GearsDB per incapsulare l'API di basso livello
Volevamo incapsulare e rendere più pratiche alcune delle attività comuni del database. Ad esempio,
- Volevamo un modo semplice per registrare l'SQL generato durante il debug dell'applicazione.
- Volevamo gestire le eccezioni in un unico posto, anziché doverle
try{}catch(){}
ovunque. - Volevamo gestire gli oggetti JavaScript anziché i set di risultati durante la lettura o la scrittura dei dati.
Per gestire questi problemi in modo generico, abbiamo creato GearsDB, una libreria open source che esegue il wrapping dell'oggetto Database. Ora mostreremo come utilizzare GearsDB.
Configurazione iniziale
Nel nostro codice window.onload, dobbiamo assicurarci che le tabelle del database su cui facciamo affidamento siano presenti. Se l'utente ha installato Gears quando viene eseguito il seguente codice, creerà un oggetto GearsBaseContent
:
content = hasGears() ? new GearsBaseContent() : new CookieBaseContent();
A questo punto, apriamo il database e creiamo le tabelle se non esistono già:
db = new GearsDB('gears-base'); // db is defined as a global for reuse later! if (db) { db.run('create table if not exists BaseQueries' + ' (Phrase varchar(255), Itemtype varchar(100))'); db.run('create table if not exists BaseFeeds' + ' (id varchar(255), JSON text)'); }
A questo punto, siamo sicuri di avere una tabella per archiviare le query e i feed. Il codice new GearsDB(name)
racchiuderà l'apertura di un database con il nome specificato. Il metodo run
esegue il wrapping del metodo di livello inferiore execute
, ma gestisce anche l'output di debug in una console e l'intercettazione delle eccezioni.
Aggiungere un termine di ricerca
Quando esegui l'app per la prima volta, non avrai ricerche. Se provi a cercare una Nintendo Wii nei prodotti, questo termine di ricerca verrà salvato nella tabella BaseQueries.
La versione Gears del metodo addQuery
esegue questa operazione prendendo l'input e salvandolo tramite insertRow
:
var searchterm = { Phrase: phrase, Itemtype: itemtype }; db.insertRow('BaseQueries', searchterm);
insertRow
accetta un oggetto JavaScript (searchterm
) e gestisce l'inserimento nella tabella. Consente inoltre di definire vincoli (ad esempio, l'inserimento di blocchi di unicità di più di un "Bob"). Tuttavia, la maggior parte delle volte gestirai questi vincoli nel database stesso.
Recuperare tutti i termini di ricerca
Per compilare l'elenco delle ricerche precedenti, utilizziamo un wrapper di selezione denominato selectAll
:
GearsBaseContent.prototype.getQueries = function() { return this.db.selectAll('select * from BaseQueries'); }
Viene restituito un array di oggetti JavaScript che corrispondono alle righe del database (ad es. [ { Phrase: 'Nintendo Wii', Itemtype: 'product' }, { ... }, ...]
).
In questo caso, è consentito restituire l'elenco completo. Tuttavia, se hai molti dati, probabilmente vorrai utilizzare un callback nella chiamata di selezione in modo da poter operare su ogni riga restituita man mano che arriva:
db.selectAll('select * from BaseQueries where Itemtype = ?', ['product'], function(row) { ... do something with this row ... });
Ecco alcuni altri metodi di selezione utili in GearsDB:
selectOne(sql, args) | Restituisce il primo/unico oggetto JavaScript corrispondente |
selectRow(table, where, args, select) | Utilizzato normalmente in casi semplici per ignorare SQL |
selectRows(table, where, args, callback, select) | Come selectRow, ma per più risultati. |
Caricamento di un feed
Quando riceviamo il feed dei risultati da Google Base, dobbiamo salvarlo nel database:
content.setFeed({ id: id, JSON: json.toJSONString() }); ... which calls ... GearsBaseContent.prototype.setFeed = function(feed) { this.db.forceRow('BaseFeeds', feed); }
Innanzitutto, prendiamo il feed JSON e lo restituiamo come stringa utilizzando il metodo toJSONString
. Quindi creiamo l'oggetto feed
e lo passiamo al metodo forceRow
. forceRow
INSERISCE una voce se non esiste già o AGGIORNA una voce esistente.
Visualizzazione dei risultati di ricerca
La nostra app mostra i risultati di una determinata ricerca nel riquadro a destra della pagina. Ecco come recuperiamo il feed associato al termine di ricerca:
GearsBaseContent.prototype.getFeed = function(url) { var row = this.db.selectRow('BaseFeeds', 'id = ?', [ url ]); return row.JSON; }
Ora che abbiamo il JSON per una riga, possiamo eval()
per recuperare gli oggetti:
eval("var json = " + jsonString + ";");
Siamo pronti per iniziare a inserire i contenuti da JSON nella nostra pagina utilizzando innerHTML.
Utilizzo di un archivio risorse per l'accesso offline
Poiché i contenuti provengono da un database locale, questa app dovrebbe funzionare anche offline, giusto?
No. Il problema è che per avviare questa app devi caricare le sue risorse web, come JavaScript, CSS, HTML e immagini. Allo stato attuale, se l'utente ha eseguito i seguenti passaggi, l'app potrebbe comunque funzionare: avviare la navigazione online, eseguire alcune ricerche, non chiudere il browser, passare alla modalità offline. In questo modo, gli elementi rimarranno nella cache del browser. Ma cosa succede se non è così? Vogliamo che i nostri utenti possano accedere all'app da zero, dopo un riavvio e così via.
Per farlo, utilizziamo il componente LocalServer e acquisiamo le nostre risorse. Quando acquisisci una risorsa (ad esempio l'HTML e JavaScript necessari per eseguire l'applicazione), Gears salva questi elementi e intercetta anche le richieste del browser per restituirli. Il server locale fungerà da vigile urbano e restituirà i contenuti salvati dallo store.
Utilizziamo anche il componente ResourceStore, che richiede di indicare manualmente al sistema quali file vuoi acquisire. In molti scenari, vuoi creare versioni della tua applicazione e consentire gli upgrade in modo transazionale. Un insieme di risorse definisce una versione e, quando rilasci un nuovo insieme di risorse, vuoi che i tuoi utenti eseguano un upgrade dei file senza problemi. Se questo è il tuo modello, utilizzerai l'API ManagedResourceStore.
Per acquisire le nostre risorse, l'oggetto GearsBaseContent:
- Configurare un array di file da acquisire
- Crea un LocalServer
- Apri o crea un nuovo ResourceStore
- Richiamare per acquisire le pagine nel negozio
// Step 1 this.storeName = 'gears-base'; this.pageFiles = [ location.pathname, 'gears_base.js', '../scripts/gears_db.js', '../scripts/firebug/firebug.js', '../scripts/firebug/firebug.html', '../scripts/firebug/firebug.css', '../scripts/json_util.js', 'style.css', 'capture.gif' ]; // Step 2 try { this.localServer = google.gears.factory.create('beta.localserver', '1.0'); } catch (e) { alert('Could not create local server: ' + e.message); return; } // Step 3 this.store = this.localServer.openStore(this.storeName) || this.localServer.createStore(this.storeName); // Step 4 this.capturePageFiles(); ... which calls ... GearsBaseContent.prototype.capturePageFiles = function() { this.store.capture(this.pageFiles, function(url, success, captureId) { console.log(url + ' capture ' + (success ? 'succeeded' : 'failed')); }); }
È importante notare che puoi acquisire risorse solo sul tuo dominio. Abbiamo riscontrato questa limitazione quando abbiamo tentato di accedere al file JavaScript GearsDB direttamente dal file "gears_db.js" originale nel suo trunk SVN. La soluzione è semplice: devi scaricare tutte le risorse esterne e inserirle nel tuo dominio. Tieni presente che i reindirizzamenti 302 o 301 non funzioneranno, in quanto LocalServer accetta solo i codici server 200 (operazione riuscita) o 304 (non modificato).
Ciò ha delle implicazioni. Se inserisci le immagini su images.yourdomain.com, non potrai acquisirle. www1 e www2 non possono vedersi. Potresti configurare proxy lato server, ma ciò vanificherebbe lo scopo di dividere l'applicazione in più domini.
Debug dell'applicazione offline
Il debug di un'applicazione offline è un po' più complicato. Ora sono disponibili più scenari da testare:
- Sono online e l'app è in esecuzione completa nella cache
- Sono online, ma non ho eseguito l'accesso all'app e non c'è nulla nella cache
- Sono offline, ma ho effettuato l'accesso all'app
- Sono offline e non ho mai avuto accesso all'app (non è una buona situazione).
Per semplificare la vita, abbiamo utilizzato il seguente pattern:
- Disattiviamo la cache in Firefox (o nel browser che preferisci) quando dobbiamo assicurarci che il browser non stia semplicemente recuperando qualcosa dalla cache.
- Eseguiamo il debug utilizzando Firebug (e Firebug Lite per i test su altri browser); utilizziamo
console.log()
ovunque e rileviamo la console per sicurezza. - Aggiungiamo codice JavaScript di supporto a:
- permetterci di cancellare il database e ricominciare da zero
- rimuovi i file acquisiti, in modo che quando ricarichi, vada su internet per recuperarli di nuovo (utile quando stai iterando lo sviluppo ;)
Il widget di debug viene visualizzato sul lato sinistro della pagina solo se hai installato Gears. Contiene callout per la pulizia del codice:
GearsBaseContent.prototype.clearServer = function() { if (this.localServer.openStore(this.storeName)) { this.localServer.removeStore(this.storeName); this.store = null; } } GearsBaseContent.prototype.clearTables = function() { if (this.db) { this.db.run('delete from BaseQueries'); this.db.run('delete from BaseFeeds'); } displayQueries(); }
Conclusione
Come puoi vedere, lavorare con Google Gears è piuttosto semplice. Abbiamo utilizzato GearsDB per semplificare ulteriormente il componente Database e ResourceStore manuale, che ha funzionato perfettamente per il nostro esempio.
L'area in cui trascorri più tempo è la definizione della strategia per quando mettere i dati online e come archiviarli offline. È importante dedicare del tempo alla definizione dello schema del database. Se in futuro dovrai modificare lo schema, dovrai gestire la modifica poiché gli utenti attuali avranno già una versione del database. Ciò significa che dovrai spedire il codice dello script con qualsiasi upgrade del database. Ovviamente, è utile ridurre al minimo questo problema e potresti provare GearShift, una piccola libreria che può aiutarti a gestire le revisioni.
Avremmo anche potuto utilizzare ManagedResourceStore per tenere traccia dei nostri file, con le seguenti conseguenze:
- Saremmo buoni cittadini e versioneremmo i nostri file per consentire upgrade futuri puliti
- Esiste una funzionalità di ManagedResourceStore che consente di assegnare un alias a un URL per un altro contenuto. Una scelta di architettura valida sarebbe quella di avere gears_base.js come versione non Gears e di creare un alias in modo che Gears stesso scarichi gears_base_withgears.js, che avrebbe tutto il supporto offline.
Ci auguriamo che tu abbia trovato le applicazioni Gearing up divertenti e facili da usare. Se hai domande o un'app da condividere, partecipa al forum di Google Gears.