Dion Almaer et Pamela Fox, Google
Juin 2007
Remarque de l'éditeur : L'API Google Gears n'est plus disponible.
- Introduction
- Comprendre l'application
- Utiliser les flux de l'API Google Base Data
- Ajouter Google Gears à l'application
- Déboguer l'application hors connexion
- Conclusion
Introduction
En combinant Google Base et Google Gears, nous vous montrons comment créer une application utilisable hors connexion. Après avoir lu cet article, vous serez plus familiarisé avec l'API Google Base et vous comprendrez comment utiliser Google Gears pour stocker et accéder aux préférences et aux données des utilisateurs.
Comprendre l'application
Pour comprendre cette application, vous devez d'abord vous familiariser avec Google Base, qui est essentiellement une grande base de données d'éléments couvrant diverses catégories telles que les produits, les avis, les recettes, les événements, etc.
Chaque élément est annoté avec un titre, une description, un lien vers la source d'origine des données (le cas échéant) et des attributs supplémentaires qui varient selon le type de catégorie. Google Base tire parti du fait que les éléments d'une même catégorie partagent un ensemble commun d'attributs (par exemple, toutes les recettes ont des ingrédients). Les éléments Google Base peuvent même apparaître de temps en temps dans les résultats de recherche sur le Web ou dans la recherche de produits Google.
Notre application de démonstration, Base with Gears, vous permet de stocker et d'afficher les recherches courantes que vous pouvez effectuer sur Google Base, comme trouver des recettes avec "chocolat" (miam) ou des annonces personnelles avec "promenades sur la plage" (romantique !). Vous pouvez le considérer comme un "lecteur Google Base" qui vous permet de vous abonner à des recherches et de voir les résultats mis à jour lorsque vous revenez dans l'application ou lorsque l'application recherche des flux mis à jour toutes les 15 minutes.
Les développeurs qui souhaitent étendre l'application peuvent ajouter d'autres fonctionnalités, comme alerter visuellement l'utilisateur lorsque les résultats de recherche contiennent de nouveaux résultats, lui permettre de mettre en favori (étoiler) ses éléments préférés (hors connexion et en ligne) et lui permettre d'effectuer des recherches d'attributs spécifiques à une catégorie, comme Google Base.
Utiliser les flux de données de l'API Google Base
Google Base peut être interrogé de manière programmatique avec l'API Google Base Data, qui est conforme au framework Google Data APIs. Le protocole Google Data API fournit un protocole simple pour lire et écrire sur le Web. Il est utilisé par de nombreux produits Google : Picasa, Sheets, Blogger, Agenda, Notebook, etc.
Le format de l'API Google Data est basé sur XML et le protocole de publication Atom. La plupart des interactions de lecture/écriture sont donc au format XML.
Voici un exemple de flux Google Base basé sur l'API Google Data :
http://www.google.com/base/feeds/snippets/-/products?bq=digital+camera
Le type de flux snippets
nous donne accès au flux d'articles disponible publiquement. -/products
nous permet de limiter le flux à la catégorie de produits. Le paramètre bq=
nous permet de limiter davantage le flux aux résultats contenant le mot clé "appareil photo numérique". Si vous affichez ce flux dans le navigateur, vous verrez du code XML contenant des nœuds <entry>
avec les résultats correspondants. Chaque entrée contient les éléments classiques (auteur, titre, contenu et lien), mais aussi des attributs supplémentaires spécifiques à la catégorie (comme "prix" pour les éléments de la catégorie "Produits").
En raison de la restriction interdomaine de XMLHttpRequest dans le navigateur, nous ne sommes pas autorisés à lire directement un flux XML depuis www.google.com dans notre code JavaScript. Nous pourrions configurer un proxy côté serveur pour lire le fichier XML et le renvoyer à un emplacement sur le même domaine que notre application, mais nous préférerions éviter complètement la programmation côté serveur. Heureusement, il existe une alternative.
Comme les autres API Google Data, l'API Google Base Data propose une option de sortie JSON, en plus du format XML standard. La sortie du flux que nous avons vu précédemment au format JSON se trouverait à cette URL :
http://www.google.com/base/feeds/snippets/-/products?bq=digital+camera&alt=json
JSON est un format d'échange léger qui permet l'imbrication hiérarchique ainsi que différents types de données. Mais surtout, la sortie JSON est du code JavaScript natif. Vous pouvez donc la charger dans votre page Web en la référençant simplement dans une balise de script, ce qui contourne la restriction interdomaines.
Les API Google Data vous permettent également de spécifier une sortie "json-in-script" avec une fonction de rappel à exécuter une fois le JSON chargé. Cela facilite encore davantage l'utilisation de la sortie JSON, car nous pouvons ajouter dynamiquement des balises de script à la page et spécifier différentes fonctions de rappel pour chacune d'elles.
Ainsi, pour charger dynamiquement un flux JSON de l'API Base dans la page, nous pouvons utiliser la fonction suivante qui crée une balise de script avec l'URL du flux (à laquelle sont ajoutées les valeurs alt
callback
) et l'ajoute à la page.
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); }
Notre fonction de rappel listResults
peut désormais parcourir le fichier JSON transmis en tant que seul paramètre et afficher des informations sur chaque entrée trouvée dans une liste à puces.
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(""); }
Ajouter Google Gears
Maintenant que nous disposons d'une application capable de communiquer avec Google Base via l'API Google Data, nous souhaitons permettre à cette application de s'exécuter hors connexion. C'est là que Google Gears entre en jeu.
Il existe différents choix d'architecture pour écrire une application qui peut passer hors connexion. Vous vous poserez des questions sur le fonctionnement de l'application en ligne et hors connexion (par exemple, fonctionne-t-elle exactement de la même manière ? Certaines fonctionnalités sont-elles désactivées, comme la recherche ? Comment allez-vous gérer la synchronisation ?)
Dans notre cas, nous voulions nous assurer que les utilisateurs de navigateurs sans Gears puissent toujours utiliser l'application, tout en offrant aux utilisateurs qui disposent du plug-in les avantages de l'utilisation hors connexion et d'une interface utilisateur plus réactive.
Notre architecture se présente comme suit :
- Nous disposons d'un objet JavaScript chargé de stocker vos requêtes de recherche et de renvoyer les résultats de ces requêtes.
- Si vous avez installé Google Gears, vous obtenez une version Gears qui stocke tout dans la base de données locale.
- Si vous n'avez pas installé Google Gears, vous obtenez une version qui stocke les requêtes dans un cookie et ne stocke pas du tout les résultats complets (d'où la réactivité légèrement plus lente), car les résultats sont trop volumineux pour être stockés dans un cookie.
if (online) {}
partout. Au lieu de cela, l'application effectue une vérification Gears, puis l'adaptateur approprié est utilisé.
Utiliser une base de données locale Gears
L'un des composants de Gears est la base de données SQLite locale qui est intégrée et prête à l'emploi. Il existe une API de base de données simple qui vous sera familière si vous avez déjà utilisé des API pour des bases de données côté serveur telles que MySQL ou Oracle.
Les étapes à suivre pour utiliser une base de données locale sont assez simples :
- Initialiser les objets Google Gears
- Obtenir un objet de fabrique de base de données et ouvrir une base de données
- Commencer à exécuter des requêtes SQL
Passons-les rapidement en revue.
Initialiser les objets Google Gears
Votre application doit lire le contenu de /gears/samples/gears_init.js
directement ou en collant le code dans votre propre fichier JavaScript. Une fois que vous avez <script src="..../gears_init.js" type="text/JavaScript"></script>
, vous avez accès à l'espace de noms google.gears.
Obtenir un objet Database Factory et ouvrir une base de données
var db = google.gears.factory.create('beta.database', '1.0'); db.open('testdb');
Cet appel unique vous fournira un objet de base de données qui vous permettra d'ouvrir un schéma de base de données. Lorsque vous ouvrez des bases de données, elles sont délimitées par les règles de la même origine. Votre "testdb" ne sera donc pas en conflit avec mon "testdb".
Commencer à exécuter des requêtes SQL
Nous sommes maintenant prêts à envoyer des requêtes SQL à la base de données. Lorsque nous envoyons des requêtes "select", nous obtenons un ensemble de résultats que nous pouvons parcourir pour obtenir les données souhaitées :
var rs = db.execute('select * from foo where name = ?', [ name ]);
Vous pouvez effectuer des opérations sur l'ensemble de résultats renvoyé à l'aide des méthodes suivantes :
boolean | isValidRow() |
void | next() |
void | close() |
int | fieldCount() |
string | fieldName(int fieldIndex) |
variant | field(int fieldIndex) |
variant | fieldByName(string fieldname) |
Pour en savoir plus, veuillez consulter la documentation de l'API du module de base de données. (Remarque de l'éditeur : l'API Google Gears n'est plus disponible).
Utiliser GearsDB pour encapsuler l'API de bas niveau
Nous voulions encapsuler et simplifier certaines tâches courantes liées aux bases de données. Par exemple,
- Nous voulions disposer d'un moyen simple d'enregistrer le code SQL généré lors du débogage de l'application.
- Nous voulions gérer les exceptions à un seul endroit au lieu d'avoir à
try{}catch(){}
partout. - Nous voulions traiter des objets JavaScript plutôt que des ensembles de résultats lors de la lecture ou de l'écriture de données.
Pour gérer ces problèmes de manière générique, nous avons créé GearsDB, une bibliothèque Open Source qui encapsule l'objet Database. Nous allons maintenant vous montrer comment utiliser GearsDB.
Configuration initiale
Dans notre code window.onload, nous devons nous assurer que les tables de base de données sur lesquelles nous nous appuyons sont en place. Si l'utilisateur a installé Gears lorsque le code suivant s'exécute, il crée un objet GearsBaseContent
:
content = hasGears() ? new GearsBaseContent() : new CookieBaseContent();
Ensuite, nous ouvrons la base de données et créons des tables si elles n'existent pas déjà :
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)'); }
À ce stade, nous sommes certains d'avoir une table pour stocker les requêtes et les flux. Le code new GearsDB(name)
encapsulera l'ouverture d'une base de données portant le nom donné. La méthode run
encapsule la méthode execute
de niveau inférieur, mais gère également le débogage des résultats sur une console et l'interception des exceptions.
Ajouter un terme de recherche
Lorsque vous exécutez l'application pour la première fois, vous n'avez aucune recherche. Si vous essayez de rechercher une Nintendo Wii dans les produits, ce terme de recherche sera enregistré dans la table BaseQueries.
La version Gears de la méthode addQuery
effectue cette opération en prenant l'entrée et en l'enregistrant via insertRow
:
var searchterm = { Phrase: phrase, Itemtype: itemtype }; db.insertRow('BaseQueries', searchterm);
insertRow
prend un objet JavaScript (searchterm
) et gère son insertion dans le tableau pour vous. Il vous permet également de définir des contraintes (par exemple, l'unicité pour empêcher l'insertion de plusieurs "Bob"). Toutefois, la plupart du temps, vous gérerez ces contraintes dans la base de données elle-même.
Obtenir tous les termes de recherche
Pour remplir votre liste de recherches précédentes, nous utilisons un wrapper de sélection agréable nommé selectAll
:
GearsBaseContent.prototype.getQueries = function() { return this.db.selectAll('select * from BaseQueries'); }
Cela renverra un tableau d'objets JavaScript correspondant aux lignes de la base de données (par exemple, [ { Phrase: 'Nintendo Wii', Itemtype: 'product' }, { ... }, ...]
).
Dans ce cas, vous pouvez renvoyer la liste complète. Toutefois, si vous disposez de nombreuses données, vous souhaiterez probablement utiliser un rappel dans l'appel de sélection afin de pouvoir agir sur chaque ligne renvoyée à mesure qu'elle arrive :
db.selectAll('select * from BaseQueries where Itemtype = ?', ['product'], function(row) { ... do something with this row ... });
Voici d'autres méthodes de sélection utiles dans GearsDB :
selectOne(sql, args) | Renvoie le premier objet JavaScript correspondant |
selectRow(table, where, args, select) | Normalement utilisé dans les cas simples pour ignorer SQL |
selectRows(table, where, args, callback, select) | Identique à selectRow, mais pour plusieurs résultats. |
Charger un flux
Lorsque nous recevons le flux de résultats de Google Base, nous devons l'enregistrer dans la base de données :
content.setFeed({ id: id, JSON: json.toJSONString() }); ... which calls ... GearsBaseContent.prototype.setFeed = function(feed) { this.db.forceRow('BaseFeeds', feed); }
Nous commençons par récupérer le flux JSON et le renvoyer sous forme de chaîne à l'aide de la méthode toJSONString
. Nous créons ensuite l'objet feed
et le transmettons à la méthode forceRow
. forceRow
insère une entrée si elle n'existe pas déjà ou met à jour une entrée existante.
Afficher les résultats de recherche
Notre application affiche les résultats d'une recherche donnée dans le panneau de droite de la page. Voici comment nous récupérons le flux associé au terme de recherche :
GearsBaseContent.prototype.getFeed = function(url) { var row = this.db.selectRow('BaseFeeds', 'id = ?', [ url ]); return row.JSON; }
Maintenant que nous avons le JSON pour une ligne, nous pouvons le eval()
pour récupérer les objets :
eval("var json = " + jsonString + ";");
Nous pouvons commencer à insérer du contenu JSON dans notre page à l'aide de innerHTML.
Utiliser un Resource Store pour l'accès hors connexion
Puisque nous obtenons du contenu à partir d'une base de données locale, cette application devrait également fonctionner hors connexion, n'est-ce pas ?
Eh bien, non. Le problème est que pour lancer cette application, vous devez charger ses ressources Web, telles que JavaScript, CSS, HTML et les images. Dans l'état actuel des choses, si votre utilisateur a effectué les étapes suivantes, l'application peut toujours fonctionner : démarrer en ligne, effectuer des recherches, ne pas fermer le navigateur, passer hors connexion. Cela peut fonctionner, car les éléments se trouveront toujours dans le cache du navigateur. Mais que faire si ce n'est pas le cas ? Nous voulons que nos utilisateurs puissent accéder à l'application depuis le début, après un redémarrage, etc.
Pour ce faire, nous utilisons le composant LocalServer et capturons nos ressources. Lorsque vous capturez une ressource (telle que le code HTML et JavaScript requis pour exécuter l'application), Gears enregistre ces éléments et intercepte également les requêtes du navigateur pour les renvoyer. Le serveur local agira comme un agent de circulation et renverra le contenu enregistré du magasin.
Nous utilisons également le composant ResourceStore, qui vous oblige à indiquer manuellement au système les fichiers que vous souhaitez capturer. Dans de nombreux scénarios, vous souhaitez versionner votre application et autoriser les mises à niveau de manière transactionnelle. Un ensemble de ressources définit une version. Lorsque vous publiez un nouvel ensemble de ressources, vous souhaitez que vos utilisateurs puissent mettre à niveau les fichiers de manière fluide. Si tel est le cas, vous utiliserez l'API ManagedResourceStore.
Pour capturer nos ressources, l'objet GearsBaseContent :
- Configurer un ensemble de fichiers à capturer
- Créer un LocalServer
- Ouvrez ou créez un ResourceStore.
- Appeler pour capturer les pages dans le magasin
// 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')); }); }
Il est important de noter que vous ne pouvez capturer des ressources que sur votre propre domaine. Nous avons rencontré cette limite lorsque nous avons essayé d'accéder au fichier JavaScript GearsDB directement à partir du fichier "gears_db.js" d'origine dans son trunk SVN. La solution est simple : vous devez télécharger toutes les ressources externes et les placer sous votre domaine. Notez que les redirections 302 ou 301 ne fonctionneront pas, car LocalServer n'accepte que les codes de serveur 200 (Succès) ou 304 (Non modifié).
Cela a des conséquences. Si vous placez vos images sur images.yourdomain.com, vous ne pourrez pas les capturer. www1 et www2 ne peuvent pas se voir. Vous pouvez configurer des proxys côté serveur, mais cela irait à l'encontre de l'objectif de répartir votre application sur plusieurs domaines.
Déboguer l'application hors connexion
Le débogage d'une application hors connexion est un peu plus compliqué. Vous pouvez désormais tester d'autres scénarios :
- Je suis en ligne et l'application est entièrement exécutée dans le cache.
- Je suis en ligne, mais je n'ai pas accédé à l'application et il n'y a rien dans le cache.
- Je suis hors connexion, mais j'ai accédé à l'application
- Je suis hors connexion et je n'ai jamais accédé à l'application (ce n'est pas une bonne situation).
Pour vous faciliter la tâche, nous avons utilisé le modèle suivant :
- Nous désactivons le cache dans Firefox (ou le navigateur de votre choix) lorsque nous devons nous assurer que le navigateur ne récupère pas simplement un élément du cache.
- Nous déboguons à l'aide de Firebug (et de Firebug Lite pour les tests sur d'autres navigateurs). Nous utilisons
console.log()
partout et détectons la console au cas où. - Nous ajoutons du code JavaScript d'assistance pour :
- nous permettent d'effacer la base de données et de repartir de zéro.
- supprimer les fichiers capturés afin que, lorsque vous rechargez la page, le navigateur se connecte à Internet pour les récupérer à nouveau (utile lorsque vous itérez sur le développement) ;
Le widget de débogage s'affiche sur le côté gauche de la page uniquement si vous avez installé Gears. Il comporte des encadrés pour nettoyer le code :
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(); }
Conclusion
Vous pouvez constater que l'utilisation de Google Gears est en fait assez simple. Nous avons utilisé GearsDB pour simplifier encore davantage le composant Database et le ResourceStore manuel, qui fonctionnait parfaitement pour notre exemple.
La partie la plus chronophage consiste à définir la stratégie pour mettre les données en ligne et la façon de les stocker hors connexion. Il est important de prendre le temps de définir le schéma de la base de données. Si vous devez modifier le schéma à l'avenir, vous devrez gérer cette modification, car vos utilisateurs actuels auront déjà une version de la base de données. Cela signifie que vous devrez fournir le code du script avec toute mise à niveau de la base de données. Il est évidemment préférable de minimiser ce problème. Vous pouvez essayer GearShift, une petite bibliothèque qui peut vous aider à gérer les révisions.
Nous aurions également pu utiliser ManagedResourceStore pour suivre nos fichiers, avec les conséquences suivantes :
- Nous serons de bons citoyens et nous versionnerons nos fichiers pour permettre des mises à niveau propres à l'avenir.
- Le ManagedResourceStore dispose d'une fonctionnalité qui vous permet d'associer une URL à un autre contenu. Une architecture valide consisterait à faire de gears_base.js une version non Gears et à créer un alias pour que Gears télécharge gears_base_withgears.js, qui inclurait toutes les fonctionnalités hors connexion.
Nous espérons que vous avez trouvé la section "Préparer les applications" amusante et facile à suivre. Si vous avez des questions ou une application à partager, rejoignez-nous sur le forum Google Gears.