Utiliser Ruby avec les API Google Data

Jochen Hartmann, équipe Google Data APIs
Avril 2008

Introduction

Ruby est un langage de script dynamique qui a fait l'objet d'une grande attention ces dernières années en raison du framework de développement Web Rails populaire. Cet article explique comment utiliser Ruby pour interagir avec les services de l'API Google Data. Nous ne nous concentrerons pas sur Rails, mais plutôt sur les commandes HTTP sous-jacentes et la structure de nos flux. Vous pouvez suivre tous les exemples présentés ici à partir de la ligne de commande en utilisant irb, l'interface système interactive de Ruby.

Comme nous vous l'avons indiqué dans l'article sur cURL, les API Google Data utilisent le protocole de publication Atom pour représenter, créer et mettre à jour des ressources Web. La beauté de ce protocole est que des verbes HTTP standards sont utilisés pour formuler les requêtes auxquelles on répond avec des codes d'état HTTP standards.

Les verbes que nous utiliserons dans cet article sont GET pour récupérer du contenu, POST pour importer un nouveau contenu et PUT pour mettre à jour du contenu existant. Certains des codes standards que vous pouvez rencontrer lors de l'utilisation des API Google Data sont 200 pour représenter la réussite de la récupération d'un flux ou d'une entrée ; ou 201 pour représenter la création ou la mise à jour réussie d'une ressource. En cas de problème, par exemple lorsqu'une requête mal formulée est envoyée, un code 400 (qui signifie "Requête incorrecte") est renvoyé. Un message plus détaillé sera affiché dans le corps de la réponse, expliquant ce qui s'est passé exactement.

Ruby propose une option de débogage intéressante dans le module "Net". Toutefois, pour des raisons de concision, je ne l'ai pas activé ici.

Obtenir et installer Ruby

Ruby peut être installé avec la plupart des systèmes de gestion de packages si vous utilisez Linux. Pour les autres systèmes d'exploitation et pour obtenir le code source complet, veuillez consulter la page http://www.ruby-lang.org/en/downloads/. Irb, l'interface système interactive que nous allons utiliser pour ces exemples doit être installée par défaut. Pour suivre les exemples de code listés ici, vous devez également installer XmlSimple, une petite bibliothèque permettant d'analyser le XML dans des structures de données Ruby. Pour obtenir/installer XmlSimple, accédez à http://xml-simple.rubyforge.org.

Une fois qu'une copie de Ruby est en cours d'exécution sur votre machine, vous pouvez utiliser le package Net:HTTP pour envoyer des requêtes de base aux services de données de Google. L'extrait ci-dessous montre comment effectuer les importations nécessaires depuis l'interface système interactive de Ruby. Nous devons demander le package "net/http", analyser l'URL du flux vidéo le mieux noté de YouTube, puis envoyer une requête 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>

Cette requête aurait dû transmettre un grand nombre de fichiers XML à la ligne de commande. Vous avez peut-être remarqué que tous les éléments se trouvent dans un élément <feed> et sont appelés éléments <entry>. Pour l'instant, nous n'avons pas à nous soucier du formatage XML. Je vais juste vous expliquer comment effectuer une requête d'API Google Data de base à l'aide de HTTP. Nous allons maintenant passer aux API et nous concentrer sur les feuilles de calcul, car les informations que nous pouvons envoyer et récupérer sont plus "conviviales sur la ligne de commande".

Authentification | Utilisation de l'API Google Tableur

Nous allons de nouveau commencer par récupérer un flux d'éléments d'entrée. Cette fois, nous allons utiliser nos propres feuilles de calcul. Pour ce faire, nous devons d'abord nous authentifier auprès du service Comptes Google.

Comme nous l'avons vu dans la documentation sur l'authentification GData, vous pouvez vous authentifier auprès des services Google de deux manières. AuthSub est destiné aux applications Web et, en résumé, implique un processus d'échange de jetons. Le véritable avantage d'AuthSub est que votre application n'a pas besoin de stocker des identifiants utilisateur. ClientLogin est destiné aux applications "installées". Au cours du processus ClientLogin, le nom d'utilisateur et le mot de passe sont envoyés aux services de Google via https, ainsi qu'une chaîne identifiant le service que vous souhaitez utiliser. Le service de l'API Google Sheets est identifié par la chaîne wise.

Revenons à notre interface système interactive et authentifions-nous auprès de Google. Notez que nous utilisons https pour envoyer notre demande d'authentification et nos identifiants:

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. Maintenant que nous sommes authentifiés, essayons de récupérer nos propres feuilles de calcul à l'aide d'une requête pour

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

Comme il s'agit d'une requête authentifiée, nous voulons également transmettre nos en-têtes. Étant donné que nous allons effectuer plusieurs requêtes pour différents flux, nous pourrions tout aussi bien intégrer cette fonctionnalité dans une fonction simple que nous appellerons 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> ...

Là encore, nous voyons de nombreux fichiers XML que nous avons soulignés plus haut, car vous n'avez pas à vous soucier de le déchiffrer à partir de la ligne de commande. Pour rendre les choses plus conviviales, analysons-les plutôt en une structure de données à l'aide de 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 ]

Obtenir des feuilles de calcul

Comme vous pouvez le voir dans le résultat ci-dessus, mon flux contient 6 feuilles de calcul. Afin que cet article reste concis, nous avons coupé le reste du contenu XML fourni ci-dessus (ainsi que dans la plupart des autres fiches). Pour examiner cette feuille de calcul plus en détail, nous devons effectuer quelques étapes supplémentaires:

  1. Obtenir la clé de la feuille de calcul
  2. Utiliser la clé de la feuille de calcul pour obtenir le flux de la feuille de calcul
  3. Obtenir l'ID de la feuille de calcul que nous voulons utiliser
  4. Demander à "cellsFeed" ou à "listFeed" d'accéder au contenu réel de la feuille de calcul

Cela peut sembler compliqué, mais je vais vous montrer qu'il est assez facile d'écrire quelques méthodes simples. Cell cellules et listFeed sont deux représentations différentes du contenu d'une feuille de calcul. Le listFeed représente une ligne d'informations entière. Il est recommandé pour POST de nouvelles données. Ce paramètre représente des cellules individuelles et est utilisé soit pour des mises à jour de cellules individuelles, soit pour des mises à jour par lot vers de nombreuses cellules (à l'aide de PUT). Pour en savoir plus, consultez la documentation de l'API Google Sheets.

Nous devons d'abord extraire la clé de la feuille de calcul (mise en surbrillance dans le résultat XML ci-dessus) pour obtenir le flux de la feuille de calcul:

# 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 ]

Comme vous pouvez le voir ici, nous avons maintenant les liens (highlighted above) pour accéder à listFeed et cellsFeed. Avant d'explorer cet élément listlist, voyons rapidement quelles données existent dans notre exemple de feuille de calcul pour que vous sachiez ce que nous recherchons:

Notre feuille de calcul est très simple et se présente comme suit:

languagewebsite
Javahttp://java.com
PHPhttp://php.net

Et voici à quoi ressemblent ces données dans le listFeed:

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 ]

Comme vous pouvez le constater, listFeed renvoie le contenu de votre feuille de calcul en créant une entrée pour chaque ligne. Il part du principe que la première ligne de la feuille de calcul contient les en-têtes de cellule, puis génère dynamiquement des en-têtes XML à partir des données de cette ligne. Consultez le code XML pour mieux comprendre:

<?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>

Pour une comparaison rapide, voyons comment les mêmes informations sont représentées dans les cellules du flux:

# 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 ]

Comme vous pouvez le voir ici, 6 entrées sont renvoyées, une pour chaque cellule. J'ai coupé toutes les autres sorties en plus de la valeur de la cellule A1, qui contient le mot "language". Notez également le lien edit affiché ci-dessus. Ce lien contient une chaîne de version (8srvbs) à la fin. La chaîne de version est importante lors de la mise à jour des données de cellule, comme nous le ferons à la fin de cet article. Ainsi, les mises à jour ne seront pas écrasées. Lorsque vous effectuez une requête PUT pour mettre à jour des données de cellule, vous devez inclure la dernière chaîne de version de la cellule dans votre requête. Une nouvelle chaîne de version est renvoyée après chaque mise à jour.

Publier du contenu dans le listFeed

La première chose dont nous avons besoin pour publier du contenu est le lien POST pour listFeed. Ce lien est renvoyé lorsque le flux de liste est demandé. Il contiendra l'URL http://schemas.google.com/g/2005#post comme valeur de l'attribut rel. Vous devez analyser cet élément link et extraire son attribut href. Tout d'abord, nous allons créer une petite méthode pour faciliter la publication:

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>

L'état 201 indique que l'article a réussi.

Mettre à jour du contenu à l'aide du contenu des cellules

D'après la documentation, nous constatons que le flux de cellules privilégie les requêtes PUT sur le contenu existant. Toutefois, étant donné que les informations que nous avons récupérées précédemment dans les cellules du flux ne concernent que les données figurant déjà dans notre feuille de calcul, comment pouvons-nous ajouter de nouvelles informations ? Il nous suffit d'effectuer une requête pour chaque cellule vide dans laquelle nous voulons saisir des données. L'extrait ci-dessous montre comment récupérer la cellule vide R5C1 (ligne 5, colonne 1) dans laquelle nous voulons insérer des informations sur le langage de programmation Python.

Notre variable d'origine cellfeed_uri ne contenait que l'URI du flux de cellules lui-même. Nous voulons maintenant ajouter la cellule que nous souhaitons modifier et obtenir cette chaîne de version de cellules pour effectuer notre modification:

# 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>"

Comme vous pouvez le voir dans le code ci-dessus, la chaîne de version est 47pc. (Vous devrez peut-être faire défiler l'écran jusqu'en haut à droite.) Pour faciliter les choses, nous allons créer une méthode de commodité qui nous permet d'obtenir la chaîne de version pour chaque cellule qui nous intéresse:

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

Pendant ce temps, nous pouvons aussi écrire une méthode pour exécuter la requête PUT, ou mieux encore, nous devons écrire une méthode pour effectuer l'ensemble de la mise à jour par lot. Notre fonction prend un tableau de hachages contenant les variables suivantes:

  • :batch_id : identifiant unique de chaque élément de la requête par lot.
  • :cell_id : ID de la cellule à mettre à jour au format R#C#, où la cellule A1 est représentée par R1C1.
  • :data : données à insérer.

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 ]

Comme vous pouvez le constater, notre requête par lot a abouti, car nous avons reçu le code de réponse 200 OK. En analysant le code XML de réponse, nous pouvons voir qu'un message distinct est renvoyé pour chaque :batch_id défini dans notre tableau response_data. Pour en savoir plus sur le traitement par lot, veuillez consulter la documentation Traitement par lots dans GData.

Conclusion

Comme vous avez pu le constater, il est très facile d'utiliser l'interface système interactive de Ruby pour tester les API Google Data. Nous avons pu accéder à nos feuilles de calcul et à nos feuilles de calcul à l'aide de listFeed et cellsFeed. Nous avons également ajouté de nouvelles données à l'aide d'une requête POST, puis écrit des méthodes pour effectuer une mise à jour par lot avec seulement 120 lignes de code. À ce stade, il ne devrait pas être trop difficile d'intégrer certaines de ces méthodes simples dans des classes et de créer un framework réutilisable.

Rejoignez les groupes de discussion si vous avez des questions sur l'utilisation de ces outils avec votre API de données Google préférée.

Un fichier de classe contenant les exemples de code détaillés ci-dessus est disponible sur la page http://code.google.com/p/google-data-samples-ruby.

Discutez de cet article.