Güçlü ve Çevrimdışı Bir Deneyim için Google Base ve Google Dişlilerini Kullanma

"Google API'leriyle Daha İyi Ajax Uygulamaları Oluşturma" dizisindeki ilk makale.

Dion Almaer ve Pamela Fox, Google
Haziran 2007

Editörün Notu: Google Teams API artık kullanılamıyor.

Giriş

Google Base'i Google Araçları ile birleştirerek çevrimdışı kullanılabilecek bir uygulamayı nasıl oluşturacağınızı gösteriyoruz. Bu makaleyi okuduktan sonra, Google Base API'sını daha iyi tanır ve kullanıcı tercihlerini ve verilerini depolamak ve bunlara erişmek için Google Araçları'nı nasıl kullanacağınızı anlarsınız.

Uygulamayı Anlama

Bu uygulamayı anlamak için ilk olarak Google Base hakkında bilgi sahibi olmanız gerekir. Bu veritabanı, ürün, yorum, yemek tarifi ve etkinlikler gibi çeşitli kategorileri kapsayan geniş bir öğe veritabanıdır.

Her öğeye bir başlık, açıklama, verilerin orijinal kaynağına bağlantı (varsa) ve kategori türüne göre farklılık gösteren ek özellikler eklenir. Google Base, aynı kategorideki öğelerin ortak bir özellik grubunu paylaşmasından faydalanır (örneğin, tüm tariflerin malzemeleri vardır). Hatta Google Base öğeleri Google web aramasından veya Google ürün aramasından gelen arama sonuçlarında ara sıra görünür.

Demo uygulamamız olan Gboard ile birlikte, Google Base'de gerçekleştirebileceğiniz, "çikolata" (yum) içeren yemek tariflerini veya "plaj yürüyüşleri" (romantik!) Bunu, aramalara abone olmanızı ve uygulamayı yeniden ziyaret ettiğinizde veya uygulama her 15 dakikada bir güncellenen feed'leri aradığında güncel sonuçları görmenizi sağlayan bir "Google Base Reader" olarak düşünebilirsiniz.

Uygulamayı genişletmek isteyen geliştiriciler, arama sonuçları yeni sonuçları içerdiğinde kullanıcıyı görsel olarak uyarma, kullanıcının favori öğelere (yıldızlı) yer işareti koymasına (çevrimdışı + çevrimiçi) ve kullanıcının Google Base gibi kategoriye özgü özellik aramaları yapmasına izin verme gibi daha fazla özellik ekleyebilir.

Google Base data API Feed'lerini Kullanma

Google Base, Google Data API çerçevesiyle uyumlu olan Google Base data API ile programatik olarak sorgulanabilir. Google Veri API'sı protokolü web'de okuma ve yazma için basit bir protokol sağlar ve birçok Google ürünü tarafından kullanılır: Picasa, E-tablolar, Blogger, Takvim, Not Defteri ve daha fazlası.

Google Data API biçimi, XML ve Atom Yayınlama Protokolü'nü temel aldığından, okuma/yazma etkileşimlerinin çoğu XML biçimindedir.

Google Data API'sını temel alan bir Google Base feed'i örneği:
http://www.google.com/base/feeds/snippets/-/products?bq=digital+camera

snippets feed türü, öğelerin herkese açık feed'ini verir. -/products, feed'i ürün kategorisiyle kısıtlamamıza olanak tanır. Ayrıca bq= parametresi, feed'i yalnızca "dijital kamera" anahtar kelimesini içeren sonuçlarla kısıtlamamızı sağlar. Bu özet akışını tarayıcıda görüntülerseniz eşleşen sonuçları olan <entry> düğüm içeren XML'i görürsünüz. Her giriş tipik yazar, başlık, içerik ve bağlantı öğelerini içerir, ancak kategoriye özgü ek özellikler de içerir (ürün kategorisindeki öğeler için "fiyat" gibi).

Tarayıcıdaki XMLHttpRequest'in web alanları arası kısıtlaması nedeniyle, JavaScript kodumuzdaki www.google.com'dan gelen XML feed'inin doğrudan okunmasına izin verilmez. XML'de okumak için sunucu tarafı proxy'si ayarlayabilir ve bunu uygulamamızla aynı alan adında bir noktaya serpiştirebiliriz. Ancak sunucu tarafı programlamadan tamamen kaçınmak isteriz. Neyse ki başka bir alternatif var.

Diğer Google Veri API'leri gibi Google Base veri API'sı da standart XML'e ek olarak bir JSON çıkış seçeneği sunar. Daha önce JSON biçiminde gördüğümüz feed'in çıkışı şu URL'de olur:
http://www.google.com/base/feeds/snippets/-/products?bq=digital+camera&alt=json

JSON, çeşitli veri türlerinin yanı sıra hiyerarşik iç içe yerleştirmeye olanak tanıyan hafif bir değiştirilebilir biçimdir. Ancak daha da önemlisi, JSON çıkışı, yerel bir JavaScript kodudur. Dolayısıyla, web alanları arasında kısıtlamayı atlayarak yalnızca bir komut dosyası etiketinde buna referans vererek web sayfanıza yüklenebilir.

Google Veri API'leri ayrıca, JSON yüklendikten sonra yürütülecek bir geri çağırma işlevi içeren "json-in-script" çıkışı belirtmenize de olanak tanır. Sayfaya dinamik olarak komut dosyası etiketleri ekleyip her biri için farklı geri çağırma işlevleri belirtebileceğimizden, JSON çıkışının kullanılması daha da kolaydır.

Bu nedenle bir Base API JSON feed'ini sayfaya dinamik olarak yüklemek için feed URL'sine sahip bir komut dosyası etiketi (altcallback değerleriyle eklenir) oluşturan ve bunu sayfaya ekleyen aşağıdaki işlevi kullanabiliriz.

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);
}

Böylece, geri çağırma işlevimiz listResults artık madde işaretli listede bulunan her parametrede, tek parametre olarak JSON ve aktarılan parametreleri yansıtabilir ve her girişle ilgili bilgileri görüntüleyebilir.

  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("");
  }

Google Araçları ekleme

Artık Google Base API'sı aracılığıyla Google Base ile konuşabilecek bir uygulamamız olduğuna göre bu uygulamayı çevrimdışı olarak etkinleştirmek istiyoruz. Google dişlileri burada devreye girer.

Çevrimdışı olabilecek bir uygulama yazma söz konusu olduğunda çeşitli mimari seçenekleri mevcuttur. Kendinize uygulamanın çevrimiçi veya çevrimdışı olarak nasıl çalışması gerektiğiyle ilgili sorular soracaksınız (ör. tam olarak aynı şekilde mi çalışıyor? Arama gibi bazı özellikler devre dışı mı bırakıldı? Senkronizasyonu nasıl gerçekleştireceksiniz?)

Bu durumda, eklenti içermeyen kullanıcılara çevrimdışı kullanım avantajlarının yanı sıra daha duyarlı bir kullanıcı arayüzü sunan bir uygulama sunmak için, dişli çark içermeyen tarayıcılara sahip kullanıcıların uygulamayı kullanmaya devam edebilmesini istiyoruz.

Mimarimiz şöyle görünür:

  • Arama sorgularınızı depolamaktan ve bu sorgulardan elde edilen sonuçları döndürmeden sorumlu bir JavaScript nesnemiz var.
  • Google Kubernetes yüklüyse yerel veritabanındaki her şeyi depolayan bir DKIM sürümü alırsınız.
  • Yüklü Google Kubernetes sürümünüz yoksa sorguları bir çerezde depolayan ve sonuçların bir çerezde depolanamayacak kadar büyük olması nedeniyle tam sonuçları hiç depolamayan bir sürüm alırsınız (biraz daha yavaş yanıt verir).
Bu mimarinin en güzel yönü, tüm mağaza genelinde if (online) {} kontrolü yapmak zorunda olmamanız. Bunun yerine uygulamanın bir dişli kontrolü vardır ve ardından doğru adaptör kullanılır.


Glos Yerel Veritabanı Kullanma

Kubernetes'in bileşenlerinden biri, yerleşik ve kullanımınıza hazır yerel SQLite veritabanıdır. Daha önce MySQL veya Oracle gibi sunucu tarafı veritabanları için API'ler kullandıysanız aşina olduğunuz basit bir veritabanı API'si vardır.

Yerel veritabanı kullanma adımları oldukça basittir:

  • Google Araçları nesnelerini ilk kullanıma hazırlayın
  • Veritabanı fabrika nesnesi alın ve bir veritabanı açın
  • SQL isteklerini yürütmeye başlayın

Bunları hızlı bir şekilde ele alalım.


Google Araçları Nesnelerini başlatma

Uygulamanız, /gears/samples/gears_init.js içeriğini doğrudan veya kodu kendi JavaScript dosyanıza yapıştırarak okumalıdır. <script src="..../gears_init.js" type="text/JavaScript"></script> yola çıktıktan sonra google.dişliler ad alanına erişebilirsiniz.


Veritabanı Fabrika Nesnesi Alma ve Veritabanı Açma
var db = google.gears.factory.create('beta.database', '1.0');
db.open('testdb');

Bu tek çağrı, bir veritabanı şeması açmanıza olanak tanıyan bir veritabanı nesnesi sağlayacaktır. Veritabanlarını açtığınızda, kapsamlar aynı kaynak politikası kuralları üzerinden ayarlanır. Böylece "testdb" dosyanız "testdb" ile çakışmaz.


SQL İsteklerini Yürütmeye Başlayın

Artık SQL isteklerini veritabanına göndermeye hazırız. "Seç" istekleri gönderdiğimizde, istenen veriler için tekrar kullanabileceğimiz bir sonuç grubu elde ederiz:

var rs = db.execute('select * from foo where name = ?', [ name ]);

Döndürülen sonuç kümesinde aşağıdaki yöntemlerle çalışabilirsiniz:

booleanisValidRow()
voidnext()
voidclose()
intfieldCount()
stringfieldName(int fieldIndex)
variantfield(int fieldIndex)
variantfieldByName(string fieldname)

Daha fazla ayrıntı için Database Module API belgelerine bakın. (Editörün Notu: Google Teams API artık kullanılamıyor).


Düşük Düzey API'yi Kapsama Almak için GearDB'yi Kullanma

Sık gerçekleştirilen bazı veritabanı görevlerini kapsamak ve daha kolay hale getirmek istedik. Örneğin,

  • Uygulamada hata ayıklama sırasında oluşturulan SQL'i günlüğe kaydetmek için güzel bir yöntem istiyorduk.
  • İstisnaları tüm yerde tek bir yerden try{}catch(){} ele almak yerine sorunları tek bir yerden yönetmek istedik.
  • Verileri okurken veya yazarken sonuç grupları yerine JavaScript nesneleriyle ilgilenmek istedik.

Bu sorunları genel bir şekilde ele almak için Database nesnesini sarmalayan bir açık kaynak kitaplığı olan GearsDB'yi oluşturduk. Şimdi, KubernetesDB'den nasıl yararlanacağınızı göstereceğiz.

İlk Kurulum

window.onload kodumuzda, güvendiğimiz veritabanı tablolarının mevcut olduğundan emin olmamız gerekir. Aşağıdaki kod çalıştırıldığında kullanıcı DKIM'yi yüklemişse bir GearsBaseContent nesnesi oluşturur:

content = hasGears() ? new GearsBaseContent() : new CookieBaseContent();

Ardından, veritabanını açar ve henüz yoksa tablolar oluştururuz:

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)');
}

Bu noktada, sorguları ve feed'leri depolayabileceğimiz bir tablo oluşturduk. new GearsDB(name) kodu, veritabanının açılışını belirtilen adla içerir. run yöntemi, alt düzey execute yöntemini sarmalar ancak aynı zamanda bir konsola hata ayıklama çıktısını ve yakalama istisnalarını işler.


Arama Terimi Ekleme

Uygulamayı ilk çalıştırdığınızda hiç arama yapılmaz. Ürünlerde Nintendo Wii araması yapmaya çalışırsanız bu arama terimini BaseSorgular tablosunda kaydederiz.

addQuery yönteminin VM sürümü, girişi alıp insertRow aracılığıyla kaydederek yapar:

var searchterm = { Phrase: phrase, Itemtype: itemtype };
db.insertRow('BaseQueries', searchterm); 

insertRow, bir JavaScript nesnesini (searchterm) alıp tabloyu INSERT içine ekliyor. Ayrıca, sınırlamalar (ör. birden fazla "Bob"un benzersizlik engellemesi kullanılarak eklenmesi) tanımlamanıza da olanak tanır. Bununla birlikte, çoğu zaman bu kısıtlamaları veritabanının kendisinde ele alırsınız.


Tüm Arama Terimlerini Alma

Geçmiş aramalar listenizi doldurmak için, selectAll adında güzel bir seçim sarmalayıcı kullanırız:

GearsBaseContent.prototype.getQueries = function() {
  return this.db.selectAll('select * from BaseQueries');
}

Bu, veritabanındaki satırlarla eşleşen bir JavaScript nesneleri dizisi (ör. [ { Phrase: 'Nintendo Wii', Itemtype: 'product' }, { ... }, ...]) döndürür.

Bu durumda, tam listeyi döndürebilirsiniz. Ancak çok fazla veriniz varsa seçili çağrıda geri çağırmayı kullanmak istersiniz. Böylece döndürülen her satırda şu işlemler sırasında çalışabilirsiniz:

 db.selectAll('select * from BaseQueries where Itemtype = ?', ['product'], function(row) {
  ... do something with this row ...
});

Burada, DKIMDB'deki diğer bazı faydalı seçim yöntemlerini görebilirsiniz:

selectOne(sql, args)Eşleşen ilk/bir JavaScript nesnesini döndür
selectRow(table, where, args, select)Normalde SQL'i yoksaymak için basit durumlarda kullanılır
selectRows(table, where, args, callback, select)SelectRow ile aynı, ancak birden fazla sonuç için.

Feed Yükleme

Google Base'den gelen sonuç feed'ini aldığımızda, bunu veritabanına kaydetmemiz gerekir:

content.setFeed({ id: id, JSON: json.toJSONString() });

... which calls ...

GearsBaseContent.prototype.setFeed = function(feed) {
  this.db.forceRow('BaseFeeds', feed);
}

Önce JSON feed'ini alırız ve toJSONString yöntemini kullanarak bir Dize olarak döndürürüz. Daha sonra feed nesnesini oluşturur ve forceRow yöntemine geçiririz. forceRow, önceden var olmayan bir giriş EKLEYİN veya mevcut bir girişi GÜNCELLEYİN.


Arama Sonuçlarını Görüntüleme

Uygulamamız, belirli bir aramanın sonuçlarını sayfanın sağ panelinde görüntüler. Arama terimiyle ilişkili feed'i nasıl aldığımız aşağıda açıklanmıştır:

GearsBaseContent.prototype.getFeed = function(url) {
  var row = this.db.selectRow('BaseFeeds', 'id = ?', [ url ]);
  return row.JSON;
}

Artık bir satır için JSON'a sahip olduğumuza göre, nesneleri geri almak amacıyla bunu eval():

eval("var json = " + jsonString + ";");

Yarışa başladık ve JSON'dan sayfamıza html içeriği oluşturmaya başlayabiliriz.


Çevrimdışı Erişim için Kaynak Deposu Kullanma

Yerel bir veritabanından içerik aldığımız için bu uygulama da çevrimdışı çalışmalıdır.

Sorun, bu uygulamayı başlatmak için JavaScript, CSS, HTML ve resimler gibi web kaynaklarını yüklemeniz gerekiyor. Şu anki durumda, kullanıcınız aşağıdaki adımları attığında uygulama çalışmaya devam edebilir: online başlayın, bazı aramalar yapın, tarayıcıyı kapatmayın, çevrimdışına geçin. Bu işlem, öğeler hâlâ tarayıcının önbelleğinde kalacağından işe yarayabilir. Ancak bu mümkün değilse ne yapabilirsiniz? Kullanıcılarımızın, yeniden başlatma sonrasında, en baştan uygulamaya erişebilmelerini istiyoruz.

Bunun için LocalServer bileşenini kullanırız ve kaynaklarımızı alırız. Bir kaynağı (uygulamayı çalıştırmak için gereken HTML ve JavaScript gibi) yakaladığınızda, dişli çarklar bu öğeleri kaydeder ve ayrıca, söz konusu öğelerin döndürülmesi için tarayıcıdaki istekleri yakalar. Yerel sunucu, trafik polisi olarak hareket eder ve kayıtlı içerikleri mağazadan döndürür.

Ayrıca, sisteme hangi dosyaları yakalamak istediğinizi manuel olarak bildirmenizi gerektiren ResourceStore bileşenini de kullanırız. Çoğu senaryoda uygulamanızı sürümlendirmek ve işlemlerde yeni sürüme geçişlere izin vermek istersiniz. Bir dizi kaynak birlikte sürüm tanımlar ve yeni bir kaynak grubunu yayınladığınızda kullanıcılarınızın dosyaları sorunsuz bir şekilde yükseltmesini istersiniz. Bu, sizin modelinizse ManagedResourceStore API'yi kullanıyor olacaksınız.

Kaynaklarımızı yakalamak için, GearBaseContent nesnesi:

  1. Yakalanması gereken bir dosya dizisi oluşturma
  2. YerelSunucu Oluştur
  3. Yeni bir ResourceStore aç veya oluştur
  4. Sayfaları mağazaya yakalamak için çağrıda bulunun
// 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'));
  });
}

Burada dikkat edilmesi gereken nokta, yalnızca kendi alanınızdaki kaynakları yakalayabilmenizdir. Bu sınırlama, SVN santraline ait orijinal " Gears_db.js" dosyasından doğrudan KubernetesDB JavaScript dosyasına erişmeye çalıştığımızda ortaya çıktı. Elbette çözüm basittir: Tüm harici kaynakları indirmeniz ve alan adınıza yerleştirmeniz gerekir. LocalServer yalnızca 200 (Başarılı) veya 304 (Değiştirilmedi) sunucu kodlarını kabul ettiğinden 302 veya 301 yönlendirmeleri çalışmaz.

Bu durum etkili olabilir. Resimlerinizi images.alanadiniz.com'a yerleştirirseniz yakalayamazsınız. www1 ve www2 birbirini göremez. Sunucu tarafı proxy'leri ayarlayabilirsiniz ancak bu durumda uygulamanızı birden fazla alana bölmeniz gerekmez.

Çevrimdışı Uygulamada Hata Ayıklama

Çevrimdışı bir uygulamada hata ayıklamak biraz daha karmaşıktır. Artık test edilmesi gereken daha fazla senaryo var:

  • Uygulama tamamen önbellekte çalışırken internete bağlıyım
  • Onlineyım ancak uygulamaya erişmedim ve önbellekte hiçbir şey yok
  • İnternete bağlı değilim ancak uygulamaya eriştim
  • İnternete bağlı değilim ve uygulamaya hiç erişmedim (iyi bir yer değil)

Hayatı kolaylaştırmak için aşağıdaki düzeni kullandık:

  • Tarayıcının önbellekten bir şey almadığından emin olmamız gerektiğinde Firefox'ta (veya tercih ettiğiniz tarayıcıda) önbelleği devre dışı bırakırız.
  • Firebug (ve diğer tarayıcılarda test için Firebug Lite) ile hata ayıklayoruz. Her yerde console.log() kullanıyoruz ve her ihtimale karşı konsol için tespit ediyoruz
  • Yardımcı JavaScript kodunu şu öğelere ekleriz:
    • veritabanını temizlememize ve bize temiz bir seçenek listesi vermemize olanak tanıyor
    • Yakalanan dosyaları kaldırabilir, böylece yeniden yüklediğinizde bu dosyaları tekrar almak için İnternet'e yönlendirilirsiniz (Geliştirme için yinelediğinizde geçerlidir ;)

Hata ayıklama widget'ı, yalnızca DKIM yüklüyse sayfanın sol tarafında gösterilir. Kodu temizlemek için açıklama metinleri içeriyor:

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();
}

Sonuç

Gördüğünüz gibi, Google Araçları ile çalışmak son derece basit. Veritabanı bileşenini daha da kolaylaştırmak için GearDB'den, örneğimizde de sorunsuz şekilde çalışan manuel ResourceStore'dan yararlandık.

En çok zaman harcadığınız alan, verilerin ne zaman çevrimiçi olacağına ve çevrimdışı olarak nasıl depolanacağına ilişkin stratejiyi tanımlıyor. Veritabanı şemasını tanımlamaya zaman ayırmanız önemlidir. Gelecekte şemayı değiştirmeniz gerekirse mevcut kullanıcılarınızın halihazırda veritabanının bir sürümü olacağı için bu değişikliği yönetmeniz gerekir. Bu, komut dosyası kodunu tüm db yükseltmeleriyle göndermeniz gerektiği anlamına gelir. Elbette, düzeltme sayısını en aza indirmek faydalıdır. Düzeltmeleri yönetmenize yardımcı olabilecek küçük bir kitaplık olan GearShift'i de deneyebilirsiniz.

Dosyalarımızı takip etmek için ManagedResourceStore'u kullanarak aşağıdaki sonuçları da elde etmiş olabiliriz:

  • İyi bir vatandaş olsak ve gelecekteki temiz yükseltme işlemlerini gerçekleştirmek için dosyalarımızı da
  • ManagedResourceStore'da, bir URL'yi başka bir içeriğe takmanıza olanak tanıyan bir özellik vardır. Geçerli bir mimari seçeneği, dişlilerin_takma.js'nin Gears olmayan bir sürüme sahip olması ve takma adın, çevrimdışı desteğin tamamı olan Gears_base_with Gears.js'yi indirmesidir.
Uygulamamızda tek bir arayüze sahip olmanın ve bu arayüzü iki şekilde uygulamanın daha kolay olduğunu düşündük.

Uygulamaları etkinleştirmeyi eğlenceli ve kolay bulduğunuzu umuyoruz. Sorularınız varsa veya paylaşmak istediğiniz bir uygulama varsa lütfen Google Araçları forumunda bize katılın.