Google Fotoğrafçılık Ödülü galerisi

Ilmari Heikkinen

Google Fotoğrafçılık Ödülü web sitesi

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

Galeri sayfası

Galerinin arka ucu, Google+ API'yi kullanarak Google Fotoğrafçılık Ödülü hashtag'lerinden birini (ör. #megpp ve #travelgpp) içeren yayınları arayan bir AppEngine uygulamasıdır. Uygulama daha sonra bu yayınları denetlenmeyen fotoğraflar listesine ekler. İçerik ekibimiz, haftada bir kez denetlenmemiş fotoğrafların listesini inceler ve içerik yönergelerimizi ihlal edenleri işaretler. "Denetle" düğmesini tıkladıktan sonra, işaretlenmemiş fotoğraflar galeri sayfasında gösterilen fotoğraflar listesine eklenir.

Moderasyon arka ucu

Galeri ön ucu, Google Closure kitaplığı kullanılarak oluşturulmuştur. Galeri widget'ının kendisi bir Closure bileşenidir. Kaynak dosyanın en üstünde, Closure'a bu dosyanın photographyPrize.Gallery adlı bir bileşen sağladığını bildirir ve uygulama tarafından kullanılan Closure kitaplığının bölümlerini isteriz:

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 bulunur. JSONP, jsonpcallback("responseValue") gibi görünen bir komut dosyası ekleyen basit bir çapraz kaynak 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österilecek HTML öğeleri oluşturur. Sonsuz kaydırma, pencere kaydırma etkinliğine bağlanarak ve pencere kaydırma sayfanın alt kısmına yaklaştığında yeni bir fotoğraf grubu yükleyerek ç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österiliyor

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ını görebilirsiniz. JSDoc yorumundaki tür tanımlarına ve @private görünürlüğüne dikkat edin. Özel yöntemlerin adı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 yönetme

Ziyaretçinin sayfayı ne zaman aşağı 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ı hizmet işlevlerini kullanıyoruz: goog.dom.getDocumentScroll(), geçerli belgenin kaydırma konumunu içeren bir {x, y} nesnesi döndürür, goog.dom.getViewportSize() pencere boyutunu döndürür ve goog.dom.getDocumentHeight() 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, bir sorgu parametresi nesnesi ve başarılı geri çağırma işleviyle JSONP sağlayıcısına 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 sadeleştirmek için Closure derleyicisini kullanır. Closure derleyicisi, doğru yazımı zorunlu kılmak için de kullanışlıdır (bir özelliğin türünü ayarlamak için yorumlarınızda @type foo JSDoc notasyonunu kullanırsınız) ve bir yöntem için yorumunuz olmadığında da sizi bilgilendirir.

Birim testleri

Galeri komut dosyası için birim testlerine de ihtiyacımız vardı. Bu nedenle, Closure kitaplığında yerleşik bir birim testi çerçevesi olması işimize yaradı. jsUnit kurallarına uyduğundan kullanmaya başlamak 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, ö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 olarak oluşturulan bu testler, kod için test yazmaya kolayca başlamamı sağladı. Ayrıca tüm yöntemler ve özellikler varsayılan olarak kapsandı. Başarısız testler, testleri tek tek inceleyip doğru testler yazmamı gerektiren hoş bir psikolojik etki yaratıyor. Kod kapsamı ölçerle birlikte kullanıldığında, testleri ve kapsamı tamamen yeşil yapmak eğlenceli bir oyuna dönüşür.

Özet

Gallery+, bir #hashtag ile eşleşen Google+ fotoğraflarının denetlenmiş bir listesini görüntüleyen açık kaynaklı bir projedir. Go ve Closure kitaplığı kullanılarak oluşturulmuştur. Arka uç, App Engine'de çalışır. Gallery+, Google Fotoğrafçılık Ödülü web sitesinde gönderim galerisini görüntülemek için kullanılır. Bu makalede, ön uç komut dosyasının önemli kısımlarını inceledik. App Engine Geliştirici İlişkileri ekibinden meslektaşım Johan Euphrosine, arka uç uygulaması hakkında 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 bizi takip etmeye devam edin.

Referanslar