2009 年 2 月
簡介
「Ruby 在用戶端程式庫清單中的哪個位置?」
在開發人員的強烈需求和 Ruby on Rails (RoR) 的持續熱潮推動下,我的同事 Jeff Fisher 從末日火山的深處打造了 Ruby 公用程式庫。請注意,這並非完整的用戶端程式庫,但可處理驗證和基本 XML 操作等基本事項。此外,您還必須使用 REXML 模組和 XPath,直接處理 Atom 動態饋給。
觀眾
本文適用於有興趣使用 Ruby (特別是 Ruby on Rails) 存取 Google Data API 的開發人員。本文假設讀者已熟悉 Ruby 程式設計語言和 Rails 網路開發架構。在大部分的範例中,我著重於 Documents List API,但同樣的概念也適用於任何 Data API。
開始使用
需求條件
安裝 Google Data Ruby 公用程式庫
如要取得程式庫,您可以直接從專案主機下載程式庫來源,或是安裝 Gem:
sudo gem install gdata
提示:為確保萬無一失,請執行 gem list --local
,確認 Gem 已正確安裝。
驗證
ClientLogin
ClientLogin 可讓應用程式以程式輔助方式,登入使用者的 Google 或 G Suite 帳戶。驗證使用者憑證後,Google 會核發驗證權杖,供後續 API 要求參照。權杖在一段時間內有效,時間長度取決於您使用的 Google 服務。基於安全考量,並為使用者提供最佳體驗,開發已安裝的電腦應用程式時,請只使用 ClientLogin。如果是網路應用程式,建議使用 AuthSub 或 OAuth。
Ruby 程式庫為每個 API 提供一個用戶端類別。舉例來說,請使用下列程式碼片段登入 user@gmail.com
文件清單資料 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 的缺點之一是,應用程式可能會在登入嘗試失敗時收到人機驗證問題。如果發生這種情況,您可以呼叫 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 和 AuthSubRevokeToken,用於管理權杖。AuthSubTokenInfo
可用來檢查權杖是否有效。AuthSubRevokeToken
讓使用者選擇停止存取自己的資料。應用程式應使用 AuthSubRevokeToken
,這是最佳做法。Ruby 資料庫支援這兩種方法。
如要查詢權杖的中繼資料:
client.auth_handler.info
如要撤銷工作階段符記:
client.auth_handler.revoke
如要深入瞭解 AuthSub,請參閱完整的「網路應用程式的 AuthSub 驗證」說明文件。
OAuth
撰寫本文時,OAuth 尚未新增至 GData::Auth
模組。
使用 Rails oauth 外掛程式或 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)
建立新的 Rails 應用程式
通常,建立新 Rails 應用程式的第一項練習,就是執行架構產生器來建立 MVC 檔案。
接著執行 rake db:migrate
設定資料庫表格。不過,由於我們的應用程式會查詢 Google 文件清單 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
gem。
最後,我選擇將預設路徑 (「/
」) 連線至 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 聯絡人 API 的呼叫。
結論
建立 Google Data Rails 應用程式最困難的部分,就是設定 Rails!不過,部署應用程式也是非常重要的一環。為此,我強烈建議使用 Apache 的 mod_rails。設定、安裝及執行都非常簡單。你很快就能上手!
資源
- Google Data API 清單
- Google Data Ruby Utility Library 專案頁面
- 文章:搭配使用 Ruby 與 Google Data API
- 下載 Ruby
- 下載 RubyGems 和 Rails
附錄
範例
DocList Manager 是完整的 Ruby on Rails 範例,可展示本文討論的主題。完整原始碼可從專案主機取得。