Google Photography Prize Galerisi

Ilmari Heikkinen

Google Fotoğraf Ödülü web sitesi

Kısa süre önce Google Fotoğraf Ödülü sitesinde Galeri bölümünü kullanıma sunduk. Galeride, Google+'tan alınan sonsuz bir kaydırılabilir fotoğraf listesi gösterilir. Fotoğraf listesi, galerideki fotoğraf listesini denetlemek için kullandığımız bir AppEngine uygulamasından alınır. Ayrıca galeri uygulamasını Google Code'da açık kaynak projesi olarak yayınladık.

Galeri sayfası

Galerinin arka ucu, Google Fotoğraf Ödülü hashtag'lerinden birini (ör. #megpp ve #travelgpp) içeren gönderileri aramak için Google+ API'yi kullanan bir AppEngine uygulamasıdır. Uygulama, bu gönderileri denetlenmemiş fotoğraflar listesine ekler. İçerik ekibimiz, haftada bir kez denetlenmemiş fotoğrafların listesini inceler ve içerik yönergelerimizi ihlal eden fotoğrafları işaretler. Denetle düğmesine bastıktan sonra, işareti kaldırılan fotoğraflar galeri sayfasında gösterilen fotoğraflar listesine eklenir.

Moderasyon arka ucu

Galeri kullanıcı arayüzü, Google Closure kitaplığı kullanılarak oluşturulmuştur. Galeri widget'ı bir Kapanış bileşenidir. Kaynak dosyanın en üstünde, Closure'a bu dosyanın photographyPrize.Gallery adlı bir bileşen sağladığını ve uygulama tarafından kullanılan Closure kitaplığının bölümlerini gerektirdiğini bildiririz:

goog.provide('photographyPrize.Gallery');

goog.require('goog.debug.Logger');
goog.require('goog.dom');
goog.require('goog.dom.classes');
goog.require('goog.events');
goog.require('goog.net.Jsonp');
goog.require('goog.style');

Galeri sayfasında, AppEngine uygulamasından fotoğraf listesini almak için JSONP kullanan bir JavaScript parçası vardır. JSONP, jsonpcallback("responseValue") gibi görünen bir komut dosyası enjekte eden basit bir kaynak arası JavaScript hilesidir. JSONP ile ilgili işlemleri yapmak için Closure kitaplığındaki goog.net.Jsonp bileşenini kullanıyoruz.

Galeri komut dosyası, fotoğraf listesini inceler ve galeri sayfasında gösterilmeleri için HTML öğeleri oluşturur. Sonsuz kaydırma, pencere kaydırma etkinliğine bağlanarak çalışır ve pencere kaydırma sayfanın alt kısmına yaklaştığında yeni bir fotoğraf grubu yükler. Yeni fotoğraf listesi segmenti yüklendikten sonra galeri komut dosyası, fotoğraflar için öğeler oluşturur ve bunları görüntülemek üzere galeri öğesine ekler.

Resim listesini görüntüleme

Resim listesi görüntüleme yöntemi oldukça basittir. Resim listesini inceler, HTML öğeleri ve +1 düğmeleri oluşturur. Sonraki adım, oluşturulan liste segmentini galerinin ana galeri öğesine eklemektir. Aşağıdaki kodda bazı Closure derleyici kuralları görebilirsiniz. JSDoc yorumundaki tür tanımlarına ve @private görünürlük özelliğine dikkat edin. Özel yöntemlerin adının sonunda alt çizgi (_) bulunur.

/**
 * Displays images in imageList by putting them inside the section element.
 * Edits image urls to scale them down to imageSize x imageSize bounding
 * box.
 *
 * @param {Array.<Object>} imageList List of image objects to show. Retrieved
 *                                   by loadImages.
 * @return {Element} The generated image list container element.
 * @private
 */
photographyPrize.Gallery.prototype.displayImages_ = function(imageList) {
  
  // find the images and albums from the image list
  for (var j = 0; j < imageList.length; j++) {
    // change image urls to scale them to photographyPrize.Gallery.MAX_IMAGE_SIZE
  }

  // Go through the image list and create a gallery photo element for each image.
  // This uses the Closure library DOM helper, goog.dom.createDom:
  // element = goog.dom.createDom(tagName, className, var_childNodes);

  var category = goog.dom.createDom('div', 'category');
  for (var k = 0; k < items.length; k++) {
    var plusone = goog.dom.createDom('g:plusone');
    plusone.setAttribute('href', photoPageUrl);
    plusone.setAttribute('size', 'standard');
    plusone.setAttribute('annotation', 'none');

    var photo = goog.dom.createDom('div', {className: 'gallery-photo'}, ...)
    photo.appendChild(plusone);

    category.appendChild(photo);
  }
  this.galleryElement_.appendChild(category);
  return category;
};

Kaydırma etkinliklerini işleme

Ziyaretçinin sayfayı ne zaman en alta kaydırdığını ve yeni resimler yüklememiz gerektiğini görmek için galeri, pencere nesnesinin kaydırma etkinliğine bağlanır. Tarayıcı uygulamalarındaki farklılıkları gidermek için Closure kitaplığındaki bazı kullanışlı yardımcı işlevleri kullanıyoruz: goog.dom.getDocumentScroll(), mevcut belge kaydırma konumunu içeren bir {x, y} nesnesi döndürür, goog.dom.getViewportSize() pencere boyutunu, goog.dom.getDocumentHeight() ise HTML belgesinin yüksekliğini döndürür.

/**
 * Handle window scroll events by loading new images when the scroll reaches
 * the last screenful of the page.
 *
 * @param {goog.events.BrowserEvent} ev The scroll event.
 * @private
 */
photographyPrize.Gallery.prototype.handleScroll_ = function(ev) {
  var scrollY = goog.dom.getDocumentScroll().y;
  var height = goog.dom.getViewportSize().height;
  var documentHeight = goog.dom.getDocumentHeight();
  if (scrollY + height >= documentHeight - height / 2) {
    this.tryLoadingNextImages_();
  }
};

/**
 * Try loading the next batch of images objects from the server.
 * Only fires if we have already loaded the previous batch.
 *
 * @private
 */
photographyPrize.Gallery.prototype.tryLoadingNextImages_ = function() {
  // ...
};

Resimler yükleniyor

Resimleri sunucudan yüklemek için goog.net.Jsonp bileşenini kullanıyoruz. Sorgulamak için goog.Uri gerekir. Oluşturulduktan sonra, Jsonp sağlayıcıya bir sorgu parametresi nesnesi ve başarı geri çağırma işlevi içeren bir sorgu gönderebilirsiniz.

/**
 * Loads image list from the App Engine page and sets the callback function
 * for the image list load completion.
 *
 * @param {string} tag Fetch images tagged with this.
 * @param {number} limit How many images to fetch.
 * @param {number} offset Offset for the image list.
 * @param {function(Array.<Object>=)} callback Function to call
 *        with the loaded image list.
 * @private
 */
photographyPrize.Gallery.prototype.loadImages_ = function(tag, limit, offset, callback) {
  var jsonp = new goog.net.Jsonp(
      new goog.Uri(photographyPrize.Gallery.IMAGE_LIST_URL));
  jsonp.send({'tag': tag, 'limit': limit, 'offset': offset}, callback);
};

Yukarıda belirtildiği gibi, galeri komut dosyası kodu derlemek ve küçültmek için Closure derleyiciyi kullanır. Closure derleyici, doğru yazım kurallarını zorunlu kılmak için de kullanışlıdır (bir mülkün türünü ayarlamak için yorumlarınızda @type foo JSDoc notasyonu kullanırsınız). Ayrıca, bir yöntemle ilgili yorumunuz olmadığında bunu size bildirir.

Birim testleri

Galeri komut dosyası için birim testlerine de ihtiyacımız vardı. Bu nedenle, Closure kitaplığının yerleşik bir birim test çerçevesine sahip olması çok kullanışlı oldu. jsUnit kurallarına uyduğundan kullanımı kolaydır.

Birim testlerini yazmama yardımcı olması için JavaScript dosyasını ayrıştıran ve galeri bileşenindeki her yöntem ve özellik için başarısız bir birim testi oluşturan küçük bir Ruby komut dosyası yazdım. Aşağıdaki gibi bir komut dosyası verildiğinde:

Foo = function() {}
Foo.prototype.bar = function() {}
Foo.prototype.baz = "hello";

Test oluşturucu, her mülk için boş bir test oluşturur:

function testFoo() {
  fail();
  Foo();
}

function testFooPrototypeBar = function() {
  fail();
  instanceFoo.bar();
}

function testFooPrototypeBaz = function() {
  fail();
  instanceFoo.baz;
}

Bu otomatik olarak oluşturulan testler, kod için test yazmaya kolayca başlamamı sağladı. Ayrıca tüm yöntemler ve özellikler varsayılan olarak ele alındı. Başarısız testler, testleri tek tek incelemem ve uygun testler yazmam gerektiği için güzel bir psikolojik etki yarattı. Kod kapsamı ölçeriyle birlikte, testlerin ve kapsamın tamamının yeşil olması için eğlenceli bir oyundur.

Özet

Gallery+, #hashtag ile eşleşen Google+ fotoğraflarının denetlenmiş bir listesini gösteren açık kaynak bir projedir. Go ve Closure kitaplığı kullanılarak oluşturulmuştur. Arka uç, App Engine'de çalışır. Gallery+, Google Fotoğraf Ödülü web sitesinde gönderim galerisini görüntülemek için kullanılır. Bu makalede, kullanıcı arabirimi komut dosyasının önemli noktalarını ele aldık. App Engine Developer Relations Ekibi'nden iş arkadaşım Johan Euphrosine, arka uç uygulamasından bahseden ikinci bir makale yazıyor. Arka uç, Google'ın yeni sunucu tarafı dili olan Go'da yazılmıştır. Go kodunun üretim örneğini görmek istiyorsanız takipte kalın.

Referanslar