Fevereiro de 2009
Introdução
"Onde está o Ruby na lista de bibliotecas de cliente?"
Motivado pelo apetite feroz dos nossos desenvolvedores e pela popularidade duradoura do Ruby on Rail (RoR), meu colega Jeff Fisher criou uma biblioteca de utilitários do Ruby das profundezas do Monte Doom. Não se trata de uma biblioteca de cliente completa, mas ela processa os princípios básicos, como autenticação e manipulação básica de XML. Também é necessário trabalhar diretamente com o feed Atom usando o módulo REXML e o XPath.
Público-alvo
Este artigo é destinado a desenvolvedores interessados em acessar as APIs de dados do Google usando Ruby, especificamente o Ruby on Rail. Ele pressupõe que o leitor tenha alguma familiaridade com a linguagem de programação Ruby e o framework de desenvolvimento da Web do Rail. Eu me concentro na API List List para a maioria das amostras, mas os mesmos conceitos podem ser aplicados a qualquer uma das APIs de dados.
Como começar
Requisitos
Como instalar a biblioteca de utilitários do Ruby de dados do Google
Para conseguir a biblioteca, faça o download da origem da biblioteca diretamente na hospedagem do projeto ou instale a gem:
sudo gem install gdata
Dica: para uma boa avaliação, execute gem list --local
para verificar se a gem foi instalada corretamente.
Autenticação
ClientLogin
O ClientLogin permite que seu aplicativo faça o login dos usuários de forma programática na conta do Google ou do G Suite. Ao validar as credenciais do usuário, o Google emite um token Auth para ser usado em solicitações de API subsequentes. O token permanece válido por um período definido, definido por qualquer serviço do Google com que você esteja trabalhando. Por motivos de segurança e para oferecer aos usuários a melhor experiência, você só deve usar ClientLogin ao desenvolver aplicativos instalados para computadores. Para aplicativos da Web, use OAuth ou OAuth.
A biblioteca Ruby tem uma classe de cliente para cada uma das APIs. Por exemplo, use o seguinte snippet de código para fazer login em user@gmail.com
na
API 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')
Veja a lista completa de classes de serviço implementadas.
Se um serviço não tiver uma classe de cliente, use a
classe GData::Client::Base
.
Por exemplo, o código a seguir força os usuários a fazer login com uma conta do 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)
Observação: por padrão, a biblioteca usa HOSTED_OR_GOOGLE
para a accountType
. Os valores possíveis são HOSTED_OR_GOOGLE
,
HOSTED
ou GOOGLE
.
Uma das desvantagens de usar o ClientLogin é que seu aplicativo pode receber desafios CAPTCHA em tentativas de login malsucedidas. Se isso acontecer,
é possível processar o erro chamando o método clientlogin()
com os parâmetros extras:
client.clientlogin(username, password, captcha_token, captcha_answer)
. Consulte a documentação completa do Authentication para aplicativos instalados
para mais informações sobre como lidar com CAPTCHAs.
AuthSub
Como gerar o URL tmpRequest
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)
O bloco de código anterior cria o seguinte URL em 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
Também é possível usar o método authsub_url
do objeto cliente. Cada classe de serviço definiu um atributo authsub_scope
padrão, então não é necessário
especificar o seu.
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)
O bloco de código anterior cria o URL a seguir:
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
Como fazer upgrade de um token de uso único para um token de sessão
Depois de conceder acesso aos dados, o usuário vai ser redirecionado para http://example.com/change/to/your/app?token=SINGLE_USE_TOKEN
. O URL é somente nosso next_url
com o token de uso único
anexado como um parâmetro de consulta.
Em seguida, troque o token de uso único por um token de sessão de longa duração:
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]
O tmp seguro é muito semelhante. A única adição é definindo sua chave privada antes de fazer upgrade do 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]
Observação: para usar tokens seguros, defina secure=true
ao solicitar um
token de uso único. Consulte Como gerar o URL tmpRequest acima.
Gerenciamento de tokens
Há dois gerenciadores adicionais disponíveis para o gerenciamento de tokens, tmpTokenInfo e tmpRevogarToken. AuthSubTokenInfo
é útil para verificar
a validade de um token. O AuthSubRevokeToken
oferece aos usuários a opção de interromper o acesso aos dados. O app precisa usar AuthSubRevokeToken
como prática recomendada. Ambos os métodos são aceitos na biblioteca Ruby.
Para consultar os metadados de um token:
client.auth_handler.info
Para revogar um token de sessão:
client.auth_handler.revoke
Consulte a documentação completa do Authentication Autenticador para aplicativos da Web para saber mais sobre.
OAuth
No momento em que este artigo foi escrito, o OAuth não era adicionado ao módulo GData::Auth
.
O uso do OAuth na biblioteca de utilitários deve ser relativamente simples ao usar o plug-in oauth-plugin (link em inglês) ou a gem oauth do Ruby. Em ambos os casos, crie um objeto GData::HTTP::Request
e transmita a ele o cabeçalho Authorization
gerado por cada biblioteca.
Como acessar feeds
GET (busca de dados)
Depois de configurar um objeto cliente, use o método get()
para consultar um feed de dados do Google. O XPath pode ser usado para
recuperar elementos Atom específicos. Veja um exemplo de recuperação dos Documentos Google de um usuário:
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 (criação de novos dados)
Use o método post()
de um cliente para criar novos dados no servidor. O exemplo a seguir adicionará
new_writer@example.com
como colaborador do documento com o 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 (atualização de dados)
Para atualizar dados no servidor, use o método put()
de um cliente. O exemplo a seguir atualiza o título de um documento.
Você precisa ter um feed de uma 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)
EXCLUIR
Para excluir um <atom:Entry> ou outros dados do servidor, use o método delete()
.
O exemplo a seguir excluirá um documento. O código pressupõe que você tenha uma entrada de documento
de uma 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)
Como criar um novo aplicativo do Rail
Normalmente, o primeiro exercício na criação de um novo aplicativo do Beam envolve a execução de geradores de scaffold para criar os arquivos MVC.
Depois disso, ele está executando rake db:migrate
para configurar as tabelas do banco de dados. No entanto, como nosso aplicativo consultará a API Google Documents
List para dados, precisamos de uma estrutura ou banco de dados genérico. Em vez disso, crie um novo aplicativo e um controlador simples:
rails doclist
cd doclist
ruby script/generate controller doclist
e fazer as seguintes mudanças em config/environment.rb
:
config.frameworks -= [ :active_record, :active_resource, :action_mailer ]
config.gem 'gdata', :lib => 'gdata'
A primeira linha desvincula ActiveRecord
do aplicativo.
A segunda linha carrega a gem gdata
na inicialização.
Por fim, escolhi conectar a rota padrão ("/
") à ação documents
em DoclistController
.
Adicione esta linha a config/routes.rb
:
map.root :controller => 'doclist', :action => 'all'
Iniciar um controlador
Como não geramos andaimes, adicione manualmente uma ação chamada "all
" ao
DoclistController
no app/controllers/doclist_controller.rb
.
class DoclistController < ApplicationController
def all
@foo = 'I pity the foo!'
end
end
e crie all.html.erb
em app/views/doclist/
:
<%= @foo %>
Ativar o servidor da Web e iniciar o desenvolvimento
Agora você poderá iniciar o servidor da Web padrão invocando ruby script/server
.
Se tudo estiver certo, apontar o navegador para http://localhost:3000/
vai exibir "I pity the foo!
".
Dica: não se esqueça de remover ou renomear public/index.html
.
Quando tudo estiver funcionando, dê uma olhada nos finais
DoclistController
e
ApplicationController
da carne
do projeto DocList Manager. Você também pode analisar
ContactsController
, que
processa as chamadas para a API Google Contacts.
Conclusão
A parte mais difícil da criação de um aplicativo do Google Data Rail é configurar o! No entanto, um segundo próximo é implantar o aplicativo. Para isso, é altamente recomendável usar mod_rails para o Apache. É muito fácil de configurar, instalar e executar. Tudo estará pronto em breve.
Recursos
- Lista de APIs de dados do Google
- Página do projeto Biblioteca de utilitários do Google Data Ruby
- Artigo: Como usar o Ruby com as APIs de dados do Google
- Fazer o download do Ruby
- Fazer o download do RubyGems e do Rail
Apêndice
Exemplos
O DocList Manager é uma amostra completa do Ruby on Rail que demonstra os tópicos discutidos neste artigo. O código-fonte completo está disponível na hospedagem do projeto.
Mais exemplos
Recupere a lista de usuários do Google Docs usando ClientLogin.
#!/usr/bin/ruby
require 'rubygems'
require 'gdata'
require 'highline/import'
def user_input(prompt='> ', echo=true)
ask(prompt) {|q| q.echo = echo}
end
email = user_input('email: ')
pass = user_input('password: ', false)
client = GData::Client::DocList.new({:source => 'google-RailsArticleSample-v1'})
client.clientlogin(email, pass)
feed = client.get(client.authsub_scope + 'documents/private/full').to_xml
puts 'Listing all documents: --------------------'
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[/%3[aA](.*)$/, 1]
puts
end
Faça um mashup usando as APIs de documentos, contatos e planilhas. Este exemplo demonstra o uso de XPN seguro em RoR.
require 'gdata'
class YourController < ApplicationController
before_filter :authenticate_client
PRIVATE_KEY = '/path/to/private_key.pem'
DOCLIST_SCOPE = 'http://docs.google.com/feeds/'
CONTACTS_SCOPE = 'http://www.google.com/m8/feeds/'
SPREADSHEETS_SCOPE = 'http://spreadsheets.google.com/feeds/'
def authenticate_client
scopes = [DOCLIST_SCOPE, SPREADSHEETS_SCOPE, CONTACTS_SCOPE]
@client = GData::Client::Base.new({:authsub_scope => scopes.join(' '),
:source => 'google-RailsArticleSample-v1'})
if params[:token].nil? and session[:token].nil?
next_url = url_for :controller => self.controller_name, :action => self.action_name
secure = true
@authsub_link = @client.authsub_url(next_url, secure, true)
render :controller => 'your_controller', :action => 'your_action'
elsif params[:token] and session[:token].nil?
@client.authsub_token = params[:token]
@client.authsub_private_key = PRIVATE_KEY
session[:token] = @client.auth_handler.upgrade()
end
if session[:token]
@client.authsub_token = session[:token]
@client.authsub_private_key = PRIVATE_KEY # Make sure to set your private key for subsequent requests
end
end
def get_docs
@feed = @client.get(@client.authsub_scope + 'documents/private/full').to_xml
end
end
Criar, editar e excluir uma playlist do YouTube usando um token tmp.
#!/usr/bin/ruby require 'rubygems' require 'gdata' yt = GData::Client::YouTube.new({:source => 'google-RailsArticleSample-v1'}) yt.authsub_token = 'SESSION_TOKEN' yt.client_id = 'CLIENT_ID' yt.developer_key = 'DEVELOPER_KEY' # 1.) Creating a new playlist entry = <<-EOF <entry xmlns="http://www.w3.org/2005/Atom" xmlns:yt="http://gdata.youtube.com/schemas/2007"> <title type="text">Ruby Utility Unit Test</title> <summary>This is a test playlist.</summary> </entry> EOF response = yt.post('http://gdata.youtube.com/feeds/api/users/default/playlists', entry).to_xml # 2.) Updating the received playlist edit_uri = response.elements["link[@rel='edit']"].attributes['href'] response.elements['summary'].text = 'Updated description' response = yt.put(edit_uri, response.to_s).to_xml # 3.) Deleting the playlist yt.delete(edit_uri)
Comandos úteis do Rail
Se você é como eu e não usa o assunto há algum tempo, veja uma lista de comandos úteis.
- Atualizar a instalação do RubyGem
sudo gem update --system
- Atualizar as gemas instaladas
sudo gem update
- Criar um novo Rail com MySQL como banco de dados
rails -d mysql APP_NAME
- Iniciar servidor em produção em outra porta
ruby script/server -p PORT -e production
- Limpar arquivos de registro
rake log:clear