Utilizzo di Ruby con le API di dati di Google

Jochen Hartmann, team delle API di dati di Google
aprile 2008

Introduzione

Ruby è un linguaggio di scripting dinamico che ha ricevuto una buona attenzione negli ultimi anni a causa del suo popolare framework di sviluppo web Rails. Questo articolo spiega come utilizzare Ruby per interagire con i servizi Google Data API. Non ci concentreremo sui binari, ma ci interesserebbe maggiormente spiegare i comandi e la struttura HTTP sottostanti dei nostri feed. Tutti gli esempi qui presentati possono essere seguiti dalla riga di comando utilizzando irb, la shell interattiva di Ruby.

Come ricorderai dall'articolo su cURL, le API di dati di Google utilizzano il Protocollo di pubblicazione Atom per rappresentare, creare e aggiornare le risorse web. Il vantaggio di questo protocollo è che i verbi HTTP standard vengono utilizzati per formulare richieste che ricevono risposta con i codici di stato HTTP standard.

I verbi che utilizzeremo in questo articolo sono GET per recuperare i contenuti, POST per caricare nuovi contenuti e PUT per aggiornare quelli esistenti. Alcuni dei codici standard che potresti incontrare utilizzando le API di dati di Google sono 200 per indicare la riuscita del recupero di un feed o una voce oppure 201 per indicare la creazione o l'aggiornamento di una risorsa. Se si verifica un problema, ad esempio quando viene inviata una richiesta non valida, verrà restituito un codice 400 (ovvero "Richiesta errata"). Nel corpo della risposta verrà fornito un messaggio più dettagliato, che spiega cosa è andato storto.

Ruby offre una buona opzione di debug nel modulo "Netto". Per motivi di brevità degli esempi di codice, tuttavia, non l'ho attivata qui.

Ottenere e installare Ruby

È possibile installare Ruby utilizzando la maggior parte dei sistemi di gestione dei pacchetti se utilizzi Linux. Per altri sistemi operativi e per ottenere il codice sorgente completo, visita la pagina http://www.ruby-lang.org/en/download/. Irb, la shell interattiva che utilizzeremo per questi esempi dovrebbe essere installata per impostazione predefinita. Per seguire gli esempi di codice elencati qui, dovrai installare XmlSimple, una piccola libreria per analizzare XML nelle strutture di dati Ruby. Per ottenere/installare XmlSimple, visita http://xml-simple.rubyforge.org/

Una volta in esecuzione una copia di Ruby sul tuo computer, puoi utilizzare il pacchetto Net:HTTP per effettuare richieste di base ai servizi dati di Google. Lo snippet di seguito mostra come eseguire le importazioni necessarie dalla shell interattiva di Ruby. Ciò che stiamo facendo consiste nel richiedere il pacchetto "net/http", analizzare l'URL del feed video più valutato di YouTube e quindi eseguire una richiesta HTTP GET.

irb(main):001:0> require 'net/http'
=> true
irb(main):002:0> youtube_top_rated_videos_feed_uri = \
'http://gdata.youtube.com/feeds/api/standardfeeds/top_rated'
=> "http://gdata.youtube.com/feeds/api/standardfeeds/top_rated"
irb(main):003:0> uri = \
URI.parse(youtube_top_rated_videos_feed_uri)
=> #<URI::HTTP:0xfbf826e4 URL:http://gdata.youtube.com/feeds/api/standardfeeds/top_rated>

irb(main):004:0> uri.host
=> "gdata.youtube.com"
irb(main):005:0> Net::HTTP.start(uri.host, uri.port) do |http|
irb(main):006:1* puts http.get(uri.path)
irb(main):007:1> end
#<Net::HTTPOK:0xf7ef22cc>

Questa richiesta avrebbe dovuto causare un effetto echo di XML nella riga di comando. Forse avrai notato che tutti gli elementi sono contenuti in un elemento <feed> e vengono chiamati elementi <entry>. Per ora non ti preoccupamo della formattazione XML, ma volevo spiegarti come effettuare una richiesta API di dati di base di Google utilizzando HTTP. Passeremo ora alle API e ci concentreremo sui fogli di lavoro, poiché le informazioni che possiamo inviare e recuperare sono più "a riga di comando".

Autenticazione | Utilizzo dell'API Google Sheets

Inizieremo di nuovo recuperando un feed di elementi di input. anche se questa volta vogliamo lavorare con i nostri fogli di lavoro. A questo scopo, dobbiamo innanzitutto eseguire l'autenticazione con il servizio Google Account.

Come ricorderai dalla documentazione relativa all'autenticazione dei dati G, esistono due modi per eseguire l'autenticazione con i servizi Google. AuthSub è per applicazioni basate sul Web e, in poche parole, comporta un processo di scambio dei token. Il vero vantaggio di AuthSub è che la tua applicazione non ha bisogno di archiviare le credenziali utente. ClientLogin è per le applicazioni "installate". Durante il processo ClientLogin, il nome utente e la password vengono inviati ai servizi Google tramite https, insieme a una stringa che identifica il servizio che stai cercando di utilizzare. Il servizio API Fogli di lavoro Google è identificato dalla stringa sensibile.

Se torniamo alla nostra shell interattiva, eseguiamo l'autenticazione con Google. Tieni presente che utilizziamo https per inviare la nostra richiesta di autenticazione e le nostre credenziali:

irb(main):008:0> require 'net/https'
=> true
irb(main):009:0> http = Net::HTTP.new('www.google.com', 443)
=> #<Net::HTTP www.google.com:443 open=false>
irb(main):010:0> http.use_ssl = true
=> true
irb(main):011:0> path = '/accounts/ClientLogin'
=> "/accounts/ClientLogin"

# Now we are passing in our actual authentication data. 
# Please visit OAuth For Installed Apps for more information 
# about the accountType parameter
irb(main):014:0> data = \
irb(main):015:0* 'accountType=HOSTED_OR_GOOGLE&Email=your email' \
irb(main):016:0* '&Passwd=your password' \
irb(main):017:0* '&service=wise'

=> accountType=HOSTED_OR_GOOGLE&Email=your email&Passwd=your password&service=wise"

# Set up a hash for the headers
irb(main):018:0> headers = \
irb(main):019:0* { 'Content-Type' => 'application/x-www-form-urlencoded'}
=> {"Content-Type"=>"application/x-www-form-urlencoded"}

# Post the request and print out the response to retrieve our authentication token
irb(main):020:0> resp, data = http.post(path, data, headers)
warning: peer certificate won't be verified in this SSL session
=> [#<Net::HTTPOK 200 OK readbody=true>, "SID=DQAAAIIAAADgV7j4F-QVQjnxdDRjpslHKC3M ... [ snipping out the rest of the authentication strings ]

# Strip out our actual token (Auth) and store it
irb(main):021:0> cl_string = data[/Auth=(.*)/, 1]
=> "DQAAAIUAAADzL... [ snip ]

# Build our headers hash and add the authorization token
irb(main):022:0> headers["Authorization"] = "GoogleLogin auth=#{cl_string}"
=> "GoogleLogin auth=DQAAAIUAAADzL... [ snip ]

OK. Ora che abbiamo eseguito l'autenticazione, proviamo a recuperare i nostri fogli di lavoro utilizzando la richiesta di

http://spreadsheets.google.com/feeds/spreadsheets/private/full

Poiché questa è una richiesta autenticata, vogliamo passare anche le nostre intestazioni. In realtà, poiché presenteremo diverse richieste per vari feed, potremmo anche includere questa funzionalità in una semplice funzione che chiameremo get_feed.

# Store the URI to the feed since we may want to use it again
irb(main):023:0> spreadsheets_uri = \
irb(main):024:0* 'http://spreadsheets.google.com/feeds/spreadsheets/private/full'

# Create a simple method to obtain a feed
irb(main):025:0> def get_feed(uri, headers=nil)
irb(main):026:1> uri = URI.parse(uri)
irb(main):027:1> Net::HTTP.start(uri.host, uri.port) do |http|
irb(main):028:2* return http.get(uri.path, headers)
irb(main):029:2> end
irb(main):030:1> end
=> nil

# Lets make a request and store the response in 'my_spreadsheets'
irb(main):031:0> my_spreadsheets = get_feed(spreadsheets_uri, headers)
=> #<Net::HTTPOK 200 OK readbody=true>

irb(main):032:0> my_spreadsheets
=> #<Net::HTTPOK 200 OK readbody=true>

# Examine our XML (showing only an excerpt here...)
irb(main):033:0> my_spreadsheets.body
=> "<?xml version='1.0' encoding='UTF-8'?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/'>
<id>http://spreadsheets.google.com/feeds/spreadsheets/private/full</id><updated>2008-03-20T20:49:39.211Z</updated>
<category scheme='http://schemas.google.com/spreadsheets/2006' term='http://schemas.google.com/spreadsheets/2006#spreadsheet'/>
<title type='text'>Available Spreadsheets - test.api.jhartmann@gmail.com</title><link rel='alternate' type='text/html' href='http://docs.google.com'/>
<link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://spreadsheets.google.com/feeds/spreadsheets/private/full'/><link rel='self' type='application/atom+xml' href='http://spreadsheets.google.com/feeds/spreadsheets/private/full?tfe='/>
<openSearch:totalResults>6</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><entry>
<id>http://spreadsheets.google.com/feeds/spreadsheets/private/full/o04927555739056712307.4365563854844943790</id><updated>2008-03-19T20:44:41.055Z</updated><category scheme='http://schemas.google.com/spreadsheets/2006' term='http://schemas.google.com/spreadsheets/2006#spreadsheet'/><title type='text'>test02</title><content type='text'>test02</content><link rel='http://schemas.google.com/spreadsheets/2006#worksheetsfeed' type='application/atom+xml' href='http://spreadsheets.google.com/feeds/worksheets/o04927555739056712307.4365563854844943790/private/full'/><link rel='alternate' type='text/html' href='http://spreadsheets.google.com/ccc?key=o04927555739056712307.4365563854844943790'/><link rel='self' type='application/atom+xml' href='http://spreadsheets.google.com/feeds/spreadsheets/private/full/o04927555739056712307.4365563854844943790'/><author><name>test.api.jhartmann</name><email>test.api.jhartmann@gmail.com</email></author></entry><entry> ...

Stiamo notando una grande quantità di XML, come ho sottolineato sopra, dato che non deve preoccuparsi di decifrarlo dalla riga di comando. Per rendere il tutto più intuitivo, invece, analizzalo in una struttura di dati utilizzando XmlSimple:

# Perform imports
irb(main):034:0> require 'rubygems'
=> true
irb(main):035:0> require 'xmlsimple'
=> true
irb(main):036:0> doc = \
irb(main):037:0* XmlSimple.xml_in(my_spreadsheets.body, 'KeyAttr' => 'name')

# Import the 'pp' module for 'pretty printing'
irb(main):038:0> require 'pp'
=> true

# 'Pretty-print' our XML document
irb(main):039:0> pp doc
{"totalResults"=>["6"],
 "category"=>
  [{"term"=>"http://schemas.google.com/spreadsheets/2006#spreadsheet",
    "scheme"=>"http://schemas.google.com/spreadsheets/2006"}],
 "title"=>
  [{"type"=>"text",
    "content"=>"Available Spreadsheets - Test-account"}],
 "startIndex"=>["1"],
 "id"=>["http://spreadsheets.google.com/feeds/spreadsheets/private/full"],
 "entry"=>
  [{"category"=>
     [{"term"=>"http://schemas.google.com/spreadsheets/2006#spreadsheet",
       "scheme"=>"http://schemas.google.com/spreadsheets/2006"}],
    "title"=>[{"type"=>"text", "content"=>"blank"}],
    "author"=>
     [{"name"=>["Test-account"],
       "email"=>["my email"]}],
    "id"=>
     ["http://spreadsheets.google.com/feeds/spreadsheets/private/full/o04927555739056712307.3387874275736238738"],
    "content"=>{"type"=>"text", "content"=>"blank"},
    "link"=>
    [ snipping out the rest of the XML ]

Ottenere i fogli di lavoro

Come puoi vedere nell'output sopra, il mio feed contiene 6 fogli di lavoro. In breve, ho eliminato gli altri contenuti XML sopra riportati (nonché nella maggior parte delle altre schede). Per approfondire questo foglio di lavoro dovremo eseguire alcuni passaggi aggiuntivi:

  1. Recupera la chiave del foglio di lavoro
  2. Usa la chiave del foglio di lavoro per ottenere il feed del foglio di lavoro
  3. Ottieni l'ID del foglio di lavoro che vogliamo utilizzare
  4. Richiedi l'elemento celleFeed o listFeed per accedere ai contenuti effettivi del foglio di lavoro

Potrebbe sembrare un lavoro enorme, ma vi spiegherò che è tutto abbastanza semplice se scriviamo alcuni metodi semplici. Le celleFeed e listFeed sono due rappresentazioni diverse del contenuto effettivo delle celle di un foglio di lavoro. listFeed rappresenta un'intera riga di informazioni ed è consigliato per la pubblicazione di nuovi dati. Il feed cella rappresenta singole celle e viene utilizzato per aggiornamenti di singole celle o per aggiornamenti collettivi a molte singole celle (entrambi utilizzando PUT). Per maggiori dettagli, consulta la documentazione dell'API Google Sheets.

Prima dobbiamo estrarre la chiave del foglio di lavoro (evidenziata nell'output XML sopra) per ottenere il feed del foglio di lavoro:

# Extract the spreadsheet key from our datastructure
irb(main):040:0> spreadsheet_key = \ 
irb(main):041:0* doc["entry"][0]["id"][0][/full\/(.*)/, 1]
=> "o04927555739056712307.3387874275736238738"

# Using our get_feed method, let's obtain the worksheet feed
irb(main):042:0> worksheet_feed_uri = \ 
irb(main):043:0* "http://spreadsheets.google.com/feeds/worksheets/#{spreadsheet_key}/private/full"
=> "http://spreadsheets.google.com/feeds/worksheets/o04927555739056712307.3387874275736238738/private/full"

irb(main):044:0> worksheet_response = get_feed(worksheet_feed_uri, headers)
=> #<Net::HTTPOK 200 OK readbody=true>

# Parse the XML into a datastructure
irb(main):045:0> worksheet_data = \ 
irb(main):046:0* XmlSimple.xml_in(worksheet_response.body, 'KeyAttr' => 'name')
=> {"totalResults"=>["1"], "category"=>[{"term ... [ snip ]

# And pretty-print it
irb(main):047:0> pp worksheet_data
{"totalResults"=>["1"],
 "category"=>
  [{"term"=>"http://schemas.google.com/spreadsheets/2006#worksheet",
    "scheme"=>"http://schemas.google.com/spreadsheets/2006"}],
 "title"=>[{"type"=>"text", "content"=>"blank"}],
 "author"=>
  [{"name"=>["test.api.jhartmann"],
    "email"=>["test.api.jhartmann@gmail.com"]}],
 "startIndex"=>["1"],
 "id"=>
  ["http://spreadsheets.google.com/feeds/worksheets/o04927555739056712307.3387874275736238738/private/full"],
 "entry"=>
  [{"category"=>
     [{"term"=>"http://schemas.google.com/spreadsheets/2006#worksheet",
       "scheme"=>"http://schemas.google.com/spreadsheets/2006"}],
    "title"=>[{"type"=>"text", "content"=>"Sheet 1"}],
    "rowCount"=>["100"],
    "colCount"=>["20"],
    "id"=>
     ["http://spreadsheets.google.com/feeds/worksheets/o04927555739056712307.3387874275736238738/private/full/od6"],
    "content"=>{"type"=>"text", "content"=>"Sheet 1"},
    "link"=>
     [{"href"=>
        "http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full",
       "rel"=>"http://schemas.google.com/spreadsheets/2006#listfeed",
       "type"=>"application/atom+xml"},
      {"href"=>
        "http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full",
       "rel"=>"http://schemas.google.com/spreadsheets/2006#cellsfeed",
       "type"=>"application/atom+xml"},
    [ snip: cutting off the rest of the XML ]

Come puoi vedere qui, ora possiamo trovare i link (highlighted above) per accedere all'elencoFeed e alle celleFeed. Prima di approfondire l'argomento listFeed, vorrei spiegarvi rapidamente quali dati sono attualmente presenti nel nostro foglio di lavoro di esempio, in modo che possiate sapere che cosa stiamo cercando:

Il nostro foglio di lavoro è molto semplice e ha questo aspetto:

languagesito web
javahttp://java.com
phphttp://php.net

Ecco come si presentano i dati nell'elencoFeed:

irb(main):048:0> listfeed_uri = \
irb(main):049:0* worksheet_data["entry"][0]["link"][0]["href"]
=> "http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full"

irb(main):050:0> response = get_feed(listfeed_uri, headers)
=> #<Net::HTTPOK 200 OK readbody=true>
irb(main):051:0> listfeed_doc = \ 
irb(main):052:0* XmlSimple.xml_in(response.body, 'KeyAttr' => 'name')
=> {"totalResults"=>["2"], "category"=>[{"term" ... [ snip ]

# Again we parse the XML and then pretty print it
irb(main):053:0> pp listfeed_doc
{"totalResults"=>["2"],
 "category"=>
  [{"term"=>"http://schemas.google.com/spreadsheets/2006#list",
    "scheme"=>"http://schemas.google.com/spreadsheets/2006"}],
 "title"=>[{"type"=>"text", "content"=>"Programming language links"}],
 "author"=>
  [{"name"=>["test.api.jhartmann"],
    "email"=>["test.api.jhartmann@gmail.com"]}],
 "startIndex"=>["1"],
 "id"=>
  ["http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full"],
 "entry"=>
  [{"category"=>
     [{"term"=>"http://schemas.google.com/spreadsheets/2006#list",
       "scheme"=>"http://schemas.google.com/spreadsheets/2006"}],
    "language"=>["java"],
    "title"=>[{"type"=>"text", "content"=>"ruby"}],
    "website"=>["http://java.com"],
    "id"=>
     ["http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full/cn6ca"],
    "content"=>
     {"type"=>"text", "content"=>"website: http://java.com"},
    "link"=>
     [{"href"=>
        "http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full/cn6ca",
       "rel"=>"self",
       "type"=>"application/atom+xml"},
      {"href"=>
        "http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full/cn6ca/1j81anl6096",
       "rel"=>"edit",
       "type"=>"application/atom+xml"}],
    "updated"=>["2008-03-20T22:19:51.739Z"]},
   {"category"=>
     [{"term"=>"http://schemas.google.com/spreadsheets/2006#list",
       "scheme"=>"http://schemas.google.com/spreadsheets/2006"}],
    "language"=>["php"],
    "title"=>[{"type"=>"text", "content"=>"php"}],
    "website"=>["http://php.net"],
    "id"=>
     ["http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full/cokwr"],
    "content"=>{"type"=>"text", "content"=>"website: http://php.net"},
    [ snip ]

Come puoi vedere, listFeed restituisce i contenuti del foglio di lavoro creando una voce per ogni riga. Presuppone che la prima riga del foglio di lavoro contenga le intestazioni di cella, quindi genera dinamicamente intestazioni XML in base ai dati in quella riga. Un'analisi del file XML effettivo ti aiuterà a capire meglio:

<?xml version='1.0' encoding='UTF-8'?><feed [ snip namespaces ]>
<id>http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full</id>
<updated>2008-03-20T22:19:51.739Z</updated>
<category scheme='http://schemas.google.com/spreadsheets/2006' term='http://schemas.google.com/spreadsheets/2006#list'/>

<title type='text'>Programming language links</title>
[ snip: cutting out links and author information ]
<entry>
    <id>http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full/cn6ca</id>
    [ snip: updated and category ]
    <title type='text'>java</title>
    <content type='text'>website: http://java.com</content>
    <link rel='self' type='application/atom+xml' href='http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full/cn6ca'/>
    <link rel='edit' type='application/atom+xml' href='http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full/cn6ca/1j81anl6096'/>
    <gsx:language>java</gsx:language>
    <gsx:website>http://java.com</gsx:website>
</entry>
<entry>
    <id>http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full/cokwr</id>
    [ snip: updated and category ]
    <title type='text'>php</title>
    <content type='text'>website: http://php.net</content>
    <link rel='self' type='application/atom+xml' href='http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full/cokwr'/>
    <link rel='edit' type='application/atom+xml' href='http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full/cokwr/41677fi0nc'/>
    <gsx:language>php</gsx:language>
    <gsx:website>http://php.net</gsx:website>
</entry>
</feed>

Per fare un rapido confronto, consideriamo il modo in cui le stesse informazioni sono rappresentate nel cella Feed:

# Extract the cellfeed link
irb(main):054:0> cellfeed_uri = \
irb(main):055:0* worksheet_data["entry"][0]["link"][1]["href"]
=> "http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full"
irb(main):056:0> response = \ 
irb(main):057:0* get_feed(cellfeed_uri, headers)
=> #<Net::HTTPOK 200 OK readbody=true>

# Parse into datastructure and print
irb(main):058:0> cellfeed_doc = \ 
irb(main):059:0* XmlSimple.xml_in(response.body, 'KeyAttr' => 'name')
=> {"totalResults"=>["6"], [ snip ]

irb(main):060:0> pp cellfeed_doc
{"totalResults"=>["6"],
 "category"=>
  [{"term"=>"http://schemas.google.com/spreadsheets/2006#cell",
    "scheme"=>"http://schemas.google.com/spreadsheets/2006"}],
 "title"=>[{"type"=>"text", "content"=>"Programming language links"}],
 "rowCount"=>["101"],
 "colCount"=>["20"],
 "author"=>
  [{"name"=>["test.api.jhartmann"],
    "email"=>["test.api.jhartmann@gmail.com"]}],
 "startIndex"=>["1"],
 "id"=>
  ["http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full"],
 "entry"=>
  [{"category"=>
     [{"term"=>"http://schemas.google.com/spreadsheets/2006#cell",
       "scheme"=>"http://schemas.google.com/spreadsheets/2006"}],
    "cell"=>
     [{"col"=>"1",
       "row"=>"1",
       "content"=>"language",
       "inputValue"=>"language"}],
    "title"=>[{"type"=>"text", "content"=>"A1"}],
    "id"=>
     ["http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full/R1C1"],
    "content"=>{"type"=>"text", "content"=>"language"},
    "link"=>
     [{"href"=>
        "http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full/R1C1",
       "rel"=>"self",
       "type"=>"application/atom+xml"},
      {"href"=>
        "http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full/R1C1/8srvbs",
       "rel"=>"edit",
       "type"=>"application/atom+xml"}],
    "updated"=>["2008-03-20T22:19:51.739Z"]},
    [ snip ]

Come puoi vedere qui, vengono restituite 6 voci, una per ogni cella. Ho interrotto tutti gli altri output oltre al valore della cella A1, che contiene la parola "language". Osserva inoltre il link di modifica riportato sopra. Questo link contiene una stringa di versione (8srvbs) alla fine. La stringa della versione è importante quando aggiorni i dati della cella, come faremo alla fine di questo articolo. In questo modo, gli aggiornamenti non verranno sovrascritti. Ogni volta che effettui una richiesta PUT per aggiornare i dati della cella, devi includere l'ultima stringa della cella nella richiesta. Dopo ogni aggiornamento verrà restituita una nuova stringa di versione.

Pubblicazione di contenuti nel feed elenco

La prima cosa di cui abbiamo bisogno per pubblicare contenuti è il link POST per il feed list. Questo link verrà restituito quando verrà richiesto il feed elenco. Contiene l'URL http://schemas.google.com/g/2005#post come valore dell'attributo rel. Dovrai analizzare questo elemento link ed estrarre il relativo attributo href. Innanzitutto, creeremo un piccolo metodo per semplificare la pubblicazione dei post:

irb(main):061:0> def post(uri, data, headers)
irb(main):062:1> uri = URI.parse(uri)
irb(main):063:1> http = Net::HTTP.new(uri.host, uri.port)
irb(main):064:1> return http.post(uri.path, data, headers)
irb(main):065:1> end
=> nil
# Set up our POST url
irb(main):066:0> post_url = \ 
irb(main):067:0* "http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full"
=> "http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full"

# We must use 'application/atom+xml' as MIME type so let's change our headers 
# which were still set to 'application/x-www-form-urlencoded' when we sent our 
# ClientLogin information over https
irb(main):068:0> headers["Content-Type"] = "application/atom+xml"
=> "application/atom+xml"

# Setting up our data to post, using proper namespaces
irb(main):069:0> new_row = \ 
irb(main):070:0* '<atom:entry xmlns:atom="http://www.w3.org/2005/Atom">' << 
irb(main):071:0* '<gsx:language xmlns:gsx="http://schemas.google.com/spreadsheets/2006/extended">' <<
irb(main):072:0* 'ruby</gsx:language>' << 
irb(main):073:0* '<gsx:website xmlns:gsx="http://schemas.google.com/spreadsheets/2006/extended">' <<
irb(main):074:0* 'http://ruby-lang.org</gsx:website>' << 
irb(main):075:0* '</atom:entry>'
=> "<atom:entry xmlns:atom=\"http://www.w3.org/2005/Atom\"><gsx:language ... [ snip ] 

# Performing the post
irb(main):076:0> post_response = post(post_url, new_row, headers) 
=> #<Net::HTTPCreated 201 Created readbody=true>

Lo stato 201 indica che il nostro post è stato pubblicato correttamente.

Utilizzare il feed Cell per aggiornare i contenuti

Dalla documentazione possiamo vedere che il feed delle celle preferisce le richieste PUT sui contenuti esistenti. Ma siccome le informazioni recuperate dal cella in precedenza erano solo i dati che erano già nel nostro vero foglio di lavoro, come possiamo aggiungere nuove informazioni? Dovremo semplicemente effettuare una richiesta per ogni cella vuota in cui vogliamo inserire i dati. Lo snippet seguente mostra come recuperare la cella vuota R5C1 (riga 5, colonna 1) in cui vogliamo inserire alcune informazioni sul linguaggio di programmazione Python.

La nostra variabile originale cellfeed_uri conteneva solo l'URI del feed della cella stessa. Ora vogliamo aggiungere la cella che stiamo cercando di modificare e ottenere la stringa della versione delle celle per apportare la nostra modifica:

# Set our query URI
irb(main):077:0> cellfeed_query = cellfeed_uri + '/R5C1'
=> "http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full/R5C1"

# Request the information to extract the edit link
irb(main):078:0> cellfeed_data = get_feed(cellfeed_query, headers)
=> #<Net::HTTPOK 200 OK readbody=true>
irb(main):079:0> cellfeed_data.body
=> "<?xml version='1.0' encoding='UTF-8'?>
<entry xmlns='http://www.w3.org/2005/Atom' xmlns:gs='http://schemas.google.com/spreadsheets/2006' xmlns:batch='http://schemas.google.com/gdata/batch'>
<id>http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full/R5C1</id>
<updated>2008-03-24T21:55:36.462Z</updated>
<category scheme='http://schemas.google.com/spreadsheets/2006' term='http://schemas.google.com/spreadsheets/2006#cell'/>
<title type='text'>A5</title>
<content type='text'>
</content>
<link rel='self' type='application/atom+xml' href='http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full/R5C1'/>
<link rel='edit' type='application/atom+xml' href='http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full/R5C1/47pc'/>
<gs:cell row='5' col='1' inputValue=''>
</gs:cell>
</entry>"

Come puoi vedere nell'elenco del codice sopra, la stringa della versione è 47pc. Potresti dover scorrere in fondo verso destra. Per semplificare le cose, creiamo un metodo pratico che ci consenta la stringa di versione per ogni cella che ci interessa:

irb(main):080:0> def get_version_string(uri, headers=nil)
irb(main):081:1> response = get_feed(uri, headers)
irb(main):082:1> require 'rexml/document'
irb(main):083:1> xml = REXML::Document.new response.body
irb(main):084:1> edit_link = REXML::XPath.first(xml, '//[@rel="edit"]')
irb(main):085:1> edit_link_href = edit_link.attribute('href').to_s
irb(main):086:1> return edit_link_href.split(/\//)[10]
irb(main):087:1> end
=> nil

# A quick test
irb(main):088:0> puts get_version_string(cellfeed_query, headers)
47pc
=> nil

Anche se ci stiamo lavorando, potremmo anche scrivere un metodo per eseguire la richiesta PUT oppure, meglio ancora, scrivere un metodo per eseguire l'intero aggiornamento batch. La nostra funzione utilizzerà un array di hash contenenti le seguenti variabili:

  • :batch_id: un identificatore univoco per ogni parte della richiesta in batch.
  • :cell_id: l'ID della cella da aggiornare in formato R#C#, dove la cella A1 viene rappresentata come R1C1.
  • :data: i dati che vogliamo inserire.

irb(main):088:0> def batch_update(batch_data, cellfeed_uri, headers)
irb(main):089:1> batch_uri = cellfeed_uri + '/batch'
irb(main):090:1> batch_request = <<FEED
irb(main):091:1" <?xml version="1.0" encoding="utf-8"?> \
irb(main):092:1" <feed xmlns="http://www.w3.org/2005/Atom" \
irb(main):093:1" xmlns:batch="http://schemas.google.com/gdata/batch" \
irb(main):094:1" xmlns:gs="http://schemas.google.com/spreadsheets/2006" \
irb(main):095:1" xmlns:gd="http://schemas.google.com/g/2005">
irb(main):096:1" <id>#{cellfeed_uri}</id>
irb(main):097:1" FEED
irb(main):098:1> batch_data.each do |batch_request_data|
irb(main):099:2* version_string = get_version_string(cellfeed_uri + '/' + batch_request_data[:cell_id], headers)
irb(main):100:2> data = batch_request_data[:data]
irb(main):101:2> batch_id = batch_request_data[:batch_id]
irb(main):102:2> cell_id = batch_request_data[:cell_id]
irb(main):103:2> row = batch_request_data[:cell_id][1,1]
irb(main):104:2> column = batch_request_data[:cell_id][3,1]
irb(main):105:2> edit_link = cellfeed_uri + '/' + cell_id + '/' + version_string
irb(main):106:2> batch_request<< <<ENTRY
irb(main):107:2" <entry>
irb(main):108:2" <gs:cell col="#{column}" inputValue="#{data}" row="#{row}"/>
irb(main):109:2" <batch:id>#{batch_id}</batch:id>
irb(main):110:2" <batch:operation type="update" />
irb(main):111:2" <id>#{cellfeed_uri}/#{cell_id}</id>
irb(main):112:2" <link href="#{edit_link}" rel="edit" type="application/atom+xml" />
irb(main):113:2" </entry>
irb(main):114:2" ENTRY
irb(main):115:2> end
irb(main):116:1> batch_request << '</feed>'
irb(main):117:1> return post(batch_uri, batch_request, headers)
irb(main):118:1> end
=> nil

# Our sample batch data to insert information about the Python programming language into our worksheet
irb(main):119:0> batch_data = [ \
irb(main):120:0* {:batch_id => 'A', :cell_id => 'R5C1', :data => 'Python'}, \ 
irb(main):121:0* {:batch_id => 'B', :cell_id => 'R5C2', :data => 'http://python.org' } ]
=> [{:cell_id=>"R5C1", :data=>"Python", :batch_id=>"A"}=>{:cell_id=>"R5C2", :data=>"http://python.org", :batch_id=>"B"}]

# Perform the update
irb(main):122:0> response = batch_update(batch_data, cellfeed_uri, headers)
=> #<Net::HTTPOK 200 OK readbody=true>

# Parse the response.body XML and print it
irb(main):123:0> response_xml = XmlSimple.xml_in(response.body, 'KeyAttr' => 'name')
=> [ snip ]

irb(main):124:0> pp response_xml
{"title"=>[{"type"=>"text", "content"=>"Batch Feed"}],
 "xmlns:atom"=>"http://www.w3.org/2005/Atom",
 "id"=>
  ["http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full"],
 "entry"=>
  [{"status"=>[{"code"=>"200", "reason"=>"Success"}],
    "category"=>
     [{"term"=>"http://schemas.google.com/spreadsheets/2006#cell",
       "scheme"=>"http://schemas.google.com/spreadsheets/2006"}],
    "cell"=>
     [{"col"=>"1", "row"=>"5", "content"=>"Python", "inputValue"=>"Python"}],
    "title"=>[{"type"=>"text", "content"=>"A5"}],
    "id"=>
     ["http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full/R5C1",
      "A"],
    "operation"=>[{"type"=>"update"}],
    "content"=>{"type"=>"text", "content"=>"Python"},
    "link"=>
     [{"href"=>
        "http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full/R5C1",
       "rel"=>"self",
       "type"=>"application/atom+xml"},
      {"href"=>
        "http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full/R5C1/49kwzg",
       "rel"=>"edit",
       "type"=>"application/atom+xml"}],
    "updated"=>["2008-03-27T15:48:48.470Z"]},
    [ snip ]

Come puoi vedere, la nostra richiesta in batch è stata accettata perché abbiamo ricevuto il codice di risposta 200 OK. Analizzando il file XML di risposta, possiamo vedere che viene restituito un messaggio separato per ogni singolo elemento :batch_id impostato nell'array response_data. Per ulteriori informazioni sull'elaborazione batch, fai riferimento alla documentazione Elaborazione batch in GData.

Conclusione

Come hai visto, è molto facile utilizzare la shell interattiva di Ruby per sperimentare le API di dati di Google. Siamo riusciti ad accedere ai nostri fogli di lavoro e ai fogli di lavoro utilizzando la funzionalità listFeed e le celleFeed. Inoltre, abbiamo inserito alcuni nuovi dati usando una richiesta POST e poi abbiamo scritto dei metodi per eseguire un aggiornamento batch con solo circa 120 righe di codice. A questo punto, non deve essere troppo difficile includere alcuni di questi semplici metodi in classi e creare un framework riutilizzabile.

Unisciti a noi nei gruppi di discussione per eventuali domande sull'utilizzo di questi strumenti con la tua API di dati di Google preferita.

Il file di un corso con gli esempi di codice descritti sopra è disponibile all'indirizzo http://code.google.com/p/google-data-samples-ruby

Discuti con questo articolo.