การใช้ Ruby กับ Google Data API

Jochen Hartmann จากทีม Google Data API
เมษายน 2008

บทนำ

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

คุณอาจจําได้จากบทความ cURL ว่า Google Data API ใช้โปรโตคอลการเผยแพร่ข้อมูล Atom เพื่อนําเสนอ สร้าง และอัปเดตแหล่งข้อมูลบนเว็บ ข้อดีของโปรโตคอลนี้คือ คํากริยา HTTP มาตรฐานใช้ในการสร้างคําขอที่มีการตอบสนองด้วยรหัสสถานะ HTTP มาตรฐาน

คํากริยาที่เราจะใช้ในบทความนี้คือ GET เพื่อดึงเนื้อหา POST เพื่ออัปโหลดเนื้อหาใหม่ และ PUT เพื่ออัปเดตเนื้อหาที่มีอยู่ โค้ดมาตรฐานบางส่วนที่คุณอาจพบโดยใช้ Google Data API คือ 200 ซึ่งแสดงถึงความสําเร็จในการเรียกฟีดหรือรายการ หรือ 201 เพื่อแสดงถึงการสร้างหรืออัปเดตทรัพยากรที่ประสบความสําเร็จ หากมีข้อผิดพลาด เช่น เมื่อส่งคําขอที่มีรูปแบบไม่ถูกต้อง ระบบจะส่งรหัส 400 (แปลว่า "คําขอไม่ถูกต้อง") กลับมา ข้อความที่มีรายละเอียดมากขึ้นจะอยู่ในเนื้อหาการตอบกลับ โดยอธิบายว่าเกิดอะไรขึ้น

Ruby มีตัวเลือกการแก้ไขข้อบกพร่องที่ยอดเยี่ยมโดยเป็นส่วนหนึ่งของโมดูล "Net" อย่างไรก็ตาม ฉันได้เปิดใช้งานโค้ดดังกล่าวไว้สั้นๆ เพื่อรักษาตัวอย่างโค้ดเหล่านี้ให้สมเหตุสมผล

การรับและติดตั้ง Ruby

ในกรณีที่ติดตั้ง Ruby ได้ คุณจะติดตั้ง Ruby ได้หากใช้ระบบจัดการแพ็กเกจส่วนใหญ่ สําหรับระบบปฏิบัติการอื่นๆ และการดูซอร์สโค้ดแบบเต็ม โปรดไปที่ http://www.ruby-lang.org/en/downloads/ Irb เชลล์แบบอินเทอร์แอกทีฟที่เราจะใช้สําหรับตัวอย่างเหล่านี้ควรติดตั้งโดยค่าเริ่มต้น เพื่อทําตามตัวอย่างโค้ดที่แสดงที่นี่ คุณจะต้องติดตั้ง XmlSimple ซึ่งเป็นไลบรารีขนาดเล็กที่จะแยกวิเคราะห์ XML ลงในโครงสร้างข้อมูล Ruby ด้วย หากต้องการ/ติดตั้ง XmlSimple โปรดไปที่ http://xml- Simple.rubyforge.org/

เมื่อมีสําเนา Ruby ทํางานบนเครื่องแล้ว คุณจะใช้แพ็กเกจ Net:HTTP เพื่อส่งคําขอพื้นฐานไปยังบริการข้อมูลของ Google ได้ ข้อมูลโค้ดด้านล่างแสดงวิธีนําเข้าที่จําเป็นจาก Shell แบบอินเทอร์แอกทีฟของ Ruby สิ่งที่เราต้องทําคือต้องมีแพ็กเกจ "net/http" แยกวิเคราะห์ URL ของฟีดฟีดที่มีคะแนนสูงสุดจาก YouTube และส่งคําขอ HTTP GET

irb(main):001:0> require 'net/http'
=> true
irb(main):002:0> youtube_top_rated_videos_feed_uri = \
'http://gdata.youtube.com/feeds/api/standardfeeds/top_rated'
=> "http://gdata.youtube.com/feeds/api/standardfeeds/top_rated"
irb(main):003:0> uri = \
URI.parse(youtube_top_rated_videos_feed_uri)
=> #<URI::HTTP:0xfbf826e4 URL:http://gdata.youtube.com/feeds/api/standardfeeds/top_rated>

irb(main):004:0> uri.host
=> "gdata.youtube.com"
irb(main):005:0> Net::HTTP.start(uri.host, uri.port) do |http|
irb(main):006:1* puts http.get(uri.path)
irb(main):007:1> end
#<Net::HTTPOK:0xf7ef22cc>

คําขอดังกล่าวควรให้เสียงอ่าน XML เพียงเล็กน้อยกับบรรทัดคําสั่ง คุณอาจสังเกตเห็นว่ารายการทั้งหมดอยู่ในองค์ประกอบ <feed> และเรียกว่าองค์ประกอบ <Entry> ยังไม่กังวลเรื่องการจัดรูปแบบ XML เลย ฉันอยากอธิบายวิธีสร้างคําขอ Google Data API พื้นฐานโดยใช้ HTTP เราจะเปลี่ยน API ตอนนี้และเน้นที่สเปรดชีต เนื่องจากข้อมูลที่เราส่งและเรียกข้อมูลได้จะ "เป็นมิตรกับบรรทัดคําสั่ง" มากกว่า

การตรวจสอบสิทธิ์ | การใช้ Googlespreadsheet API

เราจะเริ่มอีกครั้งโดยเรียกข้อมูลฟีดขององค์ประกอบรายการ แต่คราวนี้เราอยากทํางานกับสเปรดชีตของเราเอง ในการดําเนินการดังกล่าว เราต้องตรวจสอบสิทธิ์กับบริการบัญชี Google ก่อน

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

เปลี่ยนกลับไปใช้ Shell แบบอินเทอร์แอกทีฟ มาตรวจสอบสิทธิ์กับ Google กัน โปรดทราบว่าเราใช้ https ในการส่งคําขอการตรวจสอบสิทธิ์และข้อมูลเข้าสู่ระบบ

irb(main):008:0> require 'net/https'
=> true
irb(main):009:0> http = Net::HTTP.new('www.google.com', 443)
=> #<Net::HTTP www.google.com:443 open=false>
irb(main):010:0> http.use_ssl = true
=> true
irb(main):011:0> path = '/accounts/ClientLogin'
=> "/accounts/ClientLogin"

# Now we are passing in our actual authentication data. 
# Please visit OAuth For Installed Apps for more information 
# about the accountType parameter
irb(main):014:0> data = \
irb(main):015:0* 'accountType=HOSTED_OR_GOOGLE&Email=your email' \
irb(main):016:0* '&Passwd=your password' \
irb(main):017:0* '&service=wise'

=> accountType=HOSTED_OR_GOOGLE&Email=your email&Passwd=your password&service=wise"

# Set up a hash for the headers
irb(main):018:0> headers = \
irb(main):019:0* { 'Content-Type' => 'application/x-www-form-urlencoded'}
=> {"Content-Type"=>"application/x-www-form-urlencoded"}

# Post the request and print out the response to retrieve our authentication token
irb(main):020:0> resp, data = http.post(path, data, headers)
warning: peer certificate won't be verified in this SSL session
=> [#<Net::HTTPOK 200 OK readbody=true>, "SID=DQAAAIIAAADgV7j4F-QVQjnxdDRjpslHKC3M ... [ snipping out the rest of the authentication strings ]

# Strip out our actual token (Auth) and store it
irb(main):021:0> cl_string = data[/Auth=(.*)/, 1]
=> "DQAAAIUAAADzL... [ snip ]

# Build our headers hash and add the authorization token
irb(main):022:0> headers["Authorization"] = "GoogleLogin auth=#{cl_string}"
=> "GoogleLogin auth=DQAAAIUAAADzL... [ snip ]

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

http://spreadsheets.google.com/feeds/spreadsheets/private/full

เนื่องจากนี่เป็นคําขอที่ตรวจสอบสิทธิ์แล้ว เราจึงต้องการส่งในส่วนหัวของเราด้วย จริงๆ แล้ว เนื่องจากเราจะส่งคําขอหลายรายการสําหรับฟีดต่างๆ เราจึงอาจรวมฟังก์ชันนี้ไว้ในฟังก์ชันง่ายๆ ซึ่งเราเรียกว่า get_feed ด้วย

# Store the URI to the feed since we may want to use it again
irb(main):023:0> spreadsheets_uri = \
irb(main):024:0* 'http://spreadsheets.google.com/feeds/spreadsheets/private/full'

# Create a simple method to obtain a feed
irb(main):025:0> def get_feed(uri, headers=nil)
irb(main):026:1> uri = URI.parse(uri)
irb(main):027:1> Net::HTTP.start(uri.host, uri.port) do |http|
irb(main):028:2* return http.get(uri.path, headers)
irb(main):029:2> end
irb(main):030:1> end
=> nil

# Lets make a request and store the response in 'my_spreadsheets'
irb(main):031:0> my_spreadsheets = get_feed(spreadsheets_uri, headers)
=> #<Net::HTTPOK 200 OK readbody=true>

irb(main):032:0> my_spreadsheets
=> #<Net::HTTPOK 200 OK readbody=true>

# Examine our XML (showing only an excerpt here...)
irb(main):033:0> my_spreadsheets.body
=> "<?xml version='1.0' encoding='UTF-8'?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/'>
<id>http://spreadsheets.google.com/feeds/spreadsheets/private/full</id><updated>2008-03-20T20:49:39.211Z</updated>
<category scheme='http://schemas.google.com/spreadsheets/2006' term='http://schemas.google.com/spreadsheets/2006#spreadsheet'/>
<title type='text'>Available Spreadsheets - test.api.jhartmann@gmail.com</title><link rel='alternate' type='text/html' href='http://docs.google.com'/>
<link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://spreadsheets.google.com/feeds/spreadsheets/private/full'/><link rel='self' type='application/atom+xml' href='http://spreadsheets.google.com/feeds/spreadsheets/private/full?tfe='/>
<openSearch:totalResults>6</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><entry>
<id>http://spreadsheets.google.com/feeds/spreadsheets/private/full/o04927555739056712307.4365563854844943790</id><updated>2008-03-19T20:44:41.055Z</updated><category scheme='http://schemas.google.com/spreadsheets/2006' term='http://schemas.google.com/spreadsheets/2006#spreadsheet'/><title type='text'>test02</title><content type='text'>test02</content><link rel='http://schemas.google.com/spreadsheets/2006#worksheetsfeed' type='application/atom+xml' href='http://spreadsheets.google.com/feeds/worksheets/o04927555739056712307.4365563854844943790/private/full'/><link rel='alternate' type='text/html' href='http://spreadsheets.google.com/ccc?key=o04927555739056712307.4365563854844943790'/><link rel='self' type='application/atom+xml' href='http://spreadsheets.google.com/feeds/spreadsheets/private/full/o04927555739056712307.4365563854844943790'/><author><name>test.api.jhartmann</name><email>test.api.jhartmann@gmail.com</email></author></entry><entry> ...

ขอย้ําอีกครั้งว่าเราพบ XML จํานวนมากที่ฉันเน้นที่การแก้ไขด้านบนเพราะไม่ต้องกังวลเรื่องการถอดรหัสจากบรรทัดคําสั่ง เรามาแยกวิเคราะห์ลงในโครงสร้างข้อมูลโดยใช้ XmlSimple แทนเพื่อให้ทุกอย่างใช้งานง่ายยิ่งขึ้น

# Perform imports
irb(main):034:0> require 'rubygems'
=> true
irb(main):035:0> require 'xmlsimple'
=> true
irb(main):036:0> doc = \
irb(main):037:0* XmlSimple.xml_in(my_spreadsheets.body, 'KeyAttr' => 'name')

# Import the 'pp' module for 'pretty printing'
irb(main):038:0> require 'pp'
=> true

# 'Pretty-print' our XML document
irb(main):039:0> pp doc
{"totalResults"=>["6"],
 "category"=>
  [{"term"=>"http://schemas.google.com/spreadsheets/2006#spreadsheet",
    "scheme"=>"http://schemas.google.com/spreadsheets/2006"}],
 "title"=>
  [{"type"=>"text",
    "content"=>"Available Spreadsheets - Test-account"}],
 "startIndex"=>["1"],
 "id"=>["http://spreadsheets.google.com/feeds/spreadsheets/private/full"],
 "entry"=>
  [{"category"=>
     [{"term"=>"http://schemas.google.com/spreadsheets/2006#spreadsheet",
       "scheme"=>"http://schemas.google.com/spreadsheets/2006"}],
    "title"=>[{"type"=>"text", "content"=>"blank"}],
    "author"=>
     [{"name"=>["Test-account"],
       "email"=>["my email"]}],
    "id"=>
     ["http://spreadsheets.google.com/feeds/spreadsheets/private/full/o04927555739056712307.3387874275736238738"],
    "content"=>{"type"=>"text", "content"=>"blank"},
    "link"=>
    [ snipping out the rest of the XML ]

การรับเวิร์กชีต

ดังนั้นฟีดของฉันจึงมีสเปรดชีต 6 รายการตามที่เห็นในผลลัพธ์ด้านบน เพื่อเป็นการทําให้บทความนี้สั้นพอ ฉันจึงตัดเอาต์พุต XML ที่เหลือทั้งหมดออกไป (รวมถึงที่อื่นๆ ส่วนใหญ่) หากต้องการเจาะลึกสเปรดชีตนี้ เราจะต้องดําเนินการเพิ่มเติมอีก 2-3 ขั้นตอน ดังนี้

  1. รับคีย์สเปรดชีต
  2. ใช้คีย์สเปรดชีตเพื่อรับฟีดเวิร์กชีตของเรา
  3. รับรหัสสําหรับเวิร์กชีตที่เราต้องการใช้
  4. ส่งคําขอ cellFeed หรือ ListFeed เพื่อเข้าถึงเนื้อหาจริงของเวิร์กชีต

อาจฟังดูเป็นงานหนัก แต่ฉันจะแสดงให้เห็นว่านี่เป็นเรื่องที่ง่ายมากหากเราเขียนวิธีง่ายๆ สัก 2-3 วิธี cellFeed และ แสดงรายการฟีดคือการนําเสนอ 2 รายการที่ต่างกันสําหรับเนื้อหาเซลล์จริงของเวิร์กชีต ListFeed แสดงข้อมูลทั้งหมดและแนะนําให้โพสต์ข้อมูลใหม่ cellFeed แสดงถึงเซลล์แต่ละเซลล์ และใช้สําหรับการอัปเดตเซลล์แต่ละเซลล์หรืออัปเดตเป็นกลุ่มไปยังเซลล์หลายๆ เซลล์ (ทั้ง 2 อย่างใช้ PUT) โปรดดูรายละเอียดเพิ่มเติมในเอกสารประกอบของ Google สเปรดชีต API

ก่อนอื่นเราต้องดึงคีย์สเปรดชีต (ที่ไฮไลต์ไว้ในเอาต์พุต XML ด้านบน) เพื่อรับฟีดเวิร์กชีต ดังนี้

# Extract the spreadsheet key from our datastructure
irb(main):040:0> spreadsheet_key = \ 
irb(main):041:0* doc["entry"][0]["id"][0][/full\/(.*)/, 1]
=> "o04927555739056712307.3387874275736238738"

# Using our get_feed method, let's obtain the worksheet feed
irb(main):042:0> worksheet_feed_uri = \ 
irb(main):043:0* "http://spreadsheets.google.com/feeds/worksheets/#{spreadsheet_key}/private/full"
=> "http://spreadsheets.google.com/feeds/worksheets/o04927555739056712307.3387874275736238738/private/full"

irb(main):044:0> worksheet_response = get_feed(worksheet_feed_uri, headers)
=> #<Net::HTTPOK 200 OK readbody=true>

# Parse the XML into a datastructure
irb(main):045:0> worksheet_data = \ 
irb(main):046:0* XmlSimple.xml_in(worksheet_response.body, 'KeyAttr' => 'name')
=> {"totalResults"=>["1"], "category"=>[{"term ... [ snip ]

# And pretty-print it
irb(main):047:0> pp worksheet_data
{"totalResults"=>["1"],
 "category"=>
  [{"term"=>"http://schemas.google.com/spreadsheets/2006#worksheet",
    "scheme"=>"http://schemas.google.com/spreadsheets/2006"}],
 "title"=>[{"type"=>"text", "content"=>"blank"}],
 "author"=>
  [{"name"=>["test.api.jhartmann"],
    "email"=>["test.api.jhartmann@gmail.com"]}],
 "startIndex"=>["1"],
 "id"=>
  ["http://spreadsheets.google.com/feeds/worksheets/o04927555739056712307.3387874275736238738/private/full"],
 "entry"=>
  [{"category"=>
     [{"term"=>"http://schemas.google.com/spreadsheets/2006#worksheet",
       "scheme"=>"http://schemas.google.com/spreadsheets/2006"}],
    "title"=>[{"type"=>"text", "content"=>"Sheet 1"}],
    "rowCount"=>["100"],
    "colCount"=>["20"],
    "id"=>
     ["http://spreadsheets.google.com/feeds/worksheets/o04927555739056712307.3387874275736238738/private/full/od6"],
    "content"=>{"type"=>"text", "content"=>"Sheet 1"},
    "link"=>
     [{"href"=>
        "http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full",
       "rel"=>"http://schemas.google.com/spreadsheets/2006#listfeed",
       "type"=>"application/atom+xml"},
      {"href"=>
        "http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full",
       "rel"=>"http://schemas.google.com/spreadsheets/2006#cellsfeed",
       "type"=>"application/atom+xml"},
    [ snip: cutting off the rest of the XML ]

จากตรงนี้ คุณจะเห็นลิงก์ (highlighted above) สําหรับเข้าถึง listFeed และ cellFeed ก่อนที่เราจะเจาะลึกเรื่องรายการฟีด ฉันจะอธิบายเกี่ยวกับข้อมูลปัจจุบันที่อยู่ในสเปรดชีตตัวอย่างอย่างรวดเร็วเพื่อให้คุณทราบว่าเรากําลังมองหาอะไร

สเปรดชีตของเราไม่ซับซ้อนและมีลักษณะดังนี้

languageเว็บไซต์
Javahttp://java.com
PHPhttp://php.net

และลักษณะของข้อมูลนี้ใน listFeed มีดังนี้

irb(main):048:0> listfeed_uri = \
irb(main):049:0* worksheet_data["entry"][0]["link"][0]["href"]
=> "http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full"

irb(main):050:0> response = get_feed(listfeed_uri, headers)
=> #<Net::HTTPOK 200 OK readbody=true>
irb(main):051:0> listfeed_doc = \ 
irb(main):052:0* XmlSimple.xml_in(response.body, 'KeyAttr' => 'name')
=> {"totalResults"=>["2"], "category"=>[{"term" ... [ snip ]

# Again we parse the XML and then pretty print it
irb(main):053:0> pp listfeed_doc
{"totalResults"=>["2"],
 "category"=>
  [{"term"=>"http://schemas.google.com/spreadsheets/2006#list",
    "scheme"=>"http://schemas.google.com/spreadsheets/2006"}],
 "title"=>[{"type"=>"text", "content"=>"Programming language links"}],
 "author"=>
  [{"name"=>["test.api.jhartmann"],
    "email"=>["test.api.jhartmann@gmail.com"]}],
 "startIndex"=>["1"],
 "id"=>
  ["http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full"],
 "entry"=>
  [{"category"=>
     [{"term"=>"http://schemas.google.com/spreadsheets/2006#list",
       "scheme"=>"http://schemas.google.com/spreadsheets/2006"}],
    "language"=>["java"],
    "title"=>[{"type"=>"text", "content"=>"ruby"}],
    "website"=>["http://java.com"],
    "id"=>
     ["http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full/cn6ca"],
    "content"=>
     {"type"=>"text", "content"=>"website: http://java.com"},
    "link"=>
     [{"href"=>
        "http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full/cn6ca",
       "rel"=>"self",
       "type"=>"application/atom+xml"},
      {"href"=>
        "http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full/cn6ca/1j81anl6096",
       "rel"=>"edit",
       "type"=>"application/atom+xml"}],
    "updated"=>["2008-03-20T22:19:51.739Z"]},
   {"category"=>
     [{"term"=>"http://schemas.google.com/spreadsheets/2006#list",
       "scheme"=>"http://schemas.google.com/spreadsheets/2006"}],
    "language"=>["php"],
    "title"=>[{"type"=>"text", "content"=>"php"}],
    "website"=>["http://php.net"],
    "id"=>
     ["http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full/cokwr"],
    "content"=>{"type"=>"text", "content"=>"website: http://php.net"},
    [ snip ]

จะเห็นว่า listFeed ส่งคืนเนื้อหาของเวิร์กชีตด้วยการสร้างรายการสําหรับแต่ละแถว โดยถือว่าแถวแรกของสเปรดชีตมีส่วนหัวของเซลล์และสร้างส่วนหัว XML แบบไดนามิกตามข้อมูลในแถวนั้น การดู XML จริงจะช่วยอธิบายเพิ่มเติมได้:

<?xml version='1.0' encoding='UTF-8'?><feed [ snip namespaces ]>
<id>http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full</id>
<updated>2008-03-20T22:19:51.739Z</updated>
<category scheme='http://schemas.google.com/spreadsheets/2006' term='http://schemas.google.com/spreadsheets/2006#list'/>

<title type='text'>Programming language links</title>
[ snip: cutting out links and author information ]
<entry>
    <id>http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full/cn6ca</id>
    [ snip: updated and category ]
    <title type='text'>java</title>
    <content type='text'>website: http://java.com</content>
    <link rel='self' type='application/atom+xml' href='http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full/cn6ca'/>
    <link rel='edit' type='application/atom+xml' href='http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full/cn6ca/1j81anl6096'/>
    <gsx:language>java</gsx:language>
    <gsx:website>http://java.com</gsx:website>
</entry>
<entry>
    <id>http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full/cokwr</id>
    [ snip: updated and category ]
    <title type='text'>php</title>
    <content type='text'>website: http://php.net</content>
    <link rel='self' type='application/atom+xml' href='http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full/cokwr'/>
    <link rel='edit' type='application/atom+xml' href='http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full/cokwr/41677fi0nc'/>
    <gsx:language>php</gsx:language>
    <gsx:website>http://php.net</gsx:website>
</entry>
</feed>

มาดูตัวอย่างกันว่าเราแสดงข้อมูลเดียวกันในเซลล์ฟีดอย่างไรเพื่อให้เปรียบเทียบได้อย่างรวดเร็ว

# Extract the cellfeed link
irb(main):054:0> cellfeed_uri = \
irb(main):055:0* worksheet_data["entry"][0]["link"][1]["href"]
=> "http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full"
irb(main):056:0> response = \ 
irb(main):057:0* get_feed(cellfeed_uri, headers)
=> #<Net::HTTPOK 200 OK readbody=true>

# Parse into datastructure and print
irb(main):058:0> cellfeed_doc = \ 
irb(main):059:0* XmlSimple.xml_in(response.body, 'KeyAttr' => 'name')
=> {"totalResults"=>["6"], [ snip ]

irb(main):060:0> pp cellfeed_doc
{"totalResults"=>["6"],
 "category"=>
  [{"term"=>"http://schemas.google.com/spreadsheets/2006#cell",
    "scheme"=>"http://schemas.google.com/spreadsheets/2006"}],
 "title"=>[{"type"=>"text", "content"=>"Programming language links"}],
 "rowCount"=>["101"],
 "colCount"=>["20"],
 "author"=>
  [{"name"=>["test.api.jhartmann"],
    "email"=>["test.api.jhartmann@gmail.com"]}],
 "startIndex"=>["1"],
 "id"=>
  ["http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full"],
 "entry"=>
  [{"category"=>
     [{"term"=>"http://schemas.google.com/spreadsheets/2006#cell",
       "scheme"=>"http://schemas.google.com/spreadsheets/2006"}],
    "cell"=>
     [{"col"=>"1",
       "row"=>"1",
       "content"=>"language",
       "inputValue"=>"language"}],
    "title"=>[{"type"=>"text", "content"=>"A1"}],
    "id"=>
     ["http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full/R1C1"],
    "content"=>{"type"=>"text", "content"=>"language"},
    "link"=>
     [{"href"=>
        "http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full/R1C1",
       "rel"=>"self",
       "type"=>"application/atom+xml"},
      {"href"=>
        "http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full/R1C1/8srvbs",
       "rel"=>"edit",
       "type"=>"application/atom+xml"}],
    "updated"=>["2008-03-20T22:19:51.739Z"]},
    [ snip ]

ซึ่งคุณจะเห็นรายการ 6 รายการแสดงขึ้น 1 รายการสําหรับแต่ละเซลล์ ฉันตัดเอาต์พุตอื่นๆ ทั้งหมดนอกเหนือจากค่าของเซลล์ A1 ซึ่งมีคําว่า "language" นอกจากนี้ สังเกตเห็นลิงก์แก้ไขที่แสดงอยู่ด้านบน ลิงก์นี้มีสตริงเวอร์ชัน (8srvbs) อยู่ที่ส่วนท้าย สตริงเวอร์ชันมีความสําคัญเมื่ออัปเดตข้อมูลเซลล์ เนื่องจากเราจะทําในตอนท้ายของบทความนี้ เพื่อให้แน่ใจว่าไม่มีการเขียนทับการอัปเดต เมื่อใดก็ตามที่คุณส่งคําขอ PUT เพื่ออัปเดตข้อมูลเซลล์ คุณต้องใส่สตริงเวอร์ชันล่าสุดของเซลล์ในคําขอ ระบบจะแสดงสตริงเวอร์ชันใหม่หลังจากการอัปเดตแต่ละครั้ง

การโพสต์เนื้อหาไปยังรายการฟีด

สิ่งแรกที่เราต้องการสําหรับการโพสต์เนื้อหาคือลิงก์ POST สําหรับ ListFeed ระบบจะแสดงลิงก์นี้เมื่อมีการขอฟีดข้อมูล ซึ่งจะมี URL http://schemas.google.com/g/2005#post เป็นค่าสําหรับแอตทริบิวต์ rel คุณจะต้องแยกวิเคราะห์องค์ประกอบลิงก์นี้ และดึงแอตทริบิวต์ href ขององค์ประกอบดังกล่าว ก่อนอื่นเราจะสร้างวิธีเล็กๆ น้อยๆ ที่จะทําให้โพสต์ง่ายขึ้น ดังนี้

irb(main):061:0> def post(uri, data, headers)
irb(main):062:1> uri = URI.parse(uri)
irb(main):063:1> http = Net::HTTP.new(uri.host, uri.port)
irb(main):064:1> return http.post(uri.path, data, headers)
irb(main):065:1> end
=> nil
# Set up our POST url
irb(main):066:0> post_url = \ 
irb(main):067:0* "http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full"
=> "http://spreadsheets.google.com/feeds/list/o04927555739056712307.3387874275736238738/od6/private/full"

# We must use 'application/atom+xml' as MIME type so let's change our headers 
# which were still set to 'application/x-www-form-urlencoded' when we sent our 
# ClientLogin information over https
irb(main):068:0> headers["Content-Type"] = "application/atom+xml"
=> "application/atom+xml"

# Setting up our data to post, using proper namespaces
irb(main):069:0> new_row = \ 
irb(main):070:0* '<atom:entry xmlns:atom="http://www.w3.org/2005/Atom">' << 
irb(main):071:0* '<gsx:language xmlns:gsx="http://schemas.google.com/spreadsheets/2006/extended">' <<
irb(main):072:0* 'ruby</gsx:language>' << 
irb(main):073:0* '<gsx:website xmlns:gsx="http://schemas.google.com/spreadsheets/2006/extended">' <<
irb(main):074:0* 'http://ruby-lang.org</gsx:website>' << 
irb(main):075:0* '</atom:entry>'
=> "<atom:entry xmlns:atom=\"http://www.w3.org/2005/Atom\"><gsx:language ... [ snip ] 

# Performing the post
irb(main):076:0> post_response = post(post_url, new_row, headers) 
=> #<Net::HTTPCreated 201 Created readbody=true>

สถานะ 201 แสดงว่าโพสต์ของเราประสบความสําเร็จ

การใช้ cellFeed เพื่ออัปเดตเนื้อหา

จากเอกสารประกอบ เราพบว่าฟีดเซลล์ต้องการคําขอ PUT ในเนื้อหาที่มีอยู่ แต่เนื่องจากข้อมูลที่เราดึงมาจากเซลล์ของฟีดก่อนหน้านี้เป็นเพียงข้อมูลที่มีอยู่แล้วอยู่แล้วในสเปรดชีตจริง เราจะเพิ่มข้อมูลใหม่ได้อย่างไร เราเพียงต้องส่งคําขอสําหรับเซลล์ว่างแต่ละเซลล์ที่เราต้องการป้อนข้อมูล ข้อมูลโค้ดด้านล่างแสดงวิธีเรียกข้อมูลเซลล์ว่าง R5C1 (แถว 5 คอลัมน์ 1) ที่เราต้องการแทรกข้อมูลบางอย่างเกี่ยวกับภาษาโปรแกรม Python

ตัวแปรเดิม cellfeed_uri มีเฉพาะ URI ของเซลล์เซลล์เอง ตอนนี้เราต้องการแก้ไขเซลล์ที่คุณต้องการแก้ไขและขอรับสตริงเวอร์ชันเซลล์นั้นเพื่อแก้ไข ดังนี้

# Set our query URI
irb(main):077:0> cellfeed_query = cellfeed_uri + '/R5C1'
=> "http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full/R5C1"

# Request the information to extract the edit link
irb(main):078:0> cellfeed_data = get_feed(cellfeed_query, headers)
=> #<Net::HTTPOK 200 OK readbody=true>
irb(main):079:0> cellfeed_data.body
=> "<?xml version='1.0' encoding='UTF-8'?>
<entry xmlns='http://www.w3.org/2005/Atom' xmlns:gs='http://schemas.google.com/spreadsheets/2006' xmlns:batch='http://schemas.google.com/gdata/batch'>
<id>http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full/R5C1</id>
<updated>2008-03-24T21:55:36.462Z</updated>
<category scheme='http://schemas.google.com/spreadsheets/2006' term='http://schemas.google.com/spreadsheets/2006#cell'/>
<title type='text'>A5</title>
<content type='text'>
</content>
<link rel='self' type='application/atom+xml' href='http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full/R5C1'/>
<link rel='edit' type='application/atom+xml' href='http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full/R5C1/47pc'/>
<gs:cell row='5' col='1' inputValue=''>
</gs:cell>
</entry>"

ดังที่เห็นในรายการโค้ดด้านบน สตริงเวอร์ชันคือ 47pc (คุณอาจต้องเลื่อนไปจนสุดทางด้านขวา) เพื่อความสะดวกในการสร้าง โปรดสร้างวิธีการที่สะดวกให้เราสร้างสตริงเวอร์ชันสําหรับเซลล์ใดๆ ที่เราสนใจ

irb(main):080:0> def get_version_string(uri, headers=nil)
irb(main):081:1> response = get_feed(uri, headers)
irb(main):082:1> require 'rexml/document'
irb(main):083:1> xml = REXML::Document.new response.body
irb(main):084:1> edit_link = REXML::XPath.first(xml, '//[@rel="edit"]')
irb(main):085:1> edit_link_href = edit_link.attribute('href').to_s
irb(main):086:1> return edit_link_href.split(/\//)[10]
irb(main):087:1> end
=> nil

# A quick test
irb(main):088:0> puts get_version_string(cellfeed_query, headers)
47pc
=> nil

นอกจากนี้ เรายังอาจเขียนวิธีขอ PUT ด้วยหรือถ้าจะดีกว่านี้ ก็เขียนวิธีอัปเดตทั้งกลุ่มได้เลย ฟังก์ชันของเราจะใช้อาร์เรย์ของแฮชที่มีตัวแปรต่อไปนี้

  • :batch_id - ตัวระบุที่ไม่ซ้ํากันสําหรับคําขอแบบกลุ่มแต่ละรายการ
  • :cell_id - รหัสของเซลล์ที่จะอัปเดตในรูปแบบ R#C# โดยเซลล์ A1 จะแสดงเป็น R1C1
  • :data - ข้อมูลที่ต้องการแทรก

irb(main):088:0> def batch_update(batch_data, cellfeed_uri, headers)
irb(main):089:1> batch_uri = cellfeed_uri + '/batch'
irb(main):090:1> batch_request = <<FEED
irb(main):091:1" <?xml version="1.0" encoding="utf-8"?> \
irb(main):092:1" <feed xmlns="http://www.w3.org/2005/Atom" \
irb(main):093:1" xmlns:batch="http://schemas.google.com/gdata/batch" \
irb(main):094:1" xmlns:gs="http://schemas.google.com/spreadsheets/2006" \
irb(main):095:1" xmlns:gd="http://schemas.google.com/g/2005">
irb(main):096:1" <id>#{cellfeed_uri}</id>
irb(main):097:1" FEED
irb(main):098:1> batch_data.each do |batch_request_data|
irb(main):099:2* version_string = get_version_string(cellfeed_uri + '/' + batch_request_data[:cell_id], headers)
irb(main):100:2> data = batch_request_data[:data]
irb(main):101:2> batch_id = batch_request_data[:batch_id]
irb(main):102:2> cell_id = batch_request_data[:cell_id]
irb(main):103:2> row = batch_request_data[:cell_id][1,1]
irb(main):104:2> column = batch_request_data[:cell_id][3,1]
irb(main):105:2> edit_link = cellfeed_uri + '/' + cell_id + '/' + version_string
irb(main):106:2> batch_request<< <<ENTRY
irb(main):107:2" <entry>
irb(main):108:2" <gs:cell col="#{column}" inputValue="#{data}" row="#{row}"/>
irb(main):109:2" <batch:id>#{batch_id}</batch:id>
irb(main):110:2" <batch:operation type="update" />
irb(main):111:2" <id>#{cellfeed_uri}/#{cell_id}</id>
irb(main):112:2" <link href="#{edit_link}" rel="edit" type="application/atom+xml" />
irb(main):113:2" </entry>
irb(main):114:2" ENTRY
irb(main):115:2> end
irb(main):116:1> batch_request << '</feed>'
irb(main):117:1> return post(batch_uri, batch_request, headers)
irb(main):118:1> end
=> nil

# Our sample batch data to insert information about the Python programming language into our worksheet
irb(main):119:0> batch_data = [ \
irb(main):120:0* {:batch_id => 'A', :cell_id => 'R5C1', :data => 'Python'}, \ 
irb(main):121:0* {:batch_id => 'B', :cell_id => 'R5C2', :data => 'http://python.org' } ]
=> [{:cell_id=>"R5C1", :data=>"Python", :batch_id=>"A"}=>{:cell_id=>"R5C2", :data=>"http://python.org", :batch_id=>"B"}]

# Perform the update
irb(main):122:0> response = batch_update(batch_data, cellfeed_uri, headers)
=> #<Net::HTTPOK 200 OK readbody=true>

# Parse the response.body XML and print it
irb(main):123:0> response_xml = XmlSimple.xml_in(response.body, 'KeyAttr' => 'name')
=> [ snip ]

irb(main):124:0> pp response_xml
{"title"=>[{"type"=>"text", "content"=>"Batch Feed"}],
 "xmlns:atom"=>"http://www.w3.org/2005/Atom",
 "id"=>
  ["http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full"],
 "entry"=>
  [{"status"=>[{"code"=>"200", "reason"=>"Success"}],
    "category"=>
     [{"term"=>"http://schemas.google.com/spreadsheets/2006#cell",
       "scheme"=>"http://schemas.google.com/spreadsheets/2006"}],
    "cell"=>
     [{"col"=>"1", "row"=>"5", "content"=>"Python", "inputValue"=>"Python"}],
    "title"=>[{"type"=>"text", "content"=>"A5"}],
    "id"=>
     ["http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full/R5C1",
      "A"],
    "operation"=>[{"type"=>"update"}],
    "content"=>{"type"=>"text", "content"=>"Python"},
    "link"=>
     [{"href"=>
        "http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full/R5C1",
       "rel"=>"self",
       "type"=>"application/atom+xml"},
      {"href"=>
        "http://spreadsheets.google.com/feeds/cells/o04927555739056712307.3387874275736238738/od6/private/full/R5C1/49kwzg",
       "rel"=>"edit",
       "type"=>"application/atom+xml"}],
    "updated"=>["2008-03-27T15:48:48.470Z"]},
    [ snip ]

คุณจะเห็นได้ว่าคําขอแบบกลุ่มของเราประสบความสําเร็จเนื่องจากเราได้รับรหัสตอบกลับ 200 ตกลง เมื่อแยกวิเคราะห์ XML คําตอบแล้ว คุณจะเห็นข้อความแยกต่างหากสําหรับ :batch_id แต่ละรายการที่เราตั้งค่าในอาร์เรย์ response_data ดูข้อมูลเพิ่มเติมเกี่ยวกับการประมวลผลแบบกลุ่มได้ในเอกสารประกอบการประมวลผลแบบกลุ่มใน GData

บทสรุป

ดังที่เห็นแล้ว คุณสามารถเรียกใช้ Shell แบบอินเทอร์แอกทีฟของ Ruby ได้อย่างง่ายดายด้วย Google Data API เราเข้าถึงสเปรดชีตและเวิร์กชีตโดยใช้ทั้ง listFeed และ cellFeed ได้ นอกจากนี้ เราได้แทรกข้อมูลใหม่โดยใช้คําขอ POST จากนั้นเขียนวิธีการอัปเดตเป็นกลุ่มด้วยโค้ดประมาณ 120 บรรทัด ในขั้นนี้ คุณไม่ควรจํากัดเมธอดง่ายๆ เหล่านี้ในชั้นเรียนแล้วสร้างเฟรมเวิร์กที่สามารถนํามาใช้ซ้ําได้

หากมีข้อสงสัยประการใดเกี่ยวกับการใช้เครื่องมือเหล่านี้กับ Google Data API โปรดเข้าร่วมกลุ่มสนทนา

คุณสามารถดูไฟล์คลาสที่มีตัวอย่างโค้ดโดยละเอียดด้านบนได้ที่ http://code.google.com/p/google-data-sample-ruby

พูดคุยเกี่ยวกับบทความนี้