Google-Daten zu Schienen

Eric Bidelman, Google Data APIs-Team
Februar 2009

Einführung

„Wo ist Ruby in der Liste der Clientbibliotheken?“

Mein Kollege Jeff Fisher hat durch den wilden Appetit unserer Entwickler und die anhaltende Beliebtheit von Ruby on Railways (RoR) motiviert, eine Ruby-Dienstprogrammbibliothek aus den feinsten Tiefen des Mount Doom zu schmieden. Es handelt sich zwar nicht um eine vollwertige Clientbibliothek, aber sie behandelt die Grundlagen wie Authentifizierung und grundlegende XML-Manipulation. Außerdem müssen Sie über das REXML-Modul und XPath direkt mit dem Atom-Feed arbeiten.

Zielgruppe

Dieser Artikel richtet sich an Entwickler, die über Ruby auf Ruby auf Google Data APIs zugreifen möchten. Dabei wird davon ausgegangen, dass der Leser mit der Programmiersprache Ruby und dem Framework für die Webentwicklung vonRail vertraut ist. In den meisten Fällen konzentriere ich mich auf die Documents List API. Die gleichen Konzepte können aber auf alle Data APIs angewendet werden.

Erste Schritte

Voraussetzungen

Ruby-Dienstprogrammbibliothek für Google-Daten installieren

Sie können die Bibliotheksquelle direkt beim Projekthosting herunterladen oder das Gem installieren, um die Bibliothek zu erhalten:

sudo gem install gdata

Tipp: Führen Sie gem list --local aus, um zu prüfen, ob das Gem korrekt installiert wurde.

Authentifizierung

ClientLogin

Mit ClientLogin kann sich Ihre Anwendung programmatisch in ihrem Google- oder G Suite-Konto anmelden. Nach der Validierung der Anmeldedaten des Nutzers stellt Google ein Authentifizierungstoken aus, auf das in nachfolgenden API-Anfragen verwiesen wird. Das Token bleibt für einen festgelegten Zeitraum gültig, je nachdem, mit welchem Google-Dienst Sie arbeiten. Aus Sicherheitsgründen und um Ihren Nutzern eine optimale Nutzererfahrung zu bieten, sollten Sie ClientLogin nur bei der Entwicklung installierter Desktopanwendungen verwenden. Für Webanwendungen wird die Verwendung von AuthSub oder OAuth bevorzugt.

Die Ruby-Bibliothek hat eine Clientbibliothek für jede der APIs. Verwenden Sie beispielsweise das folgende Code-Snippet, um sich bei der Documents List Data API in user@gmail.com anzumelden:

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

Vollständige Liste der implementierten Dienstklassen Wenn ein Dienst keine Clientklasse hat, verwenden Sie die Klasse GData::Client::Base. Der folgende Code zwingt Nutzer beispielsweise dazu, sich mit einem G Suite-Konto anzumelden.

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)

Hinweis: Standardmäßig verwendet die Bibliothek HOSTED_OR_GOOGLE für accountType. Mögliche Werte sind HOSTED_OR_GOOGLE, HOSTED oder GOOGLE.

Einer der Nachteile von ClientLogin besteht darin, dass Ihrer Anwendung bei fehlgeschlagenen Anmeldeversuchen CAPTCHA-Herausforderungen gesendet werden können. In diesem Fall können Sie den Fehler beheben, indem Sie die Methode clientlogin() mit ihren zusätzlichen Parametern aufrufen: client.clientlogin(username, password, captcha_token, captcha_answer). Weitere Informationen zum Umgang mit CAPTCHAs finden Sie in der vollständigen Dokumentation zur Authentifizierung für installierte Anwendungen.

AuthSub

AuthSubRequest-URL generieren

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)

Durch den vorherigen Codeblock wird die folgende URL in authsub_link erstellt:

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

Sie können auch die Methode authsub_url des Clientobjekts verwenden. Für jede Dienstklasse ist ein authsub_scope-Standardattribut festgelegt, sodass Sie keine eigenen Attribute angeben müssen.

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)

Mit dem vorherigen Codeblock wird die folgende URL erstellt:

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

Upgrade eines Einweg-Tokens auf ein Sitzungstoken durchführen

AuthSub leitet den Nutzer zu http://example.com/change/to/your/app?token=SINGLE_USE_TOKEN zurück, nachdem er Zugriff auf seine Daten gewährt hat. Beachten Sie, dass die URL einfach unser next_url ist und das Einweg-Token als Abfrageparameter angehängt wird.

Als Nächstes tauschen Sie das Einweg-Token gegen ein langlebiges Sitzungstoken aus:

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 ist sehr ähnlich. Der einzige zusätzliche Schritt besteht darin, den privaten Schlüssel vor dem Upgrade des Tokens festzulegen:

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]

Hinweis: Wenn Sie sichere Tokens verwenden möchten, müssen Sie bei der Anfrage eines einmaligen Tokens unbedingt secure=true festlegen. Weitere Informationen finden Sie oben unter AuthSubRequest-URL generieren.

Tokenverwaltung

AuthSub bietet zwei zusätzliche Handler, AuthSubTokenInfo und AuthSubWiderruftoken zum Verwalten von Tokens. AuthSubTokenInfo ist nützlich, um die Gültigkeit eines Tokens zu prüfen. AuthSubRevokeToken bietet Nutzern die Möglichkeit, den Zugriff auf ihre Daten zu deaktivieren. Ihre Anwendung sollte AuthSubRevokeToken als Best Practice verwenden. Beide Methoden werden in der Ruby-Bibliothek unterstützt.

So fragen Sie die Metadaten eines Tokens ab:

client.auth_handler.info

So widerrufen Sie ein Sitzungstoken:

client.auth_handler.revoke

Die vollständige AuthSub-Authentifizierung für Webanwendungen finden Sie in der vollständigen Dokumentation zu AuthSub.

OAuth

Zum Zeitpunkt der Erstellung dieses Artikels wurde OAuth dem Modul GData::Auth noch nicht hinzugefügt.

Die Verwendung von OAuth in der Dienstprogrammbibliothek sollte relativ einfach sein, wenn das Rail-Plug-in oder das OAuth-Gem von Ruby verwendet wird. In beiden Fällen empfiehlt es sich, ein GData::HTTP::Request-Objekt zu erstellen und ihm den von jeder Bibliothek generierten Authorization-Header zu übergeben.

Auf Feeds zugreifen

GET (Daten werden abgerufen)

Nachdem Sie ein Clientobjekt eingerichtet haben, können Sie mit seiner get()-Methode einen Google-Datenfeed abfragen. Mit XPath können bestimmte Atom-Elemente abgerufen werden. Hier ein Beispiel für das Abrufen der Google-Dokumente eines Nutzers:

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 (neue Daten erstellen)

Verwenden Sie die Methode post() eines Clients, um neue Daten auf dem Server zu erstellen. Im folgenden Beispiel wird new_writer@example.com als Mitbearbeiter zum Dokument mit der ID doc_id hinzugefügt.

# 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 (Daten aktualisieren)

Verwenden Sie die Methode put() eines Clients, um Daten auf dem Server zu aktualisieren. Im folgenden Beispiel wird der Titel eines Dokuments aktualisiert. Es wird davon ausgegangen, dass Sie einen Feed aus einer vorherigen Abfrage haben.

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)

LÖSCHEN

Verwenden Sie die Methode delete(), um <atom:entry> oder andere Daten vom Server zu löschen. Im folgenden Beispiel wird ein Dokument gelöscht. Im Code wird davon ausgegangen, dass Sie einen Dokumenteintrag aus einer vorherigen Abfrage haben.

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)

Neue Schienenanwendung erstellen

In der Regel besteht der erste Schritt zum Erstellen einer neuen Schienenanwendung darin, die Gerüstgeneratoren auszuführen, um Ihre MVC-Dateien zu erstellen. Danach wird rake db:migrate zum Einrichten der Datenbanktabellen ausgeführt. Da unsere Anwendung jedoch die Google Documents List API nach Daten abfragt, benötigen wir wenig generisches Gerüst oder Datenbanken. Erstellen Sie stattdessen eine neue Anwendung und einen einfachen Controller:

rails doclist
cd doclist
ruby script/generate controller doclist

und nehmen Sie folgende Änderungen an config/environment.rb vor:

config.frameworks -= [ :active_record, :active_resource, :action_mailer ]
config.gem 'gdata', :lib => 'gdata'

Die erste Zeile hebt die Verknüpfung von ActiveRecord mit der Anwendung auf. In der zweiten Zeile wird das Gem gdata beim Start geladen.

Abschließend habe ich die Standardroute („/“) mit der Aktion documents in DoclistController verbunden. Fügen Sie diese Zeile zu config/routes.rb hinzu:

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

Controller starten

Da wir kein Gerüst erstellt haben, fügen Sie dem DoclistController in app/controllers/doclist_controller.rb manuell die Aktion „all“ hinzu.

class DoclistController < ApplicationController
  def all
    @foo = 'I pity the foo!'
  end
end

und erstellen Sie all.html.erb unter app/views/doclist/:

<%= @foo %>

Webserver starten und Entwicklung starten

Sie sollten jetzt den Standard-Webserver durch Aufrufen von ruby script/server starten können. Wenn alles gut geht, sollte dein Browser http://localhost:3000/ "I pity the foo!" anzeigen.

Tipp: Denken Sie daran, public/index.html zu entfernen oder umzubenennen.

Wenn Sie mit allem fertig sind, können Sie sich die letzten DoclistController und ApplicationController für das DocList Manager-Projekt ansehen. Sie sollten auch ContactsController berücksichtigen, das die Aufrufe der Google Contacts API verarbeitet.

Fazit

Das Schwierigste beim Erstellen einer Google Data-Rails-Anwendung ist die Konfiguration von Schienen! Eine kurze Zeit später ist die Bereitstellung Ihrer Anwendung. Dafür empfehle ich Ihnen dringend den Befehl mod_rails für Apache. Das Einrichten, Installieren und Ausführen ist ganz einfach. Sie sind in kürzester Zeit einsatzbereit.

Ressourcen

Anhang

Beispiele

Der DocList Manager ist ein vollständiges Beispiel für Ruby onRail, das die in diesem Artikel beschriebenen Themen veranschaulicht. Der vollständige Quellcode ist im Projekthosting verfügbar.