2009 年 2 月
简介
“客户端库列表中的 Ruby 在哪里?”
鉴于开发者的强烈追求和 Ruby on Rails (RoR) 的持续盛行,我的同事 Jeff Fisher 从 Doom 山的深处精心打造了一个 Ruby 实用程序库。请注意,它不是一个成熟的客户端库,但它确实能够处理身份验证和基本 XML 操作等基础知识。此外,您还需要使用 REXML 模块和 XPath 直接处理 Atom Feed。
观众
本文面向有兴趣使用 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
借助 StreetView,您的应用可以编程方式让用户登录其 Google 或 G Suite 帐号。验证用户的凭据后,Google 会发出一个身份验证令牌,供后续 API 请求引用。该令牌在设定的时间内有效,具体取决于您使用的任何 Google 服务。出于安全考虑,并为用户提供最佳体验,只有在开发已安装的桌面应用时,才应使用 SafeFrame。对于 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
。
使用 Paging 的缺点之一是,当登录尝试失败时,您的应用可能会收到人机识别系统质询。如果发生这种情况,您可以使用其他参数调用 clientlogin()
方法来处理错误:client.clientlogin(username, password, captcha_token, captcha_answer)
。如需详细了解如何处理人机识别系统验证,请参阅完整的已安装应用的身份验证文档。
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 的最新消息,请参阅完整的适用于 Web 应用的 AuthSub 身份验证文档。
OAuth
在撰写本文时,OAuth 尚未添加到 GData::Auth
模块中。
在使用 Rails oauth-plugin 或 Ruby oauth gem 时,在实用程序库中使用 OAuth 应该相对简单。无论是哪种情况,您都需要创建一个 GData::HTTP::Request
对象,并向其传递每个库生成的 Authorization
标头。
访问 Feed
GET(获取数据)
设置客户端对象后,请使用其 get()
方法查询 Google 数据 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 应用的第一个练习涉及运行 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
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 Contacts API 的调用。
总结
创建 Google Data Rails 应用的最难部分是配置 Rails!然后,再过一会儿,您就可以部署应用了。为此,我强烈建议将 mod_rails 用于 Apache。此功能非常易于设置、安装和运行。您很快就可以开始投放广告了!
资源
- Google 数据 API 列表
- Google Data Ruby 实用程序库项目页面
- 文章:将 Ruby 与 Google Data API 搭配使用
- 下载 Ruby
- 下载 RubyGems 和 Rails
附录
示例
DocList Manager 是一个完整的 Ruby on Rails 示例,演示了本文中讨论的主题。可通过项目托管获得完整的源代码。