Google Data on Rails

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

บทนำ

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

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

ผู้ชม

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

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

ข้อกำหนด

การติดตั้งไลบรารียูทิลิตี Google Data Ruby

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

sudo gem install gdata

เคล็ดลับ: เพื่อให้มั่นใจ ให้เรียกใช้ gem list --local เพื่อยืนยันว่าได้ติดตั้ง Gem อย่างถูกต้อง

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

ClientLogin

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

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

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

client.auth_handler.info

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

client.auth_handler.revoke

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

OAuth

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

การใช้ OAuth ในไลบรารียูทิลิตีควรทำได้ง่ายเมื่อใช้ oauth-plugin ของ Rails หรือ oauth gem ของ Ruby ไม่ว่าจะในกรณีใด คุณจะต้องสร้างออบเจ็กต์ 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 Documents 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 จะโหลด Gem 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 แบบเต็มที่แสดงให้เห็น หัวข้อที่กล่าวถึงในบทความนี้ ซอร์สโค้ดทั้งหมด พร้อมให้บริการจากโฮสติ้งโปรเจ็กต์