กุมภาพันธ์ 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 ซึ่งง่ายต่อการตั้งค่า ติดตั้ง และเรียกใช้ คุณจะเริ่มทํางานได้ทันที
ทรัพยากร
- รายการ Google Data API
- หน้าโปรเจ็กต์ Google Data Ruby Utility Library
- บทความ: การใช้ Ruby กับ Google Data API
- ดาวน์โหลด Ruby
- ดาวน์โหลด RubyGems และ Rails
ภาคผนวก
ตัวอย่าง
DocList Manager เป็นตัวอย่าง Ruby on Rails แบบเต็มรูปแบบที่สาธิตหัวข้อที่กล่าวถึงในบทความนี้ ซอร์สโค้ดทั้งหมดพร้อมให้ใช้งานจากการโฮสต์โปรเจ็กต์
ตัวอย่างอื่นๆ
เรียกรายการเอกสารของ Google ของผู้ใช้โดยใช้ 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
ผสมผสานโดยใช้ API รายชื่อเอกสาร รายชื่อติดต่อ และสเปรดชีต ตัวอย่างนี้สาธิตการใช้ AuthSub ที่ปลอดภัยใน 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
สร้าง แก้ไข และลบเพลย์ลิสต์ใน YouTube โดยใช้โทเค็น AuthSub
#!/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)
คําสั่งเกี่ยวกับรถไฟที่มีประโยชน์
หากคุณเหมือนกับฉันและยังไม่เคยใช้ Rails มาสักพักแล้ว นี่คือรายการคําสั่งที่ควรจําได้
- อัปเดตการติดตั้ง RubyGem
sudo gem update --system
- อัปเดตเพชรที่ติดตั้งแล้ว
sudo gem update
- สร้าง Rails ใหม่ด้วย MySQL เป็น db
rails -d mysql APP_NAME
- เริ่มต้นเซิร์ฟเวอร์ในเวอร์ชันที่ใช้งานจริงบนพอร์ตอื่น
ruby script/server -p PORT -e production
- ล้างไฟล์บันทึก
rake log:clear