Google Data บนรถไฟ

Eric Bidelman จากทีม Google Data API
กุมภาพันธ์ 2009

บทนำ

"ที่ใดคือ Ruby ในรายการไลบรารีของไคลเอ็นต์"

ความกระตือรือร้นของนักพัฒนาแอปที่เพิ่มสูงขึ้นและความนิยมที่แพร่หลายของ Ruby on Rails (RoR) ทําให้เพื่อนร่วมงานของฉัน Jeff Fisher สร้างไลบรารีสาธารณูปโภคของ Ruby จากความลึกของภูเขา Doom โปรดทราบว่าไม่ใช่ไลบรารีของไคลเอ็นต์อย่างเต็มรูปแบบ แต่เป็นการจัดการพื้นฐาน เช่น การตรวจสอบสิทธิ์และการบิดเบือน XML พื้นฐาน นอกจากนี้ คุณยังต้องทํางานร่วมกับฟีด Atom โดยตรงโดยใช้โมดูล REXML และ XPath

ผู้ชม

บทความนี้มีไว้สําหรับนักพัฒนาซอฟต์แวร์ที่สนใจเข้าถึง Google Data API โดยใช้ Ruby โดยเฉพาะ Ruby บน Rails และสมมติว่าผู้อ่านคุ้นเคยกับภาษาโปรแกรม Ruby และเฟรมเวิร์กการพัฒนาเว็บรถไฟของ Rails ฉันมุ่งเน้นที่ API รายชื่อเอกสารสําหรับตัวอย่างส่วนใหญ่ แต่แนวคิดเดียวกันสามารถใช้กับ Data API

เริ่มต้นใช้งาน

ข้อกำหนด

การติดตั้งไลบรารีข้อมูล Ruby Utility

หากต้องการรับไลบรารี คุณสามารถดาวน์โหลดแหล่งที่มาของไลบรารีจากการโฮสต์โปรเจ็กต์หรือติดตั้ง gem ได้โดยตรง

sudo gem install gdata

เคล็ดลับ: สําหรับการวัดที่ดี ให้เรียกใช้ gem list --local เพื่อยืนยันว่าติดตั้งอัญมณีอย่างถูกต้องแล้ว

การตรวจสอบสิทธิ์

ClientLogin

ClientLogin ช่วยให้แอปพลิเคชันของคุณลงชื่อเข้าใช้บัญชี Google หรือ G Suite ของผู้ใช้แบบเป็นโปรแกรมได้ เมื่อตรวจสอบข้อมูลรับรองของผู้ใช้แล้ว Google จะออกโทเค็นการตรวจสอบสิทธิ์เพื่ออ้างอิงถึงในคําขอ API ในภายหลัง โทเค็นจะใช้งานได้ตามระยะเวลาที่กําหนด โดยบริการใดของ Google ที่คุณทํางานด้วย เพื่อความปลอดภัยและเพื่อให้ผู้ใช้ได้รับประสบการณ์ที่ดีที่สุด คุณควรใช้ ClientLogin เฉพาะเมื่อพัฒนาแอปพลิเคชันเดสก์ท็อปที่ติดตั้งแล้ว สําหรับเว็บแอปพลิเคชัน ขอแนะนําให้ใช้ AuthSub หรือ OAuth

ไลบรารี Ruby มีคลาสไคลเอ็นต์สําหรับ API แต่ละรายการ เช่น ใช้ข้อมูลโค้ดต่อไปนี้เพื่อเข้าสู่ระบบ user@gmail.com ใน API ของรายการเอกสาร

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 มีเครื่องจัดการเพิ่มเติม 2 รายการ ได้แก่ AuthSubTokenInfo และ AuthSubDebugToken สําหรับการจัดการโทเค็น AuthSubTokenInfo มีประโยชน์สําหรับการตรวจสอบความถูกต้องของโทเค็น AuthSubRevokeToken มีตัวเลือกในการเข้าถึงข้อมูลของผู้ใช้ แอปของคุณควรใช้ AuthSubRevokeToken แนวทางปฏิบัติแนะนํา ไลบรารี Ruby รองรับทั้ง 2 วิธี

วิธีค้นหาข้อมูลเมตาของโทเค็น

client.auth_handler.info

วิธีเพิกถอนโทเค็นเซสชัน

client.auth_handler.revoke

โปรดดูเอกสารฉบับเต็มเกี่ยวกับการตรวจสอบสิทธิ์ AuthSub สําหรับเว็บแอปพลิเคชันเพื่อดูข้อมูลทั้งหมดใน AuthSub

OAuth

ขณะที่เขียนบทความนี้ ยังไม่มีการเพิ่ม OAuth ลงในโมดูล GData::Auth

การใช้ OAuth ในไลบรารียูทิลิตีควรค่อนข้างตรงไปตรงมาเมื่อใช้ oauth-plugin ของรถไฟหรือ Ruby oauth gem ไม่ว่าจะเป็นกรณีใด คุณจะต้องสร้างออบเจ็กต์ GData::HTTP::Request และส่งส่วนหัว Authorization ที่สร้างโดยไลบรารีแต่ละรายการ

การเข้าถึงฟีด

GET (กําลังดึงข้อมูล)

เมื่อตั้งค่าออบเจ็กต์ไคลเอ็นต์แล้ว ให้ใช้เมธอด get() ของออบเจ็กต์นั้นเพื่อค้นหาฟีดข้อมูล Google XPath สามารถใช้ เพื่อดึงองค์ประกอบ Atom ที่เจาะจง ตัวอย่างการเรียก Google เอกสารของผู้ใช้มีดังนี้

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 เพื่อตั้งค่าตารางฐานข้อมูล แต่เนื่องจากแอปพลิเคชันของเราจะค้นหา Google Docs List API สําหรับข้อมูล เราจึงต้องการ เพราะ เพราะต้องการนั่งร้านหรือฐานข้อมูลทั่วไป ให้สร้างแอปพลิเคชันใหม่และตัวควบคุมแบบง่ายแทน ดังนี้

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 ออกจากแอปพลิเคชัน บรรทัดที่ 2 โหลด 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 ซึ่งจัดการการเรียก Google Contacts API ด้วย

บทสรุป

ส่วนที่ยากที่สุดในการสร้างแอป Google Data Rails คือการกําหนดค่า Rails! อย่างไรก็ตาม การปิดวินาทีทําให้แอปพลิเคชันของคุณใช้งานได้ ขอแนะนําอย่างยิ่งให้ใช้ mod_rails สําหรับ Apache ซึ่งง่ายต่อการตั้งค่า ติดตั้ง และเรียกใช้ คุณจะเริ่มทํางานได้ทันที

ทรัพยากร

ภาคผนวก

ตัวอย่าง

DocList Manager เป็นตัวอย่าง Ruby on Rails แบบเต็มรูปแบบที่สาธิตหัวข้อที่กล่าวถึงในบทความนี้ ซอร์สโค้ดทั้งหมดพร้อมให้ใช้งานจากการโฮสต์โปรเจ็กต์