Tháng 4 năm 2008
- Giới thiệu
- Lấy và cài đặt Ruby
- Xác thực | Sử dụng Spreadsheets API
- Lấy trang tính
- Đăng nội dung vào listFeed
- Sử dụng cellsFeed để cập nhật nội dung
- Lời kết
Giới thiệu
Ruby là một ngôn ngữ kịch bản động đã thu hút được nhiều sự chú ý trong những năm gần đây nhờ khung phát triển web Rails phổ biến. Bài viết này sẽ giải thích cách sử dụng Ruby để tương tác với các dịch vụ Google Data API. Chúng ta sẽ không tập trung vào Rails, thay vào đó, chúng ta quan tâm hơn đến việc giải thích các lệnh HTTP cơ bản và cấu trúc của nguồn cấp dữ liệu. Bạn có thể làm theo tất cả các ví dụ được trình bày ở đây từ dòng lệnh bằng cách sử dụng irb, trình bao tương tác của Ruby.
Như bạn có thể nhớ lại từ bài viết về cURL, API Dữ liệu của Google sử dụng Giao thức xuất bản Atom để biểu thị, tạo và cập nhật tài nguyên web. Ưu điểm của giao thức này là sử dụng các động từ HTTP tiêu chuẩn để tạo ra các yêu cầu được trả lời bằng mã trạng thái HTTP tiêu chuẩn.
Các động từ mà chúng ta sẽ sử dụng trong bài viết này là GET để truy xuất nội dung, POST để tải nội dung mới lên và PUT để cập nhật nội dung hiện có. Một số mã tiêu chuẩn mà bạn có thể gặp phải khi sử dụng Google Data API là 200 để biểu thị việc truy xuất thành công một nguồn cấp dữ liệu hoặc một mục nhập, hoặc 201 để biểu thị việc tạo hoặc cập nhật thành công một tài nguyên. Nếu có lỗi xảy ra, chẳng hạn như khi một yêu cầu không đúng định dạng được gửi, mã 400 (nghĩa là "Yêu cầu không hợp lệ") sẽ được gửi lại. Một thông báo chi tiết hơn sẽ được cung cấp trong phần nội dung phản hồi, giải thích chính xác vấn đề đã xảy ra.
Ruby cung cấp một lựa chọn gỡ lỗi hữu ích trong mô-đun "Net". Để giữ cho các mẫu mã này có độ dài hợp lý, tôi không bật tính năng này ở đây.
Lấy và cài đặt Ruby
Bạn có thể cài đặt Ruby bằng hầu hết các hệ thống quản lý gói nếu đang dùng Linux. Đối với các hệ điều hành khác và để lấy mã nguồn đầy đủ, vui lòng truy cập http://www.ruby-lang.org/en/downloads/. Irb, shell tương tác mà chúng ta sẽ sử dụng cho các ví dụ này sẽ được cài đặt theo mặc định. Để làm theo các ví dụ về mã được liệt kê ở đây, bạn cũng cần cài đặt XmlSimple
, một thư viện nhỏ để phân tích cú pháp XML thành cấu trúc dữ liệu Ruby. Để tải/cài đặt XmlSimple, vui lòng truy cập http://xml-simple.rubyforge.org/
Sau khi có bản sao của Ruby đang chạy trên máy, bạn có thể dùng gói Net:HTTP
để đưa ra các yêu cầu cơ bản đối với Dịch vụ dữ liệu của Google. Đoạn mã dưới đây cho biết cách nhập các thành phần cần thiết từ trình bao tương tác của Ruby. Chúng ta đang yêu cầu gói "net/http", phân tích cú pháp URL cho nguồn cấp dữ liệu video được đánh giá cao nhất trên YouTube, sau đó thực hiện yêu cầu 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>
Yêu cầu đó sẽ phản hồi khá nhiều XML cho dòng lệnh. Có thể bạn đã nhận thấy rằng tất cả các mục đều nằm trong một phần tử <feed> và được gọi là phần tử <entry>. Chúng ta chưa cần lo lắng về định dạng XML, tôi chỉ muốn giải thích cách thực hiện một yêu cầu cơ bản của Google Data API bằng HTTP. Giờ đây, chúng ta sẽ chuyển sang dùng API và tập trung vào Trang tính, vì thông tin mà chúng ta có thể gửi và truy xuất sẽ "thân thiện với dòng lệnh" hơn.
Xác thực | Sử dụng API Google Trang tính
Chúng ta sẽ bắt đầu bằng cách truy xuất một nguồn cấp dữ liệu gồm các phần tử mục nhập. Tuy nhiên, lần này chúng ta sẽ muốn làm việc với bảng tính của riêng mình. Để làm việc đó, trước tiên, chúng ta phải xác thực bằng dịch vụ Tài khoản Google.
Như bạn có thể nhớ lại trong tài liệu về Xác thực GData, có 2 cách để xác thực bằng các dịch vụ của Google. AuthSub dành cho các ứng dụng dựa trên web và về cơ bản, quy trình này liên quan đến quy trình trao đổi mã thông báo. Lợi ích thực sự của AuthSub là ứng dụng của bạn không cần lưu trữ thông tin đăng nhập của người dùng. ClientLogin dành cho các ứng dụng "đã cài đặt". Trong quy trình ClientLogin, tên người dùng và mật khẩu sẽ được gửi đến các dịch vụ của Google qua https cùng với một chuỗi xác định dịch vụ mà bạn muốn sử dụng. Dịch vụ Google Trang tính API được xác định bằng chuỗi wise.
Quay lại shell tương tác, hãy xác thực bằng Google. Xin lưu ý rằng chúng tôi đang sử dụng https để gửi yêu cầu xác thực và thông tin đăng nhập:
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 ]
Được rồi. Giờ đây, sau khi xác thực, hãy thử truy xuất bảng tính của riêng chúng ta bằng cách gửi yêu cầu đến
http://spreadsheets.google.com/feeds/spreadsheets/private/full
Vì đây là một yêu cầu đã xác thực, nên chúng ta cũng muốn truyền các tiêu đề vào. Trên thực tế, vì sẽ đưa ra một số yêu cầu cho nhiều nguồn cấp dữ liệu, nên chúng ta cũng có thể gói chức năng này vào một hàm đơn giản mà chúng ta sẽ gọi là 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> ...
Một lần nữa, chúng ta thấy rất nhiều XML mà tôi đã giảm bớt ở trên vì bạn không cần lo lắng về việc giải mã nó từ dòng lệnh. Để mọi thứ trở nên thân thiện hơn với người dùng, thay vào đó, hãy phân tích cú pháp thành một cấu trúc dữ liệu bằng cách sử dụng 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 ]
Lấy trang tính
Như bạn có thể thấy trong đầu ra ở trên, nguồn cấp dữ liệu của tôi chứa 6 bảng tính. Để bài viết này ngắn gọn, tôi đã cắt phần còn lại của đầu ra XML ở trên (cũng như trong hầu hết các danh sách khác). Để tìm hiểu kỹ hơn về bảng tính này, chúng ta cần thực hiện thêm một số bước:
- Lấy khoá bảng tính
- Sử dụng khoá bảng tính để lấy nguồn cấp dữ liệu trang tính của chúng tôi
- Lấy mã nhận dạng cho trang tính mà chúng ta muốn sử dụng
- Yêu cầu cellsFeed hoặc listFeed để truy cập vào nội dung thực tế của trang tính
Điều này có vẻ như tốn nhiều công sức nhưng tôi sẽ cho bạn thấy rằng mọi thứ đều khá dễ dàng nếu chúng ta viết một vài phương thức đơn giản. cellsFeed và listFeed là hai cách biểu thị khác nhau cho nội dung thực tế của một ô trong trang tính. listFeed đại diện cho toàn bộ hàng thông tin và nên dùng để đăng dữ liệu mới. cellFeed đại diện cho từng ô và được dùng cho cả việc cập nhật từng ô hoặc cập nhật hàng loạt cho nhiều ô (cả hai đều dùng PUT). Vui lòng tham khảo tài liệu về Google Spreadsheets API để biết thêm thông tin chi tiết.
Trước tiên, chúng ta cần trích xuất khoá bảng tính (được đánh dấu trong đầu ra XML ở trên) để sau đó lấy nguồn cấp dữ liệu trang tính:
# 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 ]
Như bạn có thể thấy ở đây, giờ đây, chúng ta có thể tìm thấy các đường liên kết (highlighted above
) để truy cập vào listFeed và cellsFeed. Trước khi đi sâu vào listFeed, hãy để tôi giải thích nhanh về dữ liệu hiện có trong bảng tính mẫu của chúng ta để bạn biết chúng ta đang tìm kiếm những gì:
Bảng tính của chúng tôi rất đơn giản và chỉ có dạng như sau:
language | trang web |
---|---|
java | http://java.com |
php | http://php.net |
Sau đây là cách dữ liệu này xuất hiện trong 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 ]
Như bạn thấy, listFeed trả về nội dung của trang tính bằng cách tạo một mục cho mỗi hàng. Công cụ này giả định rằng hàng đầu tiên của bảng tính chứa tiêu đề ô của bạn, sau đó tự động tạo tiêu đề XML dựa trên dữ liệu trong hàng đó. Việc xem xét XML thực tế sẽ giúp giải thích thêm về vấn đề này:
<?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>
Để so sánh nhanh, hãy xem cách thông tin tương tự được trình bày trong cellsFeed:
# 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 ]
Như bạn có thể thấy ở đây, 6 mục được trả về, mỗi mục cho một ô. Tôi đã cắt bỏ tất cả các đầu ra khác ngoài giá trị cho ô A1, ô này chứa từ "language" (ngôn ngữ). Ngoài ra, hãy lưu ý đường liên kết chỉnh sửa xuất hiện ở trên. Đường liên kết này có chứa một chuỗi phiên bản (8srvbs) ở cuối. Chuỗi phiên bản rất quan trọng khi cập nhật dữ liệu ô, như chúng ta sẽ làm ở cuối bài viết này. Việc này giúp đảm bảo các bản cập nhật không bị ghi đè. Bất cứ khi nào đưa ra yêu cầu PUT để cập nhật dữ liệu ô, bạn phải thêm chuỗi phiên bản mới nhất của ô vào yêu cầu. Một chuỗi phiên bản mới sẽ được trả về sau mỗi lần cập nhật.
Đăng nội dung vào listFeed
Điều đầu tiên chúng ta cần để đăng nội dung là đường liên kết POST cho listFeed. Đường liên kết này sẽ được trả về khi bạn yêu cầu nguồn cấp dữ liệu danh sách. Nó sẽ chứa URL http://schemas.google.com/g/2005#post
làm giá trị cho thuộc tính rel
. Bạn sẽ cần phân tích cú pháp phần tử đường liên kết này và trích xuất thuộc tính href
của phần tử đó. Trước tiên, chúng ta sẽ tạo một phương thức nhỏ để đăng bài dễ dàng hơn:
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>
Trạng thái 201 cho biết bài đăng của chúng tôi đã thành công.
Sử dụng cellsFeed để cập nhật nội dung
Theo tài liệu, chúng ta có thể thấy rằng nguồn cấp dữ liệu ô ưu tiên các yêu cầu PUT đối với nội dung hiện có. Nhưng vì thông tin mà chúng ta truy xuất từ cellsFeed trước đó chỉ là dữ liệu đã có trong bảng tính thực tế của chúng ta, làm cách nào để thêm thông tin mới? Chúng ta chỉ cần đưa ra yêu cầu cho từng ô trống mà chúng ta muốn nhập dữ liệu. Đoạn mã dưới đây cho biết cách truy xuất ô trống R5C1 (Hàng 5, Cột 1) mà chúng ta muốn chèn một số thông tin về ngôn ngữ lập trình Python.
Biến ban đầu cellfeed_uri
chỉ chứa URI cho chính cellfeed. Bây giờ, chúng ta muốn thêm ô mà chúng ta đang tìm cách chỉnh sửa và lấy chuỗi phiên bản của ô đó để chỉnh sửa:
# 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>"
Như bạn có thể thấy trong danh sách mã ở trên, chuỗi phiên bản là 47pc
. (Có thể bạn phải di chuyển đến tận cùng bên phải.) Để mọi việc trở nên dễ dàng hơn, hãy tạo một phương thức tiện lợi giúp chúng ta lấy chuỗi phiên bản cho mọi ô mà chúng ta quan tâm:
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
Đồng thời, chúng ta cũng có thể viết một phương thức để thực hiện yêu cầu PUT, hoặc tốt hơn là viết một phương thức để thực hiện toàn bộ hoạt động cập nhật hàng loạt. Hàm của chúng ta sẽ lấy một mảng gồm các hàm băm chứa các biến sau:
:batch_id
– Giá trị nhận dạng riêng biệt cho từng phần của yêu cầu hàng loạt.:cell_id
– Mã nhận dạng của ô cần cập nhật ở định dạng R#C#, trong đó ô A1 sẽ được biểu thị là R1C1.:data
– Dữ liệu mà chúng ta muốn chèn.
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 ]
Như bạn thấy, yêu cầu hàng loạt của chúng ta đã thành công vì chúng ta đã nhận được mã phản hồi 200 OK. Khi phân tích cú pháp XML phản hồi, chúng ta có thể thấy rằng một thông báo riêng biệt được trả về cho từng :batch_id
mà chúng ta đặt trong mảng response_data
. Để biết thêm thông tin về quy trình xử lý hàng loạt, vui lòng tham khảo tài liệu Xử lý hàng loạt trong GData.
Kết luận
Như bạn đã thấy, rất dễ dàng sử dụng trình bao tương tác của Ruby để khám phá các API Dữ liệu của Google. Chúng tôi có thể truy cập vào bảng tính và trang tính bằng cả listFeed và cellsFeed. Ngoài ra, chúng tôi đã chèn một số dữ liệu mới bằng cách sử dụng yêu cầu POST, sau đó viết các phương thức để thực hiện một bản cập nhật hàng loạt chỉ với khoảng 120 dòng mã. Từ thời điểm này, bạn sẽ không gặp quá nhiều khó khăn khi bao bọc một số phương thức đơn giản này vào các lớp và tự xây dựng một khung có thể sử dụng lại.
Vui lòng tham gia nhóm thảo luận nếu bạn có thắc mắc về cách sử dụng các công cụ này với Google Data API mà bạn yêu thích.
Bạn có thể tìm thấy tệp lớp có các mẫu mã được trình bày chi tiết ở trên tại http://code.google.com/p/google-data-samples-ruby