Google Veri API'leri ile Ruby'yi kullanma

Jochen Hartmann, Google Data APIs Team
Nisan 2008

Giriş

Ruby, popüler Rails web geliştirme çerçevesi sayesinde son yıllarda oldukça ilgi gören dinamik bir komut dosyası dilidir. Bu makalede, Google Veri API hizmetleriyle etkileşim kurmak için Ruby'nin nasıl kullanılacağı açıklanmaktadır. Rails'e odaklanmayacağız. Bunun yerine, feed'lerimizin temel HTTP komutlarını ve yapısını açıklamaya daha çok önem veriyoruz. Burada sunulan tüm örnekler, Ruby'nin etkileşimli kabuğu olan irb kullanılarak komut satırından takip edilebilir.

cURL makalesinden hatırlayacağınız gibi, Google Veri API'leri web kaynaklarını temsil etmek, oluşturmak ve güncellemek için Atom Yayınlama Protokolü 'nü kullanır. Bu protokolün avantajı, istekleri formüle etmek için standart HTTP fiillerinin kullanılması ve standart HTTP durum kodlarıyla yanıtlanmasıdır.

Bu makalede kullanacağımız fiiller; içeriği almak için GET, yeni içerik yüklemek için POST ve mevcut içeriği güncellemek için PUT'tur. Google Veri API'lerini kullanırken karşılaşabileceğiniz bazı standart kodlar arasında feed veya giriş alma işleminin başarılı olduğunu gösteren 200 ya da kaynağın başarılı bir şekilde oluşturulduğunu veya güncellendiğini gösteren 201 yer alır. Bir hata oluşursa (ör. hatalı biçimlendirilmiş bir istek gönderildiğinde) 400 kodu ("Hatalı İstek" anlamına gelir) geri gönderilir. Yanıt gövdesinde, tam olarak neyin yanlış gittiğini açıklayan daha ayrıntılı bir mesaj yer alır.

Ruby, "Net" modülünün bir parçası olarak güzel bir hata ayıklama seçeneği sunar. Bu kod örneklerini makul ölçüde kısa tutmak için burada etkinleştirmedim.

Ruby'yi edinme ve yükleme

Linux kullanıyorsanız Ruby'yi çoğu paket yönetim sistemini kullanarak yükleyebilirsiniz. Diğer işletim sistemleri ve kaynak kodunun tamamını edinmek için lütfen http://www.ruby-lang.org/en/downloads/ adresini ziyaret edin. Bu örneklerde kullanacağımız etkileşimli kabuk olan Irb, varsayılan olarak yüklü olmalıdır. Burada listelenen kod örneklerini takip etmek için XML'yi Ruby veri yapılarına ayrıştırmak üzere kullanılan küçük bir kitaplık olan XmlSimple'yı da yüklemeniz gerekir. XmlSimple'ı edinmek/yüklemek için lütfen http://xml-simple.rubyforge.org adresini ziyaret edin.

Makinenizde Ruby'nin bir kopyası çalıştıktan sonra, Google'ın veri hizmetlerine temel istekler göndermek için Net:HTTP paketini kullanabilirsiniz. Aşağıdaki snippet, Ruby'nin etkileşimli kabuğundan gerekli içe aktarma işlemlerinin nasıl yapılacağını gösterir. Yaptığımız işlem, "net/http" paketini gerektirmek, YouTube'daki en yüksek puanlı video feed'inin URL'sini ayrıştırmak ve ardından bir HTTP GET isteği gerçekleştirmektir.

irb(main):001:0> require 'net/http'
=> true
irb(main):002:0> youtube_top_rated_videos_feed_uri = \
'http://gdata.youtube.com/feeds/api/standardfeeds/top_rated'
=> "http://gdata.youtube.com/feeds/api/standardfeeds/top_rated"
irb(main):003:0> uri = \
URI.parse(youtube_top_rated_videos_feed_uri)
=> #<URI::HTTP:0xfbf826e4 URL:http://gdata.youtube.com/feeds/api/standardfeeds/top_rated>

irb(main):004:0> uri.host
=> "gdata.youtube.com"
irb(main):005:0> Net::HTTP.start(uri.host, uri.port) do |http|
irb(main):006:1* puts http.get(uri.path)
irb(main):007:1> end
#<Net::HTTPOK:0xf7ef22cc>

Bu istek, komut satırına oldukça fazla XML yansıtmalıdır. Tüm öğelerin bir <feed> öğesi içinde yer aldığını ve <entry> öğeleri olarak adlandırıldığını fark etmiş olabilirsiniz. Şimdilik XML biçimlendirmesiyle ilgili endişelenmeyelim. Yalnızca HTTP kullanarak temel bir Google Data API isteğinin nasıl yapılacağını açıklamak istedim. Gönderip alabileceğimiz bilgiler daha "komut satırı dostu" olduğundan, şimdi API'leri değiştirip E-Tablolar'a odaklanacağız.

Kimlik doğrulama | Google E-Tablolar API'sini kullanma

Yine giriş öğelerinin feed'ini alarak başlayacağız. Ancak bu kez kendi elektronik tablolarımızla çalışmak istiyoruz. Bunu yapabilmek için önce Google Hesapları hizmetinde kimliğimizi doğrulamamız gerekir.

GData Kimlik Doğrulaması ile ilgili dokümanlardan hatırlayacağınız gibi, Google'ın hizmetlerinde kimlik doğrulamanın iki yolu vardır. AuthSub, web tabanlı uygulamalar için kullanılır ve kısaca jeton değişimi sürecini içerir. AuthSub'ın asıl avantajı, uygulamanızın kullanıcı kimlik bilgilerini depolaması gerekmemesidir. ClientLogin, "yüklü" uygulamalar içindir. ClientLogin sürecinde, kullanıcı adı ve şifre, kullanmak istediğiniz hizmeti tanımlayan bir dizeyle birlikte https üzerinden Google'ın hizmetlerine gönderilir. Google E-Tablolar API hizmeti, wise dizesiyle tanımlanır.

Etkileşimli kabuğumuza geri dönerek Google ile kimlik doğrulama yapalım. Kimlik doğrulama isteğimizi ve kimlik bilgilerimizi göndermek için https kullandığımızı unutmayın:

irb(main):008:0> require 'net/https'
=> true
irb(main):009:0> http = Net::HTTP.new('www.google.com', 443)
=> #<Net::HTTP www.google.com:443 open=false>
irb(main):010:0> http.use_ssl = true
=> true
irb(main):011:0> path = '/accounts/ClientLogin'
=> "/accounts/ClientLogin"

# Now we are passing in our actual authentication data. 
# Please visit OAuth For Installed Apps for more information 
# about the accountType parameter
irb(main):014:0> data = \
irb(main):015:0* 'accountType=HOSTED_OR_GOOGLE&Email=your email' \
irb(main):016:0* '&Passwd=your password' \
irb(main):017:0* '&service=wise'

=> accountType=HOSTED_OR_GOOGLE&Email=your email&Passwd=your password&service=wise"

# Set up a hash for the headers
irb(main):018:0> headers = \
irb(main):019:0* { 'Content-Type' => 'application/x-www-form-urlencoded'}
=> {"Content-Type"=>"application/x-www-form-urlencoded"}

# Post the request and print out the response to retrieve our authentication token
irb(main):020:0> resp, data = http.post(path, data, headers)
warning: peer certificate won't be verified in this SSL session
=> [#<Net::HTTPOK 200 OK readbody=true>, "SID=DQAAAIIAAADgV7j4F-QVQjnxdDRjpslHKC3M ... [ snipping out the rest of the authentication strings ]

# Strip out our actual token (Auth) and store it
irb(main):021:0> cl_string = data[/Auth=(.*)/, 1]
=> "DQAAAIUAAADzL... [ snip ]

# Build our headers hash and add the authorization token
irb(main):022:0> headers["Authorization"] = "GoogleLogin auth=#{cl_string}"
=> "GoogleLogin auth=DQAAAIUAAADzL... [ snip ]

Tamam. Kimliğimiz doğrulandığına göre, şimdi de aşağıdaki isteği kullanarak kendi elektronik tablolarımızı almaya çalışalım:

http://spreadsheets.google.com/feeds/spreadsheets/private/full

Bu kimliği doğrulanmış bir istek olduğundan üstbilgilerimizi de iletmek istiyoruz. Aslında, çeşitli feed'ler için birkaç istekte bulunacağımızdan bu işlevi get_feed olarak adlandıracağımız basit bir işlevde sarmalayabiliriz.

# Store the URI to the feed since we may want to use it again
irb(main):023:0> spreadsheets_uri = \
irb(main):024:0* 'http://spreadsheets.google.com/feeds/spreadsheets/private/full'

# Create a simple method to obtain a feed
irb(main):025:0> def get_feed(uri, headers=nil)
irb(main):026:1> uri = URI.parse(uri)
irb(main):027:1> Net::HTTP.start(uri.host, uri.port) do |http|
irb(main):028:2* return http.get(uri.path, headers)
irb(main):029:2> end
irb(main):030:1> end
=> nil

# Lets make a request and store the response in 'my_spreadsheets'
irb(main):031:0> my_spreadsheets = get_feed(spreadsheets_uri, headers)
=> #<Net::HTTPOK 200 OK readbody=true>

irb(main):032:0> my_spreadsheets
=> #<Net::HTTPOK 200 OK readbody=true>

# Examine our XML (showing only an excerpt here...)
irb(main):033:0> my_spreadsheets.body
=> "<?xml version='1.0' encoding='UTF-8'?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/'>
<id>http://spreadsheets.google.com/feeds/spreadsheets/private/full</id><updated>2008-03-20T20:49:39.211Z</updated>
<category scheme='http://schemas.google.com/spreadsheets/2006' term='http://schemas.google.com/spreadsheets/2006#spreadsheet'/>
<title type='text'>Available Spreadsheets - [email protected]</title><link rel='alternate' type='text/html' href='http://docs.google.com'/>
<link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://spreadsheets.google.com/feeds/spreadsheets/private/full'/><link rel='self' type='application/atom+xml' href='http://spreadsheets.google.com/feeds/spreadsheets/private/full?tfe='/>
<openSearch:totalResults>6</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><entry>
<id>http://spreadsheets.google.com/feeds/spreadsheets/private/full/o04927555739056712307.4365563854844943790</id><updated>2008-03-19T20:44:41.055Z</updated><category scheme='http://schemas.google.com/spreadsheets/2006' term='http://schemas.google.com/spreadsheets/2006#spreadsheet'/><title type='text'>test02</title><content type='text'>test02</content><link rel='http://schemas.google.com/spreadsheets/2006#worksheetsfeed' type='application/atom+xml' href='http://spreadsheets.google.com/feeds/worksheets/o04927555739056712307.4365563854844943790/private/full'/><link rel='alternate' type='text/html' href='http://spreadsheets.google.com/ccc?key=o04927555739056712307.4365563854844943790'/><link rel='self' type='application/atom+xml' href='http://spreadsheets.google.com/feeds/spreadsheets/private/full/o04927555739056712307.4365563854844943790'/><author><name>test.api.jhartmann</name><email>[email protected]</email></author></entry><entry> ...

Yine çok fazla XML görüyoruz. Komut satırından çözmeniz gerekmediği için yukarıda bu XML'lerin önemini azalttık. İşleri daha kullanıcı dostu hale getirmek için bunun yerine XmlSimple kullanarak bir veri yapısına ayrıştırmamıza izin verin:

# Perform imports
irb(main):034:0> require 'rubygems'
=> true
irb(main):035:0> require 'xmlsimple'
=> true
irb(main):036:0> doc = \
irb(main):037:0* XmlSimple.xml_in(my_spreadsheets.body, 'KeyAttr' => 'name')

# Import the 'pp' module for 'pretty printing'
irb(main):038:0> require 'pp'
=> true

# 'Pretty-print' our XML document
irb(main):039:0> pp doc
{"totalResults"=>["6"],
 "category"=>
  [{"term"=>"http://schemas.google.com/spreadsheets/2006#spreadsheet",
    "scheme"=>"http://schemas.google.com/spreadsheets/2006"}],
 "title"=>
  [{"type"=>"text",
    "content"=>"Available Spreadsheets - Test-account"}],
 "startIndex"=>["1"],
 "id"=>["http://spreadsheets.google.com/feeds/spreadsheets/private/full"],
 "entry"=>
  [{"category"=>
     [{"term"=>"http://schemas.google.com/spreadsheets/2006#spreadsheet",
       "scheme"=>"http://schemas.google.com/spreadsheets/2006"}],
    "title"=>[{"type"=>"text", "content"=>"blank"}],
    "author"=>
     [{"name"=>["Test-account"],
       "email"=>["my email"]}],
    "id"=>
     ["http://spreadsheets.google.com/feeds/spreadsheets/private/full/o04927555739056712307.3387874275736238738"],
    "content"=>{"type"=>"text", "content"=>"blank"},
    "link"=>
    [ snipping out the rest of the XML ]

Çalışma sayfalarını edinme

Dolayısıyla, yukarıdaki çıktıda görebileceğiniz gibi feed'imde 6 e-tablo var. Bu makalenin kısa olması için yukarıdaki XML çıktısının geri kalanını (diğer listelemelerin çoğunda olduğu gibi) kestim. Bu e-tabloyu daha ayrıntılı bir şekilde incelemek için birkaç adım daha uygulamamız gerekiyor:

  1. E-tablo anahtarını edinme
  2. Çalışma sayfası feed'imizi almak için e-tablo anahtarını kullanın
  3. Kullanmak istediğimiz çalışma sayfasının kimliğini alın.
  4. Çalışma sayfasının gerçek içeriğine erişmek için cellsFeed veya listFeed isteğinde bulunun.

Bu işlem çok zahmetli gibi görünse de birkaç basit yöntem yazarak aslında ne kadar kolay olduğunu göstereceğim. cellsFeed ve listFeed, bir çalışma sayfasının gerçek hücre içeriğinin iki farklı gösterimidir. listFeed, bir bilgi satırının tamamını temsil eder ve yeni verilerin POST edilmesi için önerilir. cellFeed, tek tek hücreleri temsil eder ve tek tek hücre güncellemeleri veya birçok tek tek hücreye yönelik toplu güncellemeler için kullanılır (her ikisi de PUT kullanır). Daha ayrıntılı bilgi için lütfen Google Spreadsheets API dokümanlarına bakın.

Öncelikle çalışma sayfası feed'ini almak için e-tablo anahtarını (yukarıdaki XML çıkışında vurgulanmıştır) ayıklamamız gerekir:

# Extract the spreadsheet key from our datastructure
irb(main):040:0> spreadsheet_key = \ 
irb(main):041:0* doc["entry"][0]["id"][0][/full\/(.*)/, 1]
=> "o04927555739056712307.3387874275736238738"

# Using our get_feed method, let's obtain the worksheet feed
irb(main):042:0> worksheet_feed_uri = \ 
irb(main):043:0* "http://spreadsheets.google.com/feeds/worksheets/#{spreadsheet_key}/private/full"
=> "http://spreadsheets.google.com/feeds/worksheets/o04927555739056712307.3387874275736238738/private/full"

irb(main):044:0> worksheet_response = get_feed(worksheet_feed_uri, headers)
=> #<Net::HTTPOK 200 OK readbody=true>

# Parse the XML into a datastructure
irb(main):045:0> worksheet_data = \ 
irb(main):046:0* XmlSimple.xml_in(worksheet_response.body, 'KeyAttr' => 'name')
=> {"totalResults"=>["1"], "category"=>[{"term ... [ snip ]

# And pretty-print it
irb(main):047:0> pp worksheet_data
{"totalResults"=>["1"],
 "category"=>
  [{"term"=>"http://schemas.google.com/spreadsheets/2006#worksheet",
    "scheme"=>"http://schemas.google.com/spreadsheets/2006"}],
 "title"=>[{"type"=>"text", "content"=>"blank"}],
 "author"=>
  [{"name"=>["test.api.jhartmann"],
    "email"=>["[email protected]"]}],
 "startIndex"=>["1"],
 "id"=>
  ["http://spreadsheets.google.com/feeds/worksheets/o04927555739056712307.3387874275736238738/private/full"],
 "entry"=>
  [{"category"=>
     [{"term"=>"http://schemas.google.com/spreadsheets/2006#worksheet",
       "scheme"=>"http://schemas.google.com/spreadsheets/2006"}],
    "title"=>[{"type"=>"text", "content"=>"Sheet 1"}],
    "rowCount"=>["100"],
    "colCount"=>["20"],
    "id"=>
     ["http://spreadsheets.google.com/feeds/worksheets/o04927555739056712307.3387874275736238738/private/full/od6"],
    "content"=>{"type"=>"text", "content"=>"Sheet 1"},
    "link"=>
     [{"href"=>
        "http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full",
       "rel"=>"http://schemas.google.com/spreadsheets/2006#listfeed",
       "type"=>"application/atom+xml"},
      {"href"=>
        "http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full",
       "rel"=>"http://schemas.google.com/spreadsheets/2006#cellsfeed",
       "type"=>"application/atom+xml"},
    [ snip: cutting off the rest of the XML ]

Burada görebileceğiniz gibi, listFeed ve cellsFeed'e erişmek için bağlantıları (highlighted above) bulabiliriz. listFeed'e geçmeden önce, aradığımızı bilmeniz için örnek e-tablomuzda şu anda hangi verilerin bulunduğunu kısaca açıklayayım:

E-tablomuz çok basit ve yalnızca aşağıdaki gibi görünüyor:

languageweb sitesi
javahttp://java.com
phphttp://php.net

Bu verilerin listFeed'deki görünümü aşağıda verilmiştir:

irb(main):048:0> listfeed_uri = \
irb(main):049:0* worksheet_data["entry"][0]["link"][0]["href"]
=> "http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full"

irb(main):050:0> response = get_feed(listfeed_uri, headers)
=> #<Net::HTTPOK 200 OK readbody=true>
irb(main):051:0> listfeed_doc = \ 
irb(main):052:0* XmlSimple.xml_in(response.body, 'KeyAttr' => 'name')
=> {"totalResults"=>["2"], "category"=>[{"term" ... [ snip ]

# Again we parse the XML and then pretty print it
irb(main):053:0> pp listfeed_doc
{"totalResults"=>["2"],
 "category"=>
  [{"term"=>"http://schemas.google.com/spreadsheets/2006#list",
    "scheme"=>"http://schemas.google.com/spreadsheets/2006"}],
 "title"=>[{"type"=>"text", "content"=>"Programming language links"}],
 "author"=>
  [{"name"=>["test.api.jhartmann"],
    "email"=>["[email protected]"]}],
 "startIndex"=>["1"],
 "id"=>
  ["http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full"],
 "entry"=>
  [{"category"=>
     [{"term"=>"http://schemas.google.com/spreadsheets/2006#list",
       "scheme"=>"http://schemas.google.com/spreadsheets/2006"}],
    "language"=>["java"],
    "title"=>[{"type"=>"text", "content"=>"ruby"}],
    "website"=>["http://java.com"],
    "id"=>
     ["http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full/cn6ca"],
    "content"=>
     {"type"=>"text", "content"=>"website: http://java.com"},
    "link"=>
     [{"href"=>
        "http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full/cn6ca",
       "rel"=>"self",
       "type"=>"application/atom+xml"},
      {"href"=>
        "http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full/cn6ca/1j81anl6096",
       "rel"=>"edit",
       "type"=>"application/atom+xml"}],
    "updated"=>["2008-03-20T22:19:51.739Z"]},
   {"category"=>
     [{"term"=>"http://schemas.google.com/spreadsheets/2006#list",
       "scheme"=>"http://schemas.google.com/spreadsheets/2006"}],
    "language"=>["php"],
    "title"=>[{"type"=>"text", "content"=>"php"}],
    "website"=>["http://php.net"],
    "id"=>
     ["http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full/cokwr"],
    "content"=>{"type"=>"text", "content"=>"website: http://php.net"},
    [ snip ]

Gördüğünüz gibi, listFeed, her satır için bir giriş oluşturarak çalışma sayfanızın içeriğini döndürür. E-tablonun ilk satırının hücre başlıklarınızı içerdiğini varsayar ve ardından bu satırdaki verilere göre XML başlıklarını dinamik olarak oluşturur. Gerçek XML'ye bakmak bu durumu daha iyi açıklayacaktır:

<?xml version='1.0' encoding='UTF-8'?><feed [ snip namespaces ]>
<id>http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full</id>
<updated>2008-03-20T22:19:51.739Z</updated>
<category scheme='http://schemas.google.com/spreadsheets/2006' term='http://schemas.google.com/spreadsheets/2006#list'/>

<title type='text'>Programming language links</title>
[ snip: cutting out links and author information ]
<entry>
    <id>http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full/cn6ca</id>
    [ snip: updated and category ]
    <title type='text'>java</title>
    <content type='text'>website: http://java.com</content>
    <link rel='self' type='application/atom+xml' href='http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full/cn6ca'/>
    <link rel='edit' type='application/atom+xml' href='http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full/cn6ca/1j81anl6096'/>
    <gsx:language>java</gsx:language>
    <gsx:website>http://java.com</gsx:website>
</entry>
<entry>
    <id>http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full/cokwr</id>
    [ snip: updated and category ]
    <title type='text'>php</title>
    <content type='text'>website: http://php.net</content>
    <link rel='self' type='application/atom+xml' href='http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full/cokwr'/>
    <link rel='edit' type='application/atom+xml' href='http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full/cokwr/41677fi0nc'/>
    <gsx:language>php</gsx:language>
    <gsx:website>http://php.net</gsx:website>
</entry>
</feed>

Hızlı bir karşılaştırma için aynı bilgilerin hücre feed'inde nasıl gösterildiğine bakalım:

# Extract the cellfeed link
irb(main):054:0> cellfeed_uri = \
irb(main):055:0* worksheet_data["entry"][0]["link"][1]["href"]
=> "http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full"
irb(main):056:0> response = \ 
irb(main):057:0* get_feed(cellfeed_uri, headers)
=> #<Net::HTTPOK 200 OK readbody=true>

# Parse into datastructure and print
irb(main):058:0> cellfeed_doc = \ 
irb(main):059:0* XmlSimple.xml_in(response.body, 'KeyAttr' => 'name')
=> {"totalResults"=>["6"], [ snip ]

irb(main):060:0> pp cellfeed_doc
{"totalResults"=>["6"],
 "category"=>
  [{"term"=>"http://schemas.google.com/spreadsheets/2006#cell",
    "scheme"=>"http://schemas.google.com/spreadsheets/2006"}],
 "title"=>[{"type"=>"text", "content"=>"Programming language links"}],
 "rowCount"=>["101"],
 "colCount"=>["20"],
 "author"=>
  [{"name"=>["test.api.jhartmann"],
    "email"=>["[email protected]"]}],
 "startIndex"=>["1"],
 "id"=>
  ["http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full"],
 "entry"=>
  [{"category"=>
     [{"term"=>"http://schemas.google.com/spreadsheets/2006#cell",
       "scheme"=>"http://schemas.google.com/spreadsheets/2006"}],
    "cell"=>
     [{"col"=>"1",
       "row"=>"1",
       "content"=>"language",
       "inputValue"=>"language"}],
    "title"=>[{"type"=>"text", "content"=>"A1"}],
    "id"=>
     ["http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full/R1C1"],
    "content"=>{"type"=>"text", "content"=>"language"},
    "link"=>
     [{"href"=>
        "http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full/R1C1",
       "rel"=>"self",
       "type"=>"application/atom+xml"},
      {"href"=>
        "http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full/R1C1/8srvbs",
       "rel"=>"edit",
       "type"=>"application/atom+xml"}],
    "updated"=>["2008-03-20T22:19:51.739Z"]},
    [ snip ]

Burada görebileceğiniz gibi, her hücre için bir tane olmak üzere 6 giriş döndürülür. "Dil" kelimesini içeren A1 hücresinin değeri dışındaki tüm çıktıyı kestim. Ayrıca, yukarıda gösterilen düzenle bağlantısına da dikkat edin. Bu bağlantının sonunda bir sürüm dizesi (8srvbs) bulunur. Bu makalenin sonunda yapacağımız gibi, hücre verilerini güncellerken sürüm dizesi önemlidir. Güncellemelerin üzerine yazılmasını önler. Hücre verilerini güncellemek için PUT isteğinde bulunduğunuzda isteğinize hücrenin en son sürüm dizesini eklemeniz gerekir. Her güncellemeden sonra yeni bir sürüm dizesi döndürülür.

listFeed'e içerik yayınlama

İçerik yayınlamak için ihtiyacımız olan ilk şey, listFeed için POST bağlantısıdır. Bu bağlantı, liste feed'i istendiğinde döndürülür. rel özelliğinin değeri olarak http://schemas.google.com/g/2005#post URL'sini içerir. Bu bağlantı öğesini ayrıştırıp href özelliğini çıkarmanız gerekir. Öncelikle, yayınlamayı kolaylaştırmak için küçük bir yöntem oluşturacağız:

irb(main):061:0> def post(uri, data, headers)
irb(main):062:1> uri = URI.parse(uri)
irb(main):063:1> http = Net::HTTP.new(uri.host, uri.port)
irb(main):064:1> return http.post(uri.path, data, headers)
irb(main):065:1> end
=> nil
# Set up our POST url
irb(main):066:0> post_url = \ 
irb(main):067:0* "http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full"
=> "http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full"

# We must use 'application/atom+xml' as MIME type so let's change our headers 
# which were still set to 'application/x-www-form-urlencoded' when we sent our 
# ClientLogin information over https
irb(main):068:0> headers["Content-Type"] = "application/atom+xml"
=> "application/atom+xml"

# Setting up our data to post, using proper namespaces
irb(main):069:0> new_row = \ 
irb(main):070:0* '<atom:entry xmlns:atom="http://www.w3.org/2005/Atom">' << 
irb(main):071:0* '<gsx:language xmlns:gsx="http://schemas.google.com/spreadsheets/2006/extended">' <<
irb(main):072:0* 'ruby</gsx:language>' << 
irb(main):073:0* '<gsx:website xmlns:gsx="http://schemas.google.com/spreadsheets/2006/extended">' <<
irb(main):074:0* 'http://ruby-lang.org</gsx:website>' << 
irb(main):075:0* '</atom:entry>'
=> "<atom:entry xmlns:atom=\"http://www.w3.org/2005/Atom\"><gsx:language ... [ snip ] 

# Performing the post
irb(main):076:0> post_response = post(post_url, new_row, headers) 
=> #<Net::HTTPCreated 201 Created readbody=true>

201 durumu, yayınımızın başarılı olduğunu gösterir.

İçeriği güncellemek için cellsFeed'i kullanma

Belgelerden, hücre feed'inin mevcut içerikte PUT isteklerini tercih ettiğini görüyoruz. Ancak yukarıda cellsFeed'den aldığımız bilgiler yalnızca gerçek e-tablomuzda zaten bulunan veriler olduğundan yeni bilgileri nasıl ekleyebiliriz? Veri girmek istediğimiz her boş hücre için istekte bulunmamız yeterlidir. Aşağıdaki snippet, Python programlama dili hakkında bazı bilgiler eklemek istediğimiz boş hücre R5C1'in (5. satır, 1. sütun) nasıl alınacağını gösterir.

Orijinal değişkenimiz cellfeed_uri yalnızca hücre feed'inin URI'sini içeriyordu. Şimdi düzenlemek istediğimiz hücreyi eklemek ve düzenlememizi yapmak için bu hücrenin sürüm dizesini almak istiyoruz:

# Set our query URI
irb(main):077:0> cellfeed_query = cellfeed_uri + '/R5C1'
=> "http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full/R5C1"

# Request the information to extract the edit link
irb(main):078:0> cellfeed_data = get_feed(cellfeed_query, headers)
=> #<Net::HTTPOK 200 OK readbody=true>
irb(main):079:0> cellfeed_data.body
=> "<?xml version='1.0' encoding='UTF-8'?>
<entry xmlns='http://www.w3.org/2005/Atom' xmlns:gs='http://schemas.google.com/spreadsheets/2006' xmlns:batch='http://schemas.google.com/gdata/batch'>
<id>http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full/R5C1</id>
<updated>2008-03-24T21:55:36.462Z</updated>
<category scheme='http://schemas.google.com/spreadsheets/2006' term='http://schemas.google.com/spreadsheets/2006#cell'/>
<title type='text'>A5</title>
<content type='text'>
</content>
<link rel='self' type='application/atom+xml' href='http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full/R5C1'/>
<link rel='edit' type='application/atom+xml' href='http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full/R5C1/47pc'/>
<gs:cell row='5' col='1' inputValue=''>
</gs:cell>
</entry>"

Yukarıdaki kod listesinde görebileceğiniz gibi, sürüm dizesi 47pc. (En sağa kaydırmanız gerekebilir.) İşleri kolaylaştırmak için, ilgilendiğimiz herhangi bir hücrenin sürüm dizesini elde etmemizi sağlayan bir kolaylık yöntemi oluşturalım:

irb(main):080:0> def get_version_string(uri, headers=nil)
irb(main):081:1> response = get_feed(uri, headers)
irb(main):082:1> require 'rexml/document'
irb(main):083:1> xml = REXML::Document.new response.body
irb(main):084:1> edit_link = REXML::XPath.first(xml, '//[@rel="edit"]')
irb(main):085:1> edit_link_href = edit_link.attribute('href').to_s
irb(main):086:1> return edit_link_href.split(/\//)[10]
irb(main):087:1> end
=> nil

# A quick test
irb(main):088:0> puts get_version_string(cellfeed_query, headers)
47pc
=> nil

Bu sırada, PUT isteğini gerçekleştirecek bir yöntem de yazabiliriz. Hatta daha iyisi, tüm toplu güncellemeyi gerçekleştirecek bir yöntem yazalım. İşlevimiz, aşağıdaki değişkenleri içeren bir karma dizisi alacak:

  • :batch_id - Toplu istekteki her bir parçanın benzersiz tanımlayıcısı.
  • :cell_id: R#C# biçiminde güncellenecek hücrenin kimliği. Burada A1 hücresi R1C1 olarak gösterilir.
  • :data - Eklemek istediğimiz veriler.

irb(main):088:0> def batch_update(batch_data, cellfeed_uri, headers)
irb(main):089:1> batch_uri = cellfeed_uri + '/batch'
irb(main):090:1> batch_request = <<FEED
irb(main):091:1" <?xml version="1.0" encoding="utf-8"?> \
irb(main):092:1" <feed xmlns="http://www.w3.org/2005/Atom" \
irb(main):093:1" xmlns:batch="http://schemas.google.com/gdata/batch" \
irb(main):094:1" xmlns:gs="http://schemas.google.com/spreadsheets/2006" \
irb(main):095:1" xmlns:gd="http://schemas.google.com/g/2005">
irb(main):096:1" <id>#{cellfeed_uri}</id>
irb(main):097:1" FEED
irb(main):098:1> batch_data.each do |batch_request_data|
irb(main):099:2* version_string = get_version_string(cellfeed_uri + '/' + batch_request_data[:cell_id], headers)
irb(main):100:2> data = batch_request_data[:data]
irb(main):101:2> batch_id = batch_request_data[:batch_id]
irb(main):102:2> cell_id = batch_request_data[:cell_id]
irb(main):103:2> row = batch_request_data[:cell_id][1,1]
irb(main):104:2> column = batch_request_data[:cell_id][3,1]
irb(main):105:2> edit_link = cellfeed_uri + '/' + cell_id + '/' + version_string
irb(main):106:2> batch_request<< <<ENTRY
irb(main):107:2" <entry>
irb(main):108:2" <gs:cell col="#{column}" inputValue="#{data}" row="#{row}"/>
irb(main):109:2" <batch:id>#{batch_id}</batch:id>
irb(main):110:2" <batch:operation type="update" />
irb(main):111:2" <id>#{cellfeed_uri}/#{cell_id}</id>
irb(main):112:2" <link href="#{edit_link}" rel="edit" type="application/atom+xml" />
irb(main):113:2" </entry>
irb(main):114:2" ENTRY
irb(main):115:2> end
irb(main):116:1> batch_request << '</feed>'
irb(main):117:1> return post(batch_uri, batch_request, headers)
irb(main):118:1> end
=> nil

# Our sample batch data to insert information about the Python programming language into our worksheet
irb(main):119:0> batch_data = [ \
irb(main):120:0* {:batch_id => 'A', :cell_id => 'R5C1', :data => 'Python'}, \ 
irb(main):121:0* {:batch_id => 'B', :cell_id => 'R5C2', :data => 'http://python.org' } ]
=> [{:cell_id=>"R5C1", :data=>"Python", :batch_id=>"A"}=>{:cell_id=>"R5C2", :data=>"http://python.org", :batch_id=>"B"}]

# Perform the update
irb(main):122:0> response = batch_update(batch_data, cellfeed_uri, headers)
=> #<Net::HTTPOK 200 OK readbody=true>

# Parse the response.body XML and print it
irb(main):123:0> response_xml = XmlSimple.xml_in(response.body, 'KeyAttr' => 'name')
=> [ snip ]

irb(main):124:0> pp response_xml
{"title"=>[{"type"=>"text", "content"=>"Batch Feed"}],
 "xmlns:atom"=>"http://www.w3.org/2005/Atom",
 "id"=>
  ["http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full"],
 "entry"=>
  [{"status"=>[{"code"=>"200", "reason"=>"Success"}],
    "category"=>
     [{"term"=>"http://schemas.google.com/spreadsheets/2006#cell",
       "scheme"=>"http://schemas.google.com/spreadsheets/2006"}],
    "cell"=>
     [{"col"=>"1", "row"=>"5", "content"=>"Python", "inputValue"=>"Python"}],
    "title"=>[{"type"=>"text", "content"=>"A5"}],
    "id"=>
     ["http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full/R5C1",
      "A"],
    "operation"=>[{"type"=>"update"}],
    "content"=>{"type"=>"text", "content"=>"Python"},
    "link"=>
     [{"href"=>
        "http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full/R5C1",
       "rel"=>"self",
       "type"=>"application/atom+xml"},
      {"href"=>
        "http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full/R5C1/49kwzg",
       "rel"=>"edit",
       "type"=>"application/atom+xml"}],
    "updated"=>["2008-03-27T15:48:48.470Z"]},
    [ snip ]

Gördüğünüz gibi, 200 OK yanıt kodunu aldığımız için toplu isteğimiz başarılı oldu. Yanıt XML'si ayrıştırıldığında, response_data dizimizde ayarladığımız her bir :batch_id için ayrı bir mesaj döndürüldüğü görülür. Toplu işleme hakkında daha fazla bilgi için lütfen GData'da Toplu İşleme belgelerine bakın.

Sonuç

Gördüğünüz gibi, Google Veri API'leriyle denemeler yapmak için Ruby'nin etkileşimli kabuğunu kullanmak çok kolaydır. Hem listFeed hem de cellsFeed kullanarak e-tablolarımıza ve çalışma sayfalarımıza erişebildik. Ayrıca, POST isteği kullanarak bazı yeni veriler ekledik ve ardından yalnızca yaklaşık 120 satır kodla toplu güncelleme yapacak yöntemler yazdık. Bu noktadan sonra, bu basit yöntemlerden bazılarını sınıflara sarmak ve yeniden kullanılabilir bir çerçeve oluşturmak zor olmamalıdır.

Bu araçları en sevdiğiniz Google Data API ile kullanma hakkında sorularınız varsa lütfen tartışma gruplarımıza katılın.

Yukarıda ayrıntılı olarak açıklanan kod örneklerini içeren bir sınıf dosyası http://code.google.com/p/google-data-samples-ruby adresinde bulunabilir.

Bu makaleyi tartışın!