Google Data on Rails

Eric Bidelman, Google Data APIs Team
Februar 2009

Einführung

„Wo finde ich Ruby in der Liste der Clientbibliotheken?“

Angesichts des unstillbaren Appetits unserer Entwickler und der anhaltenden Beliebtheit von Ruby on Rails (RoR) hat mein Kollege Jeff Fisher eine Ruby-Hilfsbibliothek aus den feurigen Tiefen des Schicksalsbergs geschmiedet. Es handelt sich nicht um eine vollständige Clientbibliothek, aber sie deckt die Grundlagen wie Authentifizierung und grundlegende XML-Bearbeitung ab. Außerdem müssen Sie direkt mit dem Atom-Feed arbeiten und dazu das REXML-Modul und XPath verwenden.

Zielgruppe

Dieser Artikel richtet sich an Entwickler, die mit Ruby, insbesondere Ruby on Rails, auf die Google Data APIs zugreifen möchten. Dabei wird davon ausgegangen, dass der Leser mit der Programmiersprache Ruby und dem Rails-Webentwicklungs-Framework vertraut ist. In den meisten Beispielen konzentriere ich mich auf die Documents List API, aber dieselben Konzepte können auf alle Data APIs angewendet werden.

Erste Schritte

Voraussetzungen

Google Data Ruby Utility Library installieren

Sie können die Bibliothek entweder direkt aus dem Projekthosting herunterladen oder das Gem installieren:

sudo gem install gdata

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

Authentifizierung

ClientLogin

Mit ClientLogin können sich Nutzer programmatisch in ihrer Anwendung in ihrem Google- oder G Suite-Konto anmelden. Nach der Validierung der Anmeldedaten des Nutzers gibt Google ein Authentifizierungstoken aus, auf das in nachfolgenden API-Anfragen verwiesen werden kann. Das Token bleibt für einen bestimmten Zeitraum gültig, der vom jeweiligen Google-Dienst abhängt, den Sie verwenden. Aus Sicherheitsgründen und um Ihren Nutzern die bestmögliche Erfahrung zu bieten, sollten Sie ClientLogin nur bei der Entwicklung installierter Desktopanwendungen verwenden. Für Webanwendungen wird die Verwendung von AuthSub oder OAuth empfohlen.

Die Ruby-Bibliothek enthält für jede API eine Clientklasse. Mit dem folgenden Code-Snippet können Sie sich beispielsweise mit user@gmail.com in der Documents List Data API anmelden:

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. Im folgenden Beispielcode werden Nutzer gezwungen, 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 die accountType. Mögliche Werte sind HOSTED_OR_GOOGLE, HOSTED und GOOGLE.

Einer der Nachteile der Verwendung von ClientLogin besteht darin, dass Ihre Anwendung bei fehlgeschlagenen Anmeldeversuchen CAPTCHA-Herausforderungen erhalten kann. In diesem Fall können Sie den Fehler beheben, indem Sie die Methode clientlogin() mit den zusätzlichen Parametern client.clientlogin(username, password, captcha_token, captcha_answer) aufrufen. 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)

Mit dem 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 wurde ein Standardattribut authsub_scope festgelegt, sodass Sie kein eigenes 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

Einmal-Token in ein Sitzungstoken umwandeln

AuthSub leitet den Nutzer zurück zu http://example.com/change/to/your/app?token=SINGLE_USE_TOKEN, sobald er den Zugriff auf seine Daten gewährt hat. Die URL ist einfach unsere next_url mit dem Einmal-Token als Suchparameter.

Tauschen Sie als Nächstes das Einmal-Token gegen ein langlebiges Sitzungstoken ein:

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. Die einzige Ergänzung besteht darin, den privaten Schlüssel festzulegen, bevor Sie das Token aktualisieren:

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 secure=true festlegen, wenn Sie ein Einmal-Token anfordern. Siehe oben AuthSubRequest-URL generieren.

Tokenverwaltung

AuthSub bietet zwei zusätzliche Handler, AuthSubTokenInfo und AuthSubRevokeToken, zum Verwalten von Tokens. AuthSubTokenInfo ist nützlich, um die Gültigkeit eines Tokens zu prüfen. Mit AuthSubRevokeToken haben Nutzer die Möglichkeit, den Zugriff auf ihre Daten zu beenden. Ihre App 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 Dokumentation zu AuthSub finden Sie unter AuthSub-Authentifizierung für Webanwendungen.

OAuth

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

Die Verwendung von OAuth in der Utility-Bibliothek sollte relativ einfach sein, wenn Sie das Rails-OAuth-Plug-in oder das Ruby-OAuth-Gem verwenden. In beiden Fällen müssen Sie ein GData::HTTP::Request-Objekt erstellen und den von jeder Bibliothek generierten Authorization-Header übergeben.

Auf Feeds zugreifen

GET (Daten abrufen)

Nachdem Sie ein Clientobjekt eingerichtet haben, können Sie mit der Methode get() einen Google Data-Feed abfragen. Mit XPath können Sie bestimmte Atom-Elemente abrufen. Hier ist 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 post()-Methode eines Clients, um neue Daten auf dem Server zu erstellen. Im folgenden Beispiel wird new_writer@example.com als Mitbearbeiter für das 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. Dabei wird davon ausgegangen, dass Sie einen Feed aus einer vorherigen Anfrage 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

Wenn Sie ein <atom:entry> oder andere Daten vom Server löschen möchten, verwenden Sie die Methode delete(). Im folgenden Beispiel wird ein Dokument gelöscht. Im Code wird davon ausgegangen, dass Sie einen Dokumenteintrag aus einer vorherigen Anfrage 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 Rails-Anwendung erstellen

Normalerweise besteht die erste Übung beim Erstellen einer neuen Rails-Anwendung darin, die Scaffold-Generatoren auszuführen, um Ihre MVC-Dateien zu erstellen. Danach wird rake db:migrate ausgeführt, um Ihre Datenbanktabellen einzurichten. Da unsere Anwendung jedoch Daten über die Google Documents List API abfragen wird, benötigen wir kaum generische Gerüste oder Datenbanken. Erstellen Sie stattdessen eine neue Anwendung und einen einfachen Controller:

rails doclist
cd doclist
ruby script/generate controller doclist

Nehmen Sie die folgenden Änderungen an config/environment.rb vor:

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

In der ersten Zeile wird ActiveRecord aus der Anwendung entfernt. In der zweiten Zeile wird das gdata-Gem beim Start geladen.

Schließlich 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 generiert haben, fügen Sie der DoclistController in app/controllers/doclist_controller.rb manuell eine Aktion mit dem Namen „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 mit der Entwicklung beginnen

Sie sollten jetzt den Standardwebserver starten können, indem Sie ruby script/server aufrufen. Wenn alles in Ordnung ist, sollte beim Aufrufen von http://localhost:3000/ in Ihrem Browser „I pity the foo!“ angezeigt werden.

Tipp: Vergessen Sie nicht, public/index.html zu entfernen oder umzubenennen.

Wenn alles funktioniert, sehen Sie sich die endgültigen DoclistController und ApplicationController an, um die wichtigsten Teile des DocList Manager-Projekts zu sehen. Sehen Sie sich auch ContactsController an, das die Aufrufe der Google Contacts API verarbeitet.

Fazit

Der schwierigste Teil beim Erstellen einer Google Data Rails-App ist die Konfiguration von Rails. Gleich danach folgt die Bereitstellung Ihrer Anwendung. Dazu empfehle ich mod_rails für Apache. Die Einrichtung, Installation und Ausführung sind ganz einfach. Sie können sofort loslegen.

Ressourcen

Anhang

Beispiele

Der DocList Manager ist ein vollständiges Ruby on Rails-Beispiel, das die in diesem Artikel behandelten Themen veranschaulicht. Der vollständige Quellcode ist über das Projekthosting verfügbar.