2009 年 2 月
简介
“为什么 客户端库列表中没有 Ruby?”
在开发者对 Ruby on Rails (RoR) 的强烈需求和持久热度的推动下,我的同事 Jeff Fisher 从末日火山的炽热深处打造了一个 Ruby 实用程序库。请注意,它不是一个完整的客户端库,但确实可以处理身份验证和基本 XML 操作等基本任务。它还要求您使用 REXML 模块和 XPath 直接处理 Atom Feed。
受众群体
本文面向有兴趣使用 Ruby(尤其是 Ruby on Rails)访问 Google Data API 的开发者。 本教程假定读者对 Ruby 编程语言和 Rails Web 开发框架有一定的了解。在大多数示例中,我重点介绍 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。对于 Web 应用,建议使用 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 的缺点之一是,如果登录尝试失败,您的应用可能会收到人机识别系统挑战。如果发生这种情况,您可以调用 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 Web 应用身份验证文档。
OAuth
在撰写本文时,OAuth 尚未添加到 GData::Auth
模块。
使用 Rails oauth-plugin 或 Ruby oauth gem 时,在实用程序库中使用 OAuth 应该相对简单。无论哪种情况,您都需要创建一个 GData::HTTP::Request
对象,并向其传递由每个库生成的 Authorization
标头。
访问 Feed
GET(提取数据)
设置客户端对象后,使用其 get()
方法查询 Google Data Feed。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()
方法。以下示例将更新文档的标题。
它假定您拥有之前查询的 Feed。
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 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
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 %>
启动 Web 服务器并开始开发
现在,您应该可以通过调用 ruby script/server
来启动默认 Web 服务器了。如果一切正常,将浏览器指向 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 示例,演示了本文讨论的主题。您可以从项目托管服务获取完整源代码。