Google Data on Rails

Eric Bidelman、Google Data APIs チーム
2009 年 2 月

はじめに

クライアント ライブラリのリストに Ruby はありますか?」

デベロッパーの皆様の強い要望と Ruby on Rails(RoR)の根強い人気に後押しされ、同僚の Jeff Fisher が滅びの山の奥深くから Ruby ユーティリティ ライブラリを鍛え上げました。完全なクライアント ライブラリではありませんが、認証や基本的な XML 操作などの基本事項は処理します。また、REXML モジュールと XPath を使用して Atom フィードを直接操作する必要があります。

オーディエンス

この記事は、Ruby(特に Ruby on Rails)を使用して Google Data APIs にアクセスすることに関心のあるデベロッパーを対象としています。読者が 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 を 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)

: デフォルトでは、ライブラリは accountTypeHOSTED_OR_GOOGLE を使用します。指定可能な値は HOSTED_OR_GOOGLEHOSTEDGOOGLE です。

ClientLogin を使用するデメリットの 1 つは、ログイン試行が失敗した場合に、アプリケーションに CAPTCHA チャレンジが送信される可能性があることです。その場合は、追加のパラメータ client.clientlogin(username, password, captcha_token, captcha_answer) を指定して clientlogin() メソッドを呼び出すことで、エラーを処理できます。CAPTCHA の処理について詳しくは、インストール済みアプリケーションの認証のドキュメントをご覧ください。

AuthSub

AuthSubRequest URL を生成する

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 に次の URL を作成します。

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)

上記のコードブロックは、次の URL を作成します。

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

1 回限りのトークンをセッション トークンにアップグレードする

ユーザーがデータへのアクセスを許可すると、AuthSub はユーザーを http://example.com/change/to/your/app?token=SINGLE_USE_TOKEN にリダイレクトします。URL は、next_url に 1 回限りのトークンがクエリ パラメータとして追加されたものです。

次に、使い捨てトークンを有効期間の長いセッション トークンと交換します。

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]

Secure 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 URL の生成をご覧ください。

トークン管理

AuthSub は、トークンを管理するための AuthSubTokenInfoAuthSubRevokeToken の 2 つの追加ハンドラを提供します。AuthSubTokenInfo は、トークンの有効性を確認するのに役立ちます。AuthSubRevokeToken を使用すると、ユーザーはデータへのアクセスを停止できます。アプリでは、ベスト プラクティスとして AuthSubRevokeToken を使用する必要があります。どちらの方法も Ruby ライブラリでサポートされています。

トークンのメタデータをクエリするには:

client.auth_handler.info

セッション トークンを取り消すには:

client.auth_handler.revoke

AuthSub の詳細については、ウェブ アプリケーション用の AuthSub 認証の完全なドキュメントをご覧ください。

OAuth

この記事の執筆時点では、OAuth は GData::Auth モジュールに追加されていません。

Rails の oauth-plugin または Ruby の oauth gem を使用する場合、ユーティリティ ライブラリで OAuth を使用するのは比較的簡単です。いずれの場合も、GData::HTTP::Request オブジェクトを作成し、各ライブラリで生成された Authorization ヘッダーを渡す必要があります。

フィードへのアクセス

GET(データの取得)

クライアント オブジェクトを設定したら、その get() メソッドを使用して Google Data フィードをクエリします。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() メソッドを使用して、サーバー上に新しいデータを作成します。次の例では、ID が doc_id のドキュメントに共同編集者として new_writer@example.com を追加します。

# 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 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'

1 行目は、アプリケーションから ActiveRecord をフック解除します。2 行目は、起動時に gdata gem を読み込みます。

最後に、デフォルト ルート(「/」)を DoclistControllerdocuments アクションに接続することにしました。次の行を config/routes.rb に追加します。

map.root :controller => 'doclist', :action => 'all'

コントローラを起動する

スキャフォールディングを生成していないため、app/controllers/doclist_controller.rbDoclistController に「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 の削除または名前の変更を忘れないでください。

動作を確認したら、DocList Manager プロジェクトの核心部分である最終的な DoclistControllerApplicationController をご覧ください。また、Google Contacts API への呼び出しを処理する ContactsController もご覧ください。

まとめ

Google Data Rails アプリの作成で最も難しいのは、Rails の構成です。ただし、アプリケーションのデプロイもそれに近いほど重要です。そのため、Apache には mod_rails を強くおすすめします。セットアップ、インストール、実行が非常に簡単です。すぐに使い始められます。

リソース

付録

DocList Manager は、この記事で説明したトピックを示す完全な Ruby on Rails サンプルです。完全なソースコードはプロジェクト ホスティングから入手できます。