Google Photography Prize Galerisi

Ilmari Heikkinen

Google Photography Prize web sitesi

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

Galeri sayfası

Galerinin arka ucu, üzerinde Google Fotoğraf Ödülü hashtag'lerinden birinin (ör. #megpp ve #travelgpp) bulunduğu yayınları aramak için Google+ API'sını kullanan bir AppEngine uygulamasıdır. Daha sonra uygulama bu yayınları denetlenmeyen fotoğraflar listesine ekler. İçerik ekibimiz haftada bir kez, moderatörü olmayan fotoğrafların listesini inceleyip içerik yönergelerimize uymayan fotoğrafları işaretler. Ortala düğmesini tıkladıktan sonra, işaretlenmemiş fotoğraflar, galeri sayfasında gösterilen fotoğraf listesine eklenir.

Moderasyon arka ucu

Galeri ön ucu Google Closure kitaplığı kullanılarak oluşturulur. Galeri widget'ının kendisi bir Kapatma bileşenidir. Kaynak dosyanın üst kısmında 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 belirtiriz:

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 kodu vardır. JSONP, jsonpcallback("responseValue") benzeri bir komut dosyası ekleyen basit bir çapraz kaynak JavaScript saldırısıdır. JSONP işlemlerini işlemek için Closure kitaplığındaki goog.net.Jsonp bileşenini kullanıyoruz.

Galeri komut dosyası, fotoğraf listesini tarar ve fotoğrafların galeri sayfasında gösterilmesi için HTML öğeleri oluşturur. Sonsuz kaydırma, pencere kaydırma etkinliğinin bağlanması ve pencere kaydırma sayfasının alt kısmına yakın olduğunda yeni bir fotoğraf grubunun yüklenmesi yoluyla çalışır. Yeni fotoğraf listesi segmentini yükledikten sonra, galeri komut dosyası fotoğraflar için öğeler oluşturur ve bunları görüntülemek üzere galeri öğesine ekler.

Resim listesi görüntüleniyor

Resim listesi görüntüleme yöntemi oldukça temel bir yöntemdir. Resim listesinde gezinir, 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ını görebilirsiniz. JSDoc açıklamasındaki tür tanımlarını ve @private görünürlüğüne dikkat edin. Gizli yöntemlerin adlarından sonra 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ı ayrıntılı bir şekilde görmek için Kapatma kitaplığındaki bazı kullanışlı yardımcı program işlevlerini kullanıyoruz: goog.dom.getDocumentScroll(), geçerli doküman kaydırma konumuna sahip bir {x, y} nesnesi, goog.dom.getViewportSize(), pencere boyutunu ve goog.dom.getDocumentHeight() HTML dokümanının 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 bir goog.Uri sürer. Oluşturulduktan sonra, Jsonp sağlayıcıya sorgu parametresi nesnesi ve başarılı geri çağırma işleviyle 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 derleyicisini kullanır. Closure derleyicisi, yazımın doğru şekilde yapılabilmesi (bir özelliğin türünü ayarlamak için yorumlarınızda @type foo JSDoc gösteriminin kullanılması) açısından faydalıdır ve bir yöntemle ilgili yorumunuz olmadığında size bilgi verir.

Ünite testleri

Galeri komut dosyası için birim testlerine de ihtiyacımız vardı; dolayısıyla, Kapatma kitaplığında yerleşik bir birim test çerçevesi bulunması yararlı olacaktır. jsUnit kurallarına uyduğundan, kolayca kullanmaya başlayabilirsiniz.

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. Şuna benzer bir komut dosyası verildi:

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

Test oluşturucu, özelliklerin her biri için boş bir test oluşturur:

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

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

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

Otomatik oluşturulan bu testler, kod için test yazma konusunda bana kolay bir başlangıç sağladı. Tüm yöntemler ve özellikler varsayılan olarak karşılandı. Başarısız olan testler güzel bir psikolojik etki yaratıyor. Testlerden tek tek geçmem ve uygun testler yazmam gerekiyor. Kod kapsama sayacı ile birlikte, testlerin ve kapsama alanının tamamen yeşil olmasını sağlayan eğlenceli bir oyun.

Özet

Gallery+, bir #hashtag ile eşleşen Google+ fotoğraflarının denetlenmiş bir listesini görüntülemek için tasarlanmış bir açık kaynak projesidir. Go ve Closure kitaplığı kullanılarak oluşturuldu. Arka uç, App Engine'de çalışır. Gallery+, gönderim galerisini görüntülemek için Google Photography Prize web sitesinde kullanılır. Bu makalede, ön uç komut dosyasının ilginç kısımlarını adım adım işledik. 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. Dolayısıyla, Go kodunun üretim örneğini görmek isterseniz bizi izlemeye devam edin!

Referanslar