Datos de Google en Rails

Eric Bidelman, equipo de API de datos de Google
Febrero de 2009

Introducción

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

Motivado por el ferviente apetito de nuestros desarrolladores y la perdurable popularidad de Ruby on Rails (RoR), mi colega Jeff Fisher ha creado una biblioteca de utilidades de Ruby a partir de las profundidades intrépidas de Mount Doom. Ten en cuenta que no es una biblioteca cliente completa, pero sí controla aspectos básicos como la autenticación y la manipulación básica de XML. También requiere que trabajes directamente con el feed de Atom mediante el módulo REXML y XPath.

Público

Este artículo está dirigido a desarrolladores interesados en acceder a las API de datos de Google a través de Ruby, específicamente en Ruby on Rails. Se supone que el lector está familiarizado con el lenguaje de programación Ruby y con el marco de trabajo de desarrollo web de Rails. Me enfoco en la API de lista de documentos para la mayoría de las muestras, pero los mismos conceptos se pueden aplicar a cualquiera de las API de datos.

Cómo comenzar

Requisitos

Instala la biblioteca de utilidades de datos de Google para Ruby

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

sudo gem install gdata

Sugerencia: Por si acaso, ejecuta gem list --local para verificar que la gema esté instalada correctamente.

Autenticación

ClientLogin

ClientLogin permite que tu aplicación acceda de manera programática a los usuarios a su cuenta de Google o G Suite. Después de validar las credenciales del usuario, Google emite un token de Auth al que se hace referencia en las solicitudes a la API posteriores. El token permanece válido por un período determinado, definido por el servicio de Google con el que estés trabajando. Por motivos de seguridad y para que los usuarios disfruten de una experiencia óptima, solo debes usar ClientLogin cuando desarrolles aplicaciones instaladas de escritorio. Para las aplicaciones web, se prefiere el uso de AuthSub o OAuth.

La biblioteca de Ruby tiene una clase de cliente para cada una de las API. Por ejemplo, usa el siguiente fragmento de código para acceder user@gmail.com a la API de datos de listas de documentos:

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 de 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 cuando se producen intentos de acceso fallidos. Si eso sucede, puedes manejar 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 de Autenticación para aplicaciones instaladas a fin de obtener más información sobre cómo trabajar con CAPTCHA.

AuthSub

Genera 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 de cliente. Cada clase de servicio estableció un atributo authsub_scope predeterminado, por lo que no es necesario especificar uno propio.

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

Actualiza 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 se le haya otorgado acceso a sus datos. Ten en cuenta que la URL es solo nuestra next_url con el token de un solo uso agregado como un parámetro de consulta.

A continuación, intercambia el token de un solo uso por uno 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]

AuthSub seguro es muy similar. La única adición es configurar la 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 configurar secure=true cuando solicites un token de un solo uso. Consulta Cómo generar la URL de AuthSubRequest más arriba.

Administración de tokens

AuthSub proporciona dos controladores adicionales, AuthSubTokenInfo y AuthSubRevocaToken para administrar los tokens. AuthSubTokenInfo es útil para verificar la validez de un token. AuthSubRevokeToken brinda a los usuarios la opción de descontinuar el acceso a sus datos. Como práctica recomendada, tu app debería usar AuthSubRevokeToken. 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 de Autenticación de AuthSub para aplicaciones web para obtener la información más completa de AuthSub.

OAuth

Al momento de redactar este artículo, OAuth no se agregó al módulo GData::Auth.

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

Cómo acceder a los feeds

GET (obtención de datos)

Una vez que hayas configurado un objeto de cliente, usa su método get() para consultar un feed de datos de Google. XPath se puede usar para recuperar elementos Atom específicos. Aquí te mostramos un ejemplo de recuperación de 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á a 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 datos en el servidor, usa el método put() de un cliente. En el siguiente ejemplo, se actualiza el título de un documento. Se supone que tiene un feed de una consulta 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 <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 consulta 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)

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 andamiaje para crear tus archivos MVC. Después de eso, se ejecuta rake db:migrate para configurar las tablas de tu base de datos. Sin embargo, debido a que nuestra aplicación consultará los datos para la API de la Lista de documentos de Google, no necesitamos poca estructura básica o bases de datos. En su lugar, crea una aplicación nueva y un controlador simple:

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 desenchufa ActiveRecord de la aplicación. En la segunda línea, se carga la gema gdata al inicio.

Por último, elegí 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'

Iniciar un control

Como no generamos una estructura, agrega de forma manual una acción llamada "all" a DoclistController en app/controllers/doclist_controller.rb.

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

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

<%= @foo %>

Inicie el servidor web y comience el desarrollo

Ahora deberías poder iniciar el servidor web predeterminado si invocas ruby script/server. Si todo resulta bien, si tu navegador apunta a http://localhost:3000/, debería mostrar "I pity the foo!".

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

Una vez que todo esté funcionando, echa un vistazo a mi DoclistController final y a ApplicationController para conocer la carne del proyecto en DocList Manager. También querrás ver ContactsController, que maneja 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, un segundo cercano es implementar tu aplicación. Por eso, recomiendo especialmente mod_rails para Apache. Es muy fácil de configurar, instalar y ejecutar. ¡Comenzarás enseguida!

Recursos

Apéndice

Ejemplos

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