Данные Google на рельсах

Эрик Бидельман, команда Google Data API
Февраль 2009 г.

Введение

«Где Ruby в списке клиентских библиотек

Вдохновленный неуемным аппетитом наших разработчиков и непреходящей популярностью Ruby on Rails (RoR), мой коллега Джефф Фишер создал библиотеку утилит Ruby из огненных глубин горы Рока. Обратите внимание, что это не полноценная клиентская библиотека, но она реализует такие фундаментальные функции, как аутентификация и базовые операции с XML. Кроме того, она требует прямой работы с Atom-каналом, используя модуль REXML и XPath.

Аудитория

Эта статья предназначена для разработчиков, заинтересованных в доступе к API Google Data с помощью Ruby, в частности, Ruby on Rails. Предполагается, что читатель имеет некоторое представление о языке программирования Ruby и фреймворке веб-разработки Rails. В большинстве примеров я остановлюсь на API списка документов , но те же принципы применимы к любому API данных .

Начиная

Требования

Установка библиотеки утилит Google Data Ruby

Чтобы получить библиотеку, вы можете либо загрузить исходный код библиотеки непосредственно с хостинга проекта, либо установить gem:

sudo gem install gdata

Совет : для верности выполните команду gem list --local чтобы убедиться, что пакет установлен правильно.

Аутентификация

ClientLogin

ClientLogin позволяет вашему приложению программно авторизовать пользователей в их учётных записях Google или G Suite. После проверки учётных данных пользователя Google выдаёт токен авторизации, который будет использоваться в последующих запросах API. Токен остаётся действительным в течение определённого времени, определяемого используемым сервисом Google. В целях безопасности и для обеспечения максимального удобства пользователей следует использовать ClientLogin только при разработке устанавливаемых десктопных приложений. Для веб-приложений предпочтительнее использовать AuthSub или OAuth .

В библиотеке Ruby есть клиентский класс для каждого из API. Например, используйте следующий фрагмент кода для входа в API данных списка документов по user@gmail.com :

client = GData::Client::DocList.new
client.clientlogin('user@gmail.com', 'pa$$word')

The YouTube Data API would be:

client = GData::Client::YouTube.new
client.clientlogin('user@gmail.com', 'pa$$word')

См. полный список реализованных классов сервисов . Если у сервиса нет класса клиента, используйте класс GData::Client::Base . Например, следующий код заставляет пользователей входить в систему с учётной записью G Suite.

client_login_handler = GData::Auth::ClientLogin.new('writely', :account_type => 'HOSTED')
token = client_login_handler.get_token('user@example.com', 'pa$$word', 'google-RailsArticleSample-v1')
client = GData::Client::Base.new(:auth_handler => client_login_handler)

Примечание : По умолчанию библиотека использует HOSTED_OR_GOOGLE для accountType . Возможные значения: HOSTED_OR_GOOGLE , HOSTED или GOOGLE .

Одним из недостатков использования ClientLogin является то, что при неудачных попытках входа в систему вашему приложению могут отправляться запросы CAPTCHA. В этом случае вы можете обработать ошибку, вызвав метод clientlogin() с дополнительными параметрами: client.clientlogin(username, password, captcha_token, captcha_answer) . Подробнее о работе с CAPTCHA см. в полной документации по аутентификации для установленных приложений .

AuthSub

Генерация URL-адреса AuthSubRequest

scope = 'http://www.google.com/calendar/feeds/'
next_url = 'http://example.com/change/to/your/app'
secure = false  # set secure = true for signed AuthSub requests
sess = true
authsub_link = GData::Auth::AuthSub.get_url(next_url, scope, secure, sess)

Предыдущий блок кода создает следующий URL в authsub_link :

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

Вы также можете использовать метод authsub_url объекта клиента. Каждый класс сервиса имеет атрибут authsub_scope по умолчанию, поэтому нет необходимости указывать свой собственный.

client = GData::Client::DocList.new
next_url = 'http://example.com/change/to/your/app'
secure = false  # set secure = true for signed AuthSub requests
sess = true
domain = 'example.com'  # force users to login to a G Suite hosted domain
authsub_link = client.authsub_url(next_url, secure, sess, domain)

Предыдущий блок кода создает следующий URL:

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

Обновление одноразового токена до сеансового токена

AuthSub перенаправит пользователя обратно на http://example.com/change/to/your/app?token=SINGLE_USE_TOKEN после предоставления доступа к своим данным. Обратите внимание, что URL-адрес — это просто наш next_url с одноразовым токеном, добавленным в качестве параметра запроса.

Затем обменяйте одноразовый токен на долгосрочный сеансовый токен:

client.authsub_token = params[:token] # extract the single-use token from the URL query params
session[:token] = client.auth_handler.upgrade()
client.authsub_token = session[:token] if session[:token]

Secure AuthSub очень похож. Единственное отличие — необходимо указать свой закрытый ключ перед обновлением токена:

PRIVATE_KEY = '/path/to/private_key.pem'

client.authsub_token = params[:token]
client.authsub_private_key = PRIVATE_KEY
session[:token] = client.auth_handler.upgrade()
client.authsub_token = session[:token] if session[:token]

Примечание : Чтобы использовать защищённые токены, обязательно установите secure=true при запросе одноразового токена. См. раздел «Генерация URL-адреса AuthSubRequest» выше.

Управление токенами

AuthSub предоставляет два дополнительных обработчика: AuthSubTokenInfo и AuthSubRevokeToken для управления токенами. AuthSubTokenInfo полезен для проверки корректности токена. AuthSubRevokeToken предоставляет пользователям возможность прекратить доступ к своим данным. Рекомендуется использовать AuthSubRevokeToken в вашем приложении. Оба метода поддерживаются библиотекой Ruby.

Чтобы запросить метаданные токена:

client.auth_handler.info

Чтобы отозвать токен сеанса:

client.auth_handler.revoke

Полную документацию по аутентификации AuthSub для веб-приложений см. в полной версии.

OAuth

На момент написания статьи OAuth не был добавлен в модуль GData::Auth .

Использование OAuth в библиотеке утилит должно быть относительно простым при использовании плагина Rails oauth-plugin или гема Ruby oauth . В любом случае вам потребуется создать объект GData::HTTP::Request и передать ему заголовок Authorization , сгенерированный каждой библиотекой.

Доступ к лентам

GET (извлечение данных)

После настройки объекта клиента используйте его метод get() для запроса к фиду Google Data. XPath можно использовать для извлечения определённых элементов Atom. Вот пример извлечения данных из Google Documents пользователя:

feed = client.get('http://docs.google.com/feeds/documents/private/full').to_xml

feed.elements.each('entry') do |entry|
  puts 'title: ' + entry.elements['title'].text
  puts 'type: ' + entry.elements['category'].attribute('label').value
  puts 'updated: ' + entry.elements['updated'].text
  puts 'id: ' + entry.elements['id'].text
  
  # Extract the href value from each <atom:link>
  links = {}
  entry.elements.each('link') do |link|
    links[link.attribute('rel').value] = link.attribute('href').value
  end
  puts links.to_s
end

POST (создание новых данных)

Используйте клиентский метод post() для создания новых данных на сервере. В следующем примере new_writer@example.com будет добавлен в качестве соавтора к документу с идентификатором doc_id .

# Return documents the authenticated user owns
feed = client.get('http://docs.google.com/feeds/documents/private/full/-/mine').to_xml
entry = feed.elements['entry']  # first <atom:entry>

acl_entry = <<-EOF
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:gAcl='http://schemas.google.com/acl/2007'>
  <category scheme='http://schemas.google.com/g/2005#kind'
    term='http://schemas.google.com/acl/2007#accessRule'/>
  <gAcl:role value='writer'/>
  <gAcl:scope type='user' value='new_writer@example.com'/>
</entry>
EOF

# Regex the document id out from the full <atom:id>.
# http://docs.google.com/feeds/documents/private/full/document%3Adfrk14g25fdsdwf -> document%3Adfrk14g25fdsdwf
doc_id = entry.elements['id'].text[/full\/(.*%3[aA].*)$/, 1]
response = client.post("http://docs.google.com/feeds/acl/private/full/#{doc_id}", acl_entry)

PUT (обновление данных)

Для обновления данных на сервере используйте клиентский метод put() . Следующий пример обновляет заголовок документа. Предполагается, что у вас есть фид из предыдущего запроса.

entry = feed.elements['entry'] # first <atom:entry>

# Update the document's title
entry.elements['title'].text = 'Updated title'
entry.add_namespace('http://www.w3.org/2005/Atom')
entry.add_namespace('gd','http://schemas.google.com/g/2005')

edit_uri = entry.elements["link[@rel='edit']"].attributes['href']
response = client.put(edit_uri, entry.to_s)

УДАЛИТЬ

Чтобы удалить элемент <atom:entry> или другие данные с сервера, используйте метод delete() . В следующем примере будет удалён документ. В коде предполагается, что у вас есть запись документа из предыдущего запроса.

entry = feed.elements['entry'] # first <atom:entry>
edit_uri = entry.elements["link[@rel='edit']"].attributes['href']
client.headers['If-Match'] = entry.attribute('etag').value  # make sure we don't nuke another client's updates
client.delete(edit_uri)

Создание нового приложения Rails

Обычно первым шагом при создании нового приложения Rails является запуск генераторов шаблонов для создания MVC-файлов. После этого выполняется rake db:migrate для настройки таблиц базы данных. Однако, поскольку наше приложение будет запрашивать данные через API Google Documents List, нам не нужны ни шаблонные шаблоны, ни базы данных. Вместо этого создадим новое приложение и простой контроллер:

rails doclist
cd doclist
ruby script/generate controller doclist

и внесите следующие изменения в config/environment.rb :

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

Первая строка отключает ActiveRecord от приложения. Вторая строка загружает пакет gdata при запуске.

Наконец, я решил подключить маршрут по умолчанию (' / ') к действию documents в DoclistController . Добавьте следующую строку в config/routes.rb :

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

Запустить контроллер

Поскольку мы не создали шаблоны, вручную добавьте действие с именем « all » в DoclistController в app/controllers/doclist_controller.rb .

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

и создайте all.html.erb в app/views/doclist/ :

<%= @foo %>

Запустите веб-сервер и начните разработку.

Теперь вы можете запустить веб-сервер по умолчанию, вызвав команду ruby script/server . Если всё в порядке, при открытии страницы http://localhost:3000/ браузере должно появиться сообщение « I pity the foo! ».

Совет : Не забудьте удалить или переименовать public/index.html .

Как только всё заработает, взгляните на мои финальные DoclistController и ApplicationController , составляющие основу проекта DocList Manager. Также обратите внимание на ContactsController , который обрабатывает вызовы API Google Contacts.

Заключение

Самая сложная часть создания приложения Google Data Rails — это настройка Rails! Однако, на втором месте стоит развёртывание приложения. Для этого я настоятельно рекомендую mod_rails для Apache. Он невероятно прост в настройке, установке и запуске. Вы сможете начать работу в кратчайшие сроки!

Ресурсы

Приложение

Примеры

DocList Manager — это полноценный пример Ruby on Rails, демонстрирующий темы, обсуждаемые в этой статье. Полный исходный код доступен на хостинге проекта.