Google Data on Rails

Eric Bidelman, team delle API Google Data
Febbraio 2009

Introduzione

"Dov'è Ruby nell'elenco delle librerie client?"

Spinto dalla vorace curiosità dei nostri sviluppatori e dalla duratura popolarità di Ruby on Rails (RoR), il mio collega Jeff Fisher ha forgiato una libreria di utilità Ruby dalle infuocate profondità di Monte Fato. Tieni presente che non si tratta di una libreria client completa, ma gestisce le funzionalità di base come l'autenticazione e la manipolazione di base di XML. Inoltre, richiede di lavorare direttamente con il feed Atom utilizzando il modulo REXML e XPath.

Pubblico

Questo articolo è rivolto agli sviluppatori interessati ad accedere alle API Google Data utilizzando Ruby, in particolare Ruby on Rails. Si presume che il lettore abbia una certa familiarità con il linguaggio di programmazione Ruby e con il framework di sviluppo web Rails. Mi concentro sull'API Document List per la maggior parte degli esempi, ma gli stessi concetti possono essere applicati a qualsiasi API di dati.

Per iniziare

Requisiti

Installazione della libreria di utilità Google Data Ruby

Per ottenere la libreria, puoi scaricare direttamente l'origine della libreria dall'hosting del progetto o installare il gem:

sudo gem install gdata

Suggerimento: per sicurezza, esegui gem list --local per verificare che la gemma sia stata installata correttamente.

Autenticazione

ClientLogin

ClientLogin consente alla tua applicazione di accedere in modo programmatico agli account Google o G Suite degli utenti. Dopo aver convalidato le credenziali dell'utente, Google rilascia un token di autenticazione a cui fare riferimento nelle successive richieste API. Il token rimane valido per un periodo di tempo prestabilito, definito dal servizio Google che stai utilizzando. Per motivi di sicurezza e per offrire ai tuoi utenti la migliore esperienza, devi utilizzare ClientLogin solo quando sviluppi applicazioni installate e desktop. Per le applicazioni web, è preferibile utilizzare AuthSub o OAuth.

La libreria Ruby ha una classe client per ciascuna API. Ad esempio, utilizza il seguente snippet di codice per accedere a [email protected] all'API Documents List Data:

client = GData::Client::DocList.new
client.clientlogin('[email protected]', 'pa$$word')

The YouTube Data API would be:

client = GData::Client::YouTube.new
client.clientlogin('[email protected]', 'pa$$word')

Consulta l'elenco completo delle classi di servizio implementate. Se un servizio non ha una classe client, utilizza la classe GData::Client::Base. Ad esempio, il seguente codice impone agli utenti di accedere con un account 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)

Nota: per impostazione predefinita, la libreria utilizza HOSTED_OR_GOOGLE per accountType. I valori possibili sono HOSTED_OR_GOOGLE, HOSTED o GOOGLE.

Uno degli svantaggi dell'utilizzo di ClientLogin è che la tua applicazione può ricevere test CAPTCHA in caso di tentativi di accesso non riusciti. Se ciò accade, puoi gestire l'errore chiamando il metodo clientlogin() con i relativi parametri aggiuntivi: client.clientlogin(username, password, captcha_token, captcha_answer). Per ulteriori informazioni sulla gestione dei CAPTCHA, consulta la documentazione completa relativa all'autenticazione per le applicazioni installate.

AuthSub

Generazione dell'URL 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)

Il blocco di codice precedente crea il seguente URL in authsub_link:

https://www.google.com/accounts/AuthSubRequest?next=https%3A%2F%2F2.zoppoz.workers.dev%3A443%2Fhttp%2Fexample.com%2Fchange%2Fto%2Fyour%2Fapp&scope=https%3A%2F%2F2.zoppoz.workers.dev%3A443%2Fhttp%2Fwww.google.com%2Fcalendar%2Ffeeds%2F&session=1&secure=0

Puoi anche utilizzare il metodo authsub_url dell'oggetto client. Ogni classe di servizio ha impostato un attributo authsub_scope predefinito, quindi non è necessario specificarne uno personalizzato.

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)

Il blocco di codice precedente crea il seguente URL:

https://www.google.com/accounts/AuthSubRequest?next=https%3A%2F%2F2.zoppoz.workers.dev%3A443%2Fhttp%2Fexample.com%2Fchange%2Fto%2Fyour%2Fapp&scope=https%3A%2F%2F2.zoppoz.workers.dev%3A443%2Fhttp%2Fdocs.google.com%2Ffeeds%2F&session=1&secure=0&hd=example.com

Eseguire l'upgrade di un token monouso a un token di sessione

AuthSub reindirizzerà l'utente a http://example.com/change/to/your/app?token=SINGLE_USE_TOKEN una volta che avrà concesso l'accesso ai suoi dati. Tieni presente che l'URL è semplicemente il nostro next_url con il token monouso aggiunto come parametro di query.

Successivamente, scambia il token monouso con un token di sessione di lunga durata:

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 è molto simile. L'unica aggiunta è impostare la chiave privata prima di eseguire l'upgrade del token:

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]

Nota: per utilizzare i token sicuri, assicurati di impostare secure=true quando richiedi un token monouso. Vedi Generazione dell'URL AuthSubRequest sopra.

Gestione dei token

AuthSub fornisce due gestori aggiuntivi, AuthSubTokenInfo e AuthSubRevokeToken, per la gestione dei token. AuthSubTokenInfo è utile per verificare la validità di un token. AuthSubRevokeToken offre agli utenti la possibilità di interrompere l'accesso ai propri dati. Come best practice, la tua app deve utilizzare AuthSubRevokeToken. Entrambi i metodi sono supportati nella libreria Ruby.

Per eseguire una query sui metadati di un token:

client.auth_handler.info

Per revocare un token di sessione:

client.auth_handler.revoke

Consulta la documentazione completa sull'autenticazione AuthSub per applicazioni web per saperne di più su AuthSub.

OAuth

Al momento della stesura di questo articolo, OAuth non è stato aggiunto al modulo GData::Auth.

L'utilizzo di OAuth nella libreria di utilità dovrebbe essere relativamente semplice quando si utilizza il plug-in oauth di Rails o il gem oauth di Ruby. In entrambi i casi, ti consigliamo di creare un oggetto GData::HTTP::Request e di passargli l'intestazione Authorization generata da ogni libreria.

Accedere ai feed

GET (recupero dei dati)

Dopo aver configurato un oggetto client, utilizza il relativo metodo get() per eseguire query su un feed Google Data. XPath può essere utilizzato per recuperare elementi Atom specifici. Ecco un esempio di recupero dei documenti Google di un utente:

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 (creazione di nuovi dati)

Utilizza il metodo post() di un client per creare nuovi dati sul server. L'esempio seguente aggiungerà [email protected] come collaboratore al documento con 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='[email protected]'/>
</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 (aggiornamento dei dati)

Per aggiornare i dati sul server, utilizza il metodo put() di un client. L'esempio seguente aggiornerà il titolo di un documento. Presuppone che tu abbia un feed da una query precedente.

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)

ELIMINA

Per eliminare un elemento <atom:entry> o altri dati dal server, utilizza il metodo delete(). L'esempio seguente elimina un documento. Il codice presuppone che tu abbia una voce di documento da una query precedente.

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)

Creazione di una nuova applicazione Rails

Di solito, il primo esercizio per creare una nuova app Rails prevede l'esecuzione dei generatori di scaffold per creare i file MVC. Dopodiché, viene eseguito rake db:migrate per configurare le tabelle del database. Tuttavia, poiché la nostra applicazione eseguirà query sull'API Google Documents List per i dati, abbiamo poco bisogno di scaffolding o database generici. Crea invece una nuova applicazione e un controller semplice:

rails doclist
cd doclist
ruby script/generate controller doclist

e apporta le seguenti modifiche a config/environment.rb:

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

La prima riga scollega ActiveRecord dall'applicazione. La seconda riga carica la gemma gdata all'avvio.

Infine, ho scelto di collegare la route predefinita ("/") all'azione documents in DoclistController. Aggiungi questa riga a config/routes.rb:

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

Avviare un controller

Poiché non abbiamo generato scaffolding, aggiungi manualmente un'azione chiamata "all" a DoclistController in app/controllers/doclist_controller.rb.

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

e crea all.html.erb in app/views/doclist/:

<%= @foo %>

Avviare il server web e iniziare lo sviluppo

Ora dovresti essere in grado di avviare il server web predefinito richiamando ruby script/server. Se tutto va bene, se indirizzi il browser a http://localhost:3000/ dovrebbe essere visualizzato "I pity the foo!".

Suggerimento: non dimenticare di rimuovere o rinominare public/index.html.

Una volta che tutto funziona, dai un'occhiata al mio DoclistController e ApplicationController per il cuore del progetto DocList Manager. Ti consigliamo anche di esaminare ContactsController, che gestisce le chiamate all'API Google Contacts.

Conclusione

La parte più difficile della creazione di un'app Google Data Rails è la configurazione di Rails. Tuttavia, al secondo posto c'è il deployment dell'applicazione. Per questo, ti consiglio vivamente mod_rails per Apache. È facilissimo da configurare, installare ed eseguire. Sarai pronto in un attimo.

Risorse

Appendice

Esempi

DocList Manager è un esempio completo di Ruby on Rails che illustra gli argomenti trattati in questo articolo. Il codice sorgente completo è disponibile dall'hosting del progetto.