Использование Ruby с API данных Google

Йохен Хартманн, команда API данных Google
апрель 2008 г.

Введение

Ruby — это динамический язык сценариев, которому в последние годы уделяется большое внимание благодаря популярной среде веб-разработки Rails. В этой статье объясняется, как использовать Ruby для взаимодействия со службами Google Data API. Мы не будем фокусироваться на Rails, вместо этого нас больше интересует объяснение базовых HTTP-команд и структуры наших фидов. За всеми представленными здесь примерами можно следить из командной строки, используя интерактивную оболочку Ruby irb .

Как вы помните из статьи cURL , API данных Google используют протокол публикации Atom для представления, создания и обновления веб-ресурсов. Прелесть этого протокола в том, что стандартные команды HTTP используются для формулирования запросов, на которые отвечают стандартные коды состояния HTTP .

Глаголы, которые мы будем использовать в этой статье, — GET для извлечения контента, POST для загрузки нового контента и PUT для обновления существующего контента. Некоторые из стандартных кодов, с которыми вы можете столкнуться при использовании API данных Google, — это 200 , обозначающий успешное получение фида или записи, или 201 , обозначающий успешное создание или обновление ресурса. Если что-то пойдет не так, например, при отправке неправильно сформированного запроса, в ответ будет отправлен код 400 (означающий «Неверный запрос»). В теле ответа будет предоставлено более подробное сообщение с пояснением, что именно пошло не так.

Ruby предоставляет хороший вариант отладки как часть модуля «Сеть». Однако, чтобы эти примеры кода были достаточно короткими, я не включил его здесь.

Получение и установка Ruby

Ruby можно установить с помощью большинства систем управления пакетами, если вы используете Linux. Для других операционных систем и получения полного исходного кода посетите http://www.ruby-lang.org/en/downloads/ . Irb , интерактивная оболочка, которую мы собираемся использовать для этих примеров, должна быть установлена ​​по умолчанию. Чтобы следовать приведенным здесь примерам кода, вам также потребуется установить XmlSimple — небольшую библиотеку для разбора XML в структуры данных Ruby. Чтобы получить/установить XmlSimple, посетите http://xml-simple.rubyforge.org/

Если у вас есть копия Ruby, работающая на вашем компьютере, вы можете использовать пакет Net:HTTP для выполнения простых запросов к службам данных Google. Фрагмент ниже показывает, как выполнить необходимый импорт из интерактивной оболочки Ruby. Что мы делаем, так это требуем пакет 'net/http', анализируем URL-адрес видеопотока с самым высоким рейтингом с YouTube, а затем выполняем 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>

Этот запрос должен был вывести в командную строку довольно много XML-кода. Вы могли заметить, что все элементы содержатся в элементе <feed> и называются элементами <entry> . Давайте пока не будем беспокоиться о форматировании XML, я просто хотел объяснить, как сделать базовый запрос API данных Google с использованием HTTP. Теперь мы собираемся переключить API и сосредоточиться на электронных таблицах, поскольку информация, которую мы можем отправлять и получать, более «дружественна к командной строке».

Аутентификация | Использование API таблиц Google

Мы снова начнем с получения потока элементов входа. На этот раз, хотя мы хотим работать с нашими собственными электронными таблицами. Для этого мы должны сначала пройти аутентификацию в службе учетных записей Google.

Как вы помните из документации по GData Authentication , существует два способа аутентификации в службах Google. AuthSub предназначен для веб-приложений и, в двух словах, включает в себя процесс обмена токенами. Реальное преимущество AuthSub заключается в том, что вашему приложению не нужно хранить учетные данные пользователя. ClientLogin предназначен для «установленных» приложений. В процессе ClientLogin имя пользователя и пароль отправляются службам Google через https вместе со строкой, идентифицирующей службу, которую вы собираетесь использовать. Служба Google Spreadsheets API идентифицируется строкой .

Вернемся к нашей интерактивной оболочке, давайте авторизуемся в Google. Обратите внимание, что мы используем https для отправки нашего запроса аутентификации и учетных данных:

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 ]

ХОРОШО. Итак, теперь, когда мы аутентифицированы, давайте попробуем получить наши собственные электронные таблицы, используя запрос к

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

Поскольку это аутентифицированный запрос, мы также хотим передать наши заголовки. На самом деле, поскольку мы будем делать несколько запросов для разных каналов, мы могли бы также обернуть эту функциональность в простую функцию, которую мы назовем 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> ...

Опять же, мы видим много XML, который я не выделил выше, поскольку вам не нужно беспокоиться о его расшифровке из командной строки. Чтобы сделать вещи более удобными для пользователя, давайте вместо этого разберем их в структуру данных, используя 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 ]

Получение рабочих листов

Итак, как вы можете видеть в выводе выше, мой фид содержит 6 электронных таблиц. Чтобы эта статья была короткой, я отрезал остальную часть вывода XML выше (как и в большинстве других листингов). Чтобы углубиться в эту таблицу, нам нужно будет выполнить еще несколько шагов:

  1. Получить ключ электронной таблицы
  2. Используйте ключ электронной таблицы, чтобы получить наш канал рабочего листа
  3. Получите идентификатор рабочего листа, который мы хотим использовать
  4. Запросите CellFeed или listFeed для доступа к фактическому содержимому рабочего листа.

Это может показаться большой работой, но я покажу вам, что все довольно просто, если мы напишем несколько простых методов. CellFeed и listFeed — это два разных представления фактического содержимого ячейки рабочего листа. ListFeed представляет целую строку информации и рекомендуется для отправки новых данных. CellFeed представляет отдельные ячейки и используется либо для отдельных обновлений ячеек, либо для пакетных обновлений многих отдельных ячеек (в обоих случаях используется PUT). Дополнительные сведения см. в документации по API таблиц Google .

Сначала нам нужно извлечь ключ электронной таблицы (выделенный в выходных данных XML выше), чтобы затем получить канал рабочей таблицы:

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

Как видите, теперь мы можем найти ссылки ( highlighted above ) для доступа к listFeed и CellFeed. Прежде чем мы углубимся в listFeed, позвольте мне быстро объяснить, какие данные в настоящее время существуют в нашем примере электронной таблицы, чтобы вы знали, что мы ищем:

Наша электронная таблица очень проста и выглядит примерно так:

язык Веб-сайт
Джава http://java.com
php http://php.net

А вот как эти данные выглядят в 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 ]

Как видите, listFeed возвращает содержимое вашего рабочего листа, создавая запись для каждой строки. Предполагается, что первая строка электронной таблицы содержит заголовки ваших ячеек, а затем динамически генерирует заголовки XML на основе данных в этой строке. Просмотр фактического XML поможет объяснить это дальше:

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

Для быстрого сравнения давайте посмотрим, как одна и та же информация представлена ​​в CellFeed:

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

Как видите, возвращается 6 записей, по одной для каждой ячейки. Я отключил все остальные выходные данные, кроме значения для ячейки A1, которое содержит слово «язык». Также обратите внимание на ссылку редактирования , показанную выше. Эта ссылка содержит строку версии (8srvbs) в конце. Строка версии важна при обновлении данных ячейки, что мы и сделаем в конце этой статьи. Это гарантирует, что обновления не будут перезаписаны. Всякий раз, когда вы делаете запрос PUT для обновления данных ячейки, вы должны включить строку последней версии ячейки в свой запрос. Новая строка версии будет возвращена после каждого обновления.

Размещение контента в listFeed

Первое, что нам нужно для публикации контента, — это ссылка POST для listFeed. Эта ссылка будет возвращена при запросе фида списка. Он будет содержать URL http://schemas.google.com/g/2005#post в качестве значения атрибута rel . Вам нужно будет проанализировать этот элемент ссылки и извлечь его атрибут href . Сначала мы создадим небольшой метод, чтобы упростить отправку сообщений:

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>

Статус 201 означает, что наша публикация прошла успешно.

Использование CellsFeed для обновления содержимого

Из документации видно, что фид ячеек предпочитает запросы PUT к существующему контенту. Но поскольку информация, которую мы получили из CellFeed ранее, была только данными, которые уже были в нашей фактической электронной таблице, как мы можем добавить новую информацию? Нам просто нужно будет сделать запрос на каждую пустую ячейку, в которую мы хотим ввести данные. Фрагмент ниже показывает, как получить пустую ячейку R5C1 (строка 5, столбец 1), в которую мы хотим вставить некоторую информацию о языке программирования Python.

Наша первоначальная переменная cellfeed_uri содержала только URI самого сотового канала. Теперь мы хотим добавить ячейку, которую мы хотим отредактировать, и получить строку версии этой ячейки, чтобы внести изменения:

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

Как видно из приведенного выше кода, строка версии — 47pc . (Возможно, вам придется прокрутить весь путь до правой стороны.) Чтобы упростить задачу, давайте создадим удобный метод, который получает строку версии для любой интересующей нас ячейки:

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

Пока мы этим занимаемся, мы можем также написать метод для выполнения запроса PUT, или, что еще лучше, давайте напишем метод для выполнения всего пакетного обновления. Наша функция будет принимать массив хэшей, содержащий следующие переменные:

  • :batch_id — уникальный идентификатор для каждой части пакетного запроса.
  • :cell_id — идентификатор обновляемой ячейки в формате R#C#, где ячейка A1 будет представлена ​​как R1C1.
  • :data — данные, которые мы хотим вставить.

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 ]

Как видите, наш пакетный запрос был выполнен успешно, так как мы получили код ответа 200 OK. Анализируя XML-код ответа, мы видим, что для каждого отдельного :batch_id , который мы установили в нашем массиве response_data , возвращается отдельное сообщение. Дополнительные сведения о пакетной обработке см. в документации Пакетная обработка в GData .

Заключение

Как вы видели, интерактивную оболочку Ruby очень легко использовать для экспериментов с API данных Google. Мы смогли получить доступ к нашим электронным таблицам и рабочим листам, используя как listFeed, так и CellFeed. Кроме того, мы вставили некоторые новые данные с помощью POST-запроса, а затем написали методы для выполнения пакетного обновления, написав всего около 120 строк кода. С этого момента не должно быть слишком сложно обернуть некоторые из этих простых методов в классы и создать себе повторно используемый фреймворк.

Присоединяйтесь к нам в дискуссионных группах , если у вас есть какие-либо вопросы об использовании этих инструментов с вашим любимым API данных Google.

Файл класса с приведенными выше примерами кода можно найти по адресу http://code.google.com/p/google-data-samples-ruby .

Обсудить эту статью!