2009 年 2 月
簡介
受開發人員的渴望和 Ruby onrail (RoR) 長久的潮流所啟發,我的同事 Jeff Fisher 從 Mount Doom 的火熱深處得以建構出 Ruby 公用程式庫。提醒您,這不是完整的程式碼用戶端程式庫,但可處理驗證和基本 XML 操控等基礎知識。此外,您也需要使用 REXML 模組和 XPath 直接與 Atom 資訊提供搭配使用。
目標對象
本文的適用對象,是有意使用 Ruby (特別是 Ruby on Ram) 存取 Google Data API 的開發人員。 本文假設讀者對於 Ruby 程式設計語言和 Railation 網站開發架構有一定程度的瞭解。針對大多數範例,我會專注於 Documents List API,但相同的概念也可以套用至任何 Data API。
開始使用
需求條件
安裝 Google Data Ruby 公用程式庫
如要取得程式庫,您可以直接從專案託管下載程式庫來源,或安裝 gem:
sudo gem install gdata
提示:為確保正確評估,請在執行 gem list --local
後確認寶石已正確安裝。
驗證
ClientLogin
ClientLogin 可讓應用程式以程式輔助的方式將使用者登入 Google 或 G Suite 帳戶。驗證使用者憑證之後,Google 會發出驗證權杖,供後續 API 要求參考。這組憑證會在指定時間內有效 (視您使用的 Google 服務而定)。基於安全考量,以及為使用者提供最佳體驗,您只應在開發已安裝的桌面應用程式時使用 ClientLogin。針對網路應用程式,建議使用 AuthSub 或 OAuth。
Ruby 程式庫為每個 API 都有一個用戶端類別。例如,使用以下程式碼片段登入 user@gmail.com
到 Documents List Data API:
client = GData::Client::DocList.new client.clientlogin('user@gmail.com', 'pa$$word')
The YouTube Data API would be:
client = GData::Client::YouTube.new client.clientlogin('user@gmail.com', 'pa$$word')
請參閱已實作服務類別的完整清單。
如果服務沒有用戶端類別,請使用 GData::Client::Base
類別。舉例來說,以下程式碼會強制使用者使用 G Suite 帳戶登入。
client_login_handler = GData::Auth::ClientLogin
.new('writely', :account_type => 'HOSTED')
token = client_login_handler.get_token('user@example.com', 'pa$$word', 'google-RailsArticleSample-v1')
client = GData::Client::Base.new(:auth_handler => client_login_handler)
注意:根據預設,程式庫會將 HOSTED_OR_GOOGLE
用於 accountType
。可能的值為 HOSTED_OR_GOOGLE
、HOSTED
或 GOOGLE
。
使用 ClientLogin 的缺點之一,就是應用程式在登入失敗時,可以傳送人機驗證 (Captcha) 驗證問題。如果發生此情況,您可以呼叫 clientlogin()
方法及其額外參數來處理錯誤:client.clientlogin(username, password, captcha_token, captcha_answer)
。如要進一步瞭解如何處理人機驗證 (Captcha),請參閱完整的安裝版應用程式驗證說明文件。
AuthSub
產生 AuthSubRequest 網址
scope = 'http://www.google.com/calendar/feeds/' next_url = 'http://example.com/change/to/your/app' secure = false # set secure = true for signed AuthSub requests sess = true authsub_link = GData::Auth::AuthSub.get_url(next_url, scope, secure, sess)
上一個程式碼區塊會在 authsub_link
中建立下列網址:
https://www.google.com/accounts/AuthSubRequest?next=http%3A%2F%2Fexample.com%2Fchange%2Fto%2Fyour%2Fapp&scope=http%3A%2F%2Fwww.google.com%2Fcalendar%2Ffeeds%2F&session=1&secure=0
您也可以使用用戶端物件的 authsub_url
方法。每個服務類別皆設定了預設的 authsub_scope
屬性,因此您不需要自行指定屬性。
client = GData::Client::DocList.new next_url = 'http://example.com/change/to/your/app' secure = false # set secure = true for signed AuthSub requests sess = true domain = 'example.com' # force users to login to a G Suite hosted domain authsub_link = client.authsub_url(next_url, secure, sess, domain)
上一個程式碼區塊會建立下列網址:
https://www.google.com/accounts/AuthSubRequest?next=http%3A%2F%2Fexample.com%2Fchange%2Fto%2Fyour%2Fapp&scope=http%3A%2F%2Fdocs.google.com%2Ffeeds%2F&session=1&secure=0&hd=example.com
將一次性使用憑證升級至工作階段符記
在使用者授予資料存取權後,AuthSub 會將使用者重新導向回 http://example.com/change/to/your/app?token=SINGLE_USE_TOKEN
。請注意,這個網址只是 next_url
,當中包含單次使用憑證,做為查詢參數附加。
接著,將一次性使用權杖交換成長期工作階段符記:
client.authsub_token = params[:token] # extract the single-use token from the URL query params session[:token] = client.auth_handler.upgrade() client.authsub_token = session[:token] if session[:token]
安全 AuthSub 非常類似,唯一的做法是在升級權杖之前設定私密金鑰:
PRIVATE_KEY = '/path/to/private_key.pem' client.authsub_token = params[:token] client.authsub_private_key = PRIVATE_KEY session[:token] = client.auth_handler.upgrade() client.authsub_token = session[:token] if session[:token]
注意:如要使用安全憑證,請務必在要求單次使用憑證時設定 secure=true
。請參閱上方的產生 AuthSubRequest 網址一節。
管理權杖
AuthSub 提供兩個其他處理常式:AuthSubTokenInfo 和 AuthSub 撤銷 Token 來管理憑證。AuthSubTokenInfo
可用於檢查憑證的有效性。AuthSubRevokeToken
可讓使用者決定其資料存取權。您的應用程式應使用 AuthSubRevokeToken
做為最佳做法。Ruby 程式庫支援這兩種方法。
如何查詢憑證的中繼資料:
client.auth_handler.info
如何撤銷工作階段符記:
client.auth_handler.revoke
如需 AuthSub 的完整資訊,請參閱網路應用程式的 AuthSub 驗證說明文件。
OAuth
撰寫本文時,系統尚未將 OAuth 新增至 GData::Auth
模組。
使用 Azure oauth-plugin 或 Ruby oauth Gem 時,在公用程式庫中使用 OAuth 相對簡單。無論是哪一種情況,您都必須建立 GData::HTTP::Request
物件並傳遞至每個程式庫產生的 Authorization
標頭。
存取資訊提供
GET (擷取資料)
設定用戶端物件後,請使用其 get()
方法查詢 Google 資料動態饋給。XPath 可用於擷取特定的 Atom 元素。擷取使用者 Google 文件的範例如下:
feed = client.get('http://docs.google.com/feeds/documents/private/full').to_xml feed.elements.each('entry') do |entry| puts 'title: ' + entry.elements['title'].text puts 'type: ' + entry.elements['category'].attribute('label').value puts 'updated: ' + entry.elements['updated'].text puts 'id: ' + entry.elements['id'].text # Extract the href value from each <atom:link> links = {} entry.elements.each('link') do |link| links[link.attribute('rel').value] = link.attribute('href').value end puts links.to_s end
POST (建立新資料)
使用用戶端的 post()
方法在伺服器上建立新資料。以下範例會將 new_writer@example.com
新增為 ID 為 doc_id
的文件的協作者。
# Return documents the authenticated user owns feed = client.get('http://docs.google.com/feeds/documents/private/full/-/mine').to_xml entry = feed.elements['entry'] # first <atom:entry> acl_entry = <<-EOF <entry xmlns="http://www.w3.org/2005/Atom" xmlns:gAcl='http://schemas.google.com/acl/2007'> <category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/acl/2007#accessRule'/> <gAcl:role value='writer'/> <gAcl:scope type='user' value='new_writer@example.com'/> </entry> EOF # Regex the document id out from the full <atom:id>. # http://docs.google.com/feeds/documents/private/full/document%3Adfrk14g25fdsdwf -> document%3Adfrk14g25fdsdwf doc_id = entry.elements['id'].text[/full\/(.*%3[aA].*)$/, 1] response = client.post("http://docs.google.com/feeds/acl/private/full/#{doc_id}", acl_entry)
PUT (更新資料)
如要更新伺服器上的資料,請使用用戶端的 put()
方法。以下範例將會更新文件的標題。
並假設您有上一個查詢的資訊提供。
entry = feed.elements['entry'] # first <atom:entry> # Update the document's title entry.elements['title'].text = 'Updated title' entry.add_namespace('http://www.w3.org/2005/Atom') entry.add_namespace('gd','http://schemas.google.com/g/2005') edit_uri = entry.elements["link[@rel='edit']"].attributes['href'] response = client.put(edit_uri, entry.to_s)
刪除
如要從伺服器中刪除 <atom:entry> 或其他資料,請使用 delete()
方法。
以下範例會刪除文件。程式碼假設您有先前查詢的文件。
entry = feed.elements['entry'] # first <atom:entry> edit_uri = entry.elements["link[@rel='edit']"].attributes['href'] client.headers['If-Match'] = entry.attribute('etag').value # make sure we don't nuke another client's updates client.delete(edit_uri)
建立新的鐵路應用程式
通常在建立新的 Map 應用程式時,需要先執行 Scaffold 產生器來建立 MVC 檔案。之後,資料庫就會執行 rake db:migrate
來設定資料庫資料表。不過,由於我們的應用程式會向 Google Documents List API 查詢資料,因此幾乎不需要一般鷹架或資料庫。請改為建立新的應用程式和簡易控制器:
rails doclist cd doclist ruby script/generate controller doclist
並對 config/environment.rb
進行下列變更:
config.frameworks -= [ :active_record, :active_resource, :action_mailer ] config.gem 'gdata', :lib => 'gdata'
第一行會從應用程式取消掛載 ActiveRecord
。第二行會在啟動時載入 gdata
寶石。
最後,我選擇將預設路徑 (/
) 連結至 DoclistController
中的 documents
動作。在 config/routes.rb
中加入這一行:
map.root :controller => 'doclist', :action => 'all'
啟動控制器
由於我們並未產生鷹架,因此請在 app/controllers/doclist_controller.rb
中的 DoclistController
中手動新增名為 all
的動作。
class DoclistController < ApplicationController def all @foo = 'I pity the foo!' end end
並在 app/views/doclist/
下建立 all.html.erb
:
<%= @foo %>
啟動網路伺服器並開始開發
您現在應該可以叫用 ruby script/server
來啟動預設網路伺服器。如果一切順利,將瀏覽器指向 http://localhost:3000/
時,應該會顯示「I pity the foo!
」。
提示:別忘了移除或重新命名 public/index.html
。
完成工作後,請查看我最後的 DoclistController
和 ApplicationController
做為 DocList Manager 專案的肉類。建議您也查看 ContactsController
,其會處理對 Google Contacts API 的呼叫。
結語
建立 Google Datarail 應用程式的最困難部分,就是設定 Rail!不過,即將完成應用程式的部署。因此,我們強烈建議 Apache 的 mod_rails。設定、安裝和執行程序非常簡單。您很快就能開始使用。
資源
- Google Data API 清單
- Google Data Ruby 公用程式庫專案頁面
- 文章:搭配 Google Data API 使用 Ruby
- 下載 Ruby
- 下載 RubyGems 和 Rail
附錄
範例
DocList Manager 是完整的 Ruby onrail 範例,用於示範本文所討論的主題。完整原始碼可從專案託管取得。