Google Data on Rails

Eric Bidelman, equipo de las APIs de datos de Google
Febrero de 2009

Introducción

"¿Dónde está Ruby en la lista de bibliotecas cliente?"

Motivado por el apetito voraz de nuestros desarrolladores y la popularidad duradera de Ruby on Rails (RoR), mi colega Jeff Fisher forjó una biblioteca de utilidades de Ruby desde las profundidades ardientes del Monte Doom. Ten en cuenta que no es una biblioteca cliente completa, pero sí controla los aspectos básicos, como la autenticación y la manipulación básica de XML. También requiere que trabajes directamente con el feed Atom usando el módulo REXML y XPath.

Público

Este artículo está dirigido a los desarrolladores interesados en acceder a las APIs de Google Data con Ruby, específicamente Ruby on Rails. Se supone que el lector tiene cierta familiaridad con el lenguaje de programación Ruby y el marco de trabajo de desarrollo web de Rails. Me enfoco en la API de Documents List para la mayoría de los ejemplos, pero los mismos conceptos se pueden aplicar a cualquiera de las APIs de Data.

Comenzar

Requisitos

  • Ruby 1.8.6, nivel de parche 114 o posterior descargar
  • RubyGems 1.3.1 o versiones posteriores descargar
  • Descarga Rails 2.2.2 o versiones posteriores.

Instala la biblioteca de utilidades de Google Data para Ruby

Para obtener la biblioteca, puedes descargar la fuente de la biblioteca directamente desde el alojamiento del proyecto o instalar la gem:

sudo gem install gdata

Sugerencia: Para mayor seguridad, ejecuta gem list --local para verificar que la gem se haya instalado correctamente.

Autenticación

ClientLogin

ClientLogin permite que tu aplicación acceda de forma programática a las cuentas de Google o de G Suite de los usuarios. Después de validar las credenciales del usuario, Google emite un token de autorización para que se haga referencia a él en las solicitudes posteriores a la API. El token sigue siendo válido durante un período determinado, que define el servicio de Google con el que trabajas. Por motivos de seguridad y para brindarles a los usuarios la mejor experiencia, solo debes usar ClientLogin cuando desarrolles aplicaciones instaladas para computadoras. Para las aplicaciones web, se recomienda usar AuthSub o OAuth.

La biblioteca de Ruby tiene una clase de cliente para cada una de las APIs. Por ejemplo, usa el siguiente fragmento de código para acceder a user@gmail.com en la API de Documents List Data:

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

The YouTube Data API would be:

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

Consulta la lista completa de clases de servicio implementadas. Si un servicio no tiene una clase cliente, usa la clase GData::Client::Base. Por ejemplo, el siguiente código obliga a los usuarios a acceder con una cuenta de G Suite.

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

Nota: De forma predeterminada, la biblioteca usa HOSTED_OR_GOOGLE para el accountType. Los valores posibles son HOSTED_OR_GOOGLE, HOSTED o GOOGLE.

Una de las desventajas de usar ClientLogin es que tu aplicación puede recibir desafíos de CAPTCHA en los intentos de acceso fallidos. Si eso sucede, puedes controlar el error llamando al método clientlogin() con sus parámetros adicionales: client.clientlogin(username, password, captcha_token, captcha_answer). Consulta la documentación completa sobre la autenticación para aplicaciones instaladas para obtener más información sobre cómo controlar los CAPTCHA.

AuthSub

Cómo generar la URL de 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)

El bloque de código anterior crea la siguiente URL en 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

También puedes usar el método authsub_url del objeto cliente. Cada clase de servicio tiene establecido un atributo authsub_scope predeterminado, por lo que no es necesario que especifiques el tuyo.

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)

El bloque de código anterior crea la siguiente 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

Cómo actualizar un token de un solo uso a un token de sesión

AuthSub redireccionará al usuario a http://example.com/change/to/your/app?token=SINGLE_USE_TOKEN una vez que haya otorgado acceso a sus datos. Ten en cuenta que la URL es solo nuestro next_url con el token de un solo uso agregado como parámetro de consulta.

A continuación, intercambia el token de un solo uso por un token de sesión de larga duración:

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 es muy similar. La única adición es configurar tu clave privada antes de actualizar el token:

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

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

Nota: Para usar tokens seguros, asegúrate de establecer secure=true cuando solicites un token de un solo uso. Consulta Cómo generar la URL de AuthSubRequest, que se encuentra más arriba.

Administración de tokens

AuthSub proporciona dos controladores adicionales, AuthSubTokenInfo y AuthSubRevokeToken, para administrar tokens. AuthSubTokenInfo es útil para verificar la validez de un token. AuthSubRevokeToken les brinda a los usuarios la opción de interrumpir el acceso a sus datos. Tu app debe usar AuthSubRevokeToken como práctica recomendada. Ambos métodos son compatibles con la biblioteca de Ruby.

Para consultar los metadatos de un token, haz lo siguiente:

client.auth_handler.info

Para revocar un token de sesión, haz lo siguiente:

client.auth_handler.revoke

Consulta la documentación completa sobre la autenticación de AuthSub para aplicaciones web para obtener todos los detalles sobre AuthSub.

OAuth

Al momento de escribir este artículo, OAuth no se había agregado al módulo GData::Auth.

Usar OAuth en la biblioteca de utilidades debería ser relativamente sencillo cuando se usa el oauth-plugin de Rails o la gema oauth de Ruby. En cualquier caso, querrás crear un objeto GData::HTTP::Request y pasarle el encabezado Authorization que genera cada biblioteca.

Cómo acceder a los feeds

GET (recuperación de datos)

Una vez que hayas configurado un objeto cliente, usa su método get() para consultar un feed de datos de Google. XPath se puede usar para recuperar elementos Atom específicos. A continuación, se muestra un ejemplo de cómo recuperar los documentos de Google de un usuario:

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 (creación de datos nuevos)

Usa el método post() de un cliente para crear datos nuevos en el servidor. En el siguiente ejemplo, se agregará new_writer@example.com como colaborador al documento con el ID doc_id.

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

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

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

PUT (actualización de datos)

Para actualizar los datos en el servidor, usa el método put() de un cliente. En el siguiente ejemplo, se actualizará el título de un documento. Se supone que tienes un feed de una búsqueda anterior.

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)

BORRAR

Para borrar un elemento <atom:entry> o cualquier otro dato del servidor, usa el método delete(). En el siguiente ejemplo, se borrará un documento. El código supone que tienes una entrada de documento de una búsqueda anterior.

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)

Cómo crear una nueva aplicación de Rails

Por lo general, el primer ejercicio para crear una app de Rails nueva implica ejecutar los generadores de scaffold para crear tus archivos de MVC. Después de eso, se ejecuta rake db:migrate para configurar las tablas de la base de datos. Sin embargo, dado que nuestra aplicación consultará la API de Google Documents List para obtener datos, no necesitamos mucho scaffolding genérico ni bases de datos. En su lugar, crea una aplicación y un controlador simples nuevos:

rails doclist
cd doclist
ruby script/generate controller doclist

y realiza los siguientes cambios en config/environment.rb:

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

La primera línea desvincula ActiveRecord de la aplicación. La segunda línea carga la gema gdata durante el inicio.

Por último, decidí conectar la ruta predeterminada ("/") a la acción documents en DoclistController. Agrega esta línea a config/routes.rb:

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

Cómo iniciar un controlador

Como no generamos código de ejemplo, agrega manualmente una acción llamada "all" al DoclistController en app/controllers/doclist_controller.rb.

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

y crea all.html.erb en app/views/doclist/:

<%= @foo %>

Inicia el servidor web y comienza el desarrollo

Ahora deberías poder iniciar el servidor web predeterminado invocando ruby script/server. Si todo está bien, al dirigir el navegador a http://localhost:3000/, debería mostrarse "I pity the foo!".

Nota: No olvides quitar o cambiar el nombre de public/index.html.

Una vez que todo funcione, consulta mi DoclistController y ApplicationController finales para conocer el contenido principal del proyecto DocList Manager. También te recomendamos que consultes ContactsController, que controla las llamadas a la API de Contactos de Google.

Conclusión

La parte más difícil de crear una app de Google Data Rails es configurar Rails. Sin embargo, la segunda tarea más importante es implementar tu aplicación. Para ello, te recomiendo mod_rails para Apache. Es muy fácil de configurar, instalar y ejecutar. ¡Estarás listo para comenzar en muy poco tiempo!

Recursos

Apéndice

Ejemplos

El Administrador de DocList es una muestra completa de Ruby on Rails que demuestra los temas analizados en este artículo. El código fuente completo está disponible en el alojamiento del proyecto.