Dati di Google su Rails

Eric Bidelman, team delle API di dati di Google
febbraio 2009

Introduzione

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

Motivato dal feroce appetito dei nostri sviluppatori e dalla continua popolarità di Ruby on Rails (RoR), il mio collega Jeff Fisher ha creato una raccolta di utilità di rubini dalle incredibili profondità di Mount Doom. Tenete presente che non è una libreria client a tutti gli effetti, ma gestisce i concetti fondamentali come autenticazione e manipolazione XML di base. Richiede inoltre di lavorare direttamente con il feed Atom utilizzando il modulo REXML e XPath.

Pubblico

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

Per iniziare

Requisiti

Installare la libreria di utilità Ruby dei dati di Google

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

sudo gem install gdata

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

Autenticazione

ClientLogin

ClientLogin consente all'applicazione di accedere in modo programmatico agli utenti all'account Google o G Suite. Dopo la convalida delle credenziali dell'utente, Google emette un token di autenticazione a cui fare riferimento nelle successive richieste API. Il token rimane valido per un periodo di tempo definito, definito dal servizio Google in uso. Per motivi di sicurezza e per offrire agli utenti la migliore esperienza possibile, ti consigliamo di utilizzare ClientLogin soltanto durante lo sviluppo di applicazioni desktop installate. Per le applicazioni web è preferibile utilizzare AuthSub o OAuth.

La libreria Ruby ha una classe client per ciascuna delle API. Ad esempio, utilizza il seguente snippet di codice per accedere a user@gmail.com con l'API di dati dell'elenco documenti:

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

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, nel codice seguente gli utenti devono 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 raccolta utilizza HOSTED_OR_GOOGLE per accountType. I valori possibili sono HOSTED_OR_GOOGLE, HOSTED o GOOGLE.

Uno degli svantaggi dell'uso di ClientLogin è che all'applicazione possono essere inviate richieste CAPTCHA in caso di tentativi di accesso non riusciti. In questo caso, puoi gestire l'errore chiamando il metodo clientlogin() con i parametri aggiuntivi: client.clientlogin(username, password, captcha_token, captcha_answer). Per ulteriori informazioni sulla gestione dei CAPTCHA, consulta la documentazione completa sull'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=http%3A%2F%2Fexample.com%2Fchange%2Fto%2Fyour%2Fapp&scope=http%3A%2F%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=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 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 dopo aver concesso l'accesso ai propri dati. Tieni presente che l'URL è solo il nostro next_url con il token monouso aggiunto come parametro di ricerca.

Quindi, 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 è l'impostazione della 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. Consulta la sezione Generazione dell'URL di AuthSubRequest in alto.

Gestione dei token

AuthSub fornisce due gestori aggiuntivi, AuthSubTokenInfo e AuthSubRevocaToken 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. La tua app dovrebbe utilizzare AuthSubRevokeToken come best practice. Entrambi i metodi sono supportati nella libreria Ruby.

Per eseguire 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 SubSub per applicazioni web per la versione completa di 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 utilizzi il plug-in oauth Rails o Gem OAuth. In entrambi i casi, dovrai creare un oggetto GData::HTTP::Request e trasmetterlo all'intestazione Authorization generata da ciascuna libreria.

Accedere ai feed

GET (recupero dati)

Dopo aver configurato un oggetto client, utilizza il relativo metodo get() per eseguire query su un feed di dati di Google. 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 del client post() per creare nuovi dati sul server. L'esempio seguente aggiungerà new_writer@example.com 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='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 (aggiornamento dati)

Per aggiornare i dati sul server, utilizza il metodo put() del client. L'esempio seguente aggiornerà il titolo di un documento. Presuppone che tu abbia un feed di 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 <atom:entry> o altri dati dal server, utilizza il metodo delete(). Il seguente esempio eliminerà un documento. Il codice presume che tu abbia una voce di documento proveniente 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 nella creazione di una nuova app Rails prevede l'esecuzione dei generatori di impalcature per creare i file MVC. Successivamente, eseguirà rake db:migrate per configurare le tabelle del database. Tuttavia, poiché la nostra applicazione eseguirà una query sui dati dell'API Google List List, abbiamo pochissimo bisogno di database o scaffolding 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 sgancia ActiveRecord dall'applicazione. La seconda riga carica la gemma gdata all'avvio.

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

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

Avvia un controller

Poiché non abbiamo generato impalcature, aggiungi manualmente un'azione chiamata "all" alla DoclistController di 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 %>

Avvia il server web e avvia lo sviluppo

Ora dovresti essere in grado di avviare il server web predefinito richiamando ruby script/server. Se tutto funziona correttamente, puntando 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 avrai finito di lavorare, dai un'occhiata alle mie ultime DoclistController e a ApplicationController per l'elenco di progetti del progetto DocList Manager. Ti consigliamo inoltre 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, un attimo di tempo sta eseguendo il deployment dell'applicazione. Per questo consiglio vivamente mod_rails per Apache. È facilissimo da configurare, installare ed eseguire. Potrai pubblicare gli annunci in pochissimo tempo.

Risorse

Appendice

Esempi

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