استفاده از Ruby با Google Data API

یوخن هارتمن، تیم Google Data APIs
آوریل 2008

معرفی

روبی یک زبان اسکریپت نویسی پویا است که در سال های اخیر به دلیل چارچوب توسعه وب محبوب Rails مورد توجه زیادی قرار گرفته است. این مقاله نحوه استفاده از Ruby برای تعامل با سرویس‌های Google Data API را توضیح می‌دهد. ما روی Rails تمرکز نخواهیم کرد، در عوض بیشتر به توضیح دستورات HTTP و ساختار فیدهایمان علاقه مندیم. تمام مثال‌های ارائه‌شده در اینجا را می‌توان با استفاده از irb ، پوسته تعاملی Ruby از خط فرمان دنبال کرد.

همانطور که از مقاله cURL به یاد دارید، APIهای Google Data از پروتکل انتشار اتم برای نمایش، ایجاد و به روز رسانی منابع وب استفاده می کنند. زیبایی این پروتکل این است که از افعال استاندارد HTTP برای فرمول بندی درخواست هایی استفاده می شود که با کدهای وضعیت استاندارد HTTP پاسخ داده می شوند.

افعالی که در این مقاله استفاده خواهیم کرد عبارتند از GET برای بازیابی محتوا، POST برای آپلود محتوای جدید و PUT برای به روز رسانی محتوای موجود. برخی از کدهای استانداردی که ممکن است در استفاده از Google Data API با آنها برخورد کنید، 200 برای نشان دادن موفقیت در بازیابی فید یا ورودی یا 201 برای نشان دادن ایجاد یا به روز رسانی موفقیت آمیز یک منبع هستند. اگر مشکلی پیش بیاید، مانند زمانی که یک درخواست ناقص ارسال شود، یک کد 400 (به معنای «درخواست بد») بازگردانده می‌شود. پیام دقیق تری در بدنه پاسخ ارائه می شود و توضیح می دهد که دقیقا چه اشتباهی رخ داده است.

Ruby یک گزینه اشکال زدایی خوب را به عنوان بخشی از ماژول "Net" ارائه می دهد. با این حال، به خاطر کوتاه نگه داشتن این نمونه کدها، آن را در اینجا فعال نکرده ام.

دریافت و نصب Ruby

اگر از لینوکس استفاده می کنید، Ruby را می توان با استفاده از اکثر سیستم های مدیریت بسته نصب کرد. برای سایر سیستم عامل ها و دریافت کد منبع کامل، لطفاً به http://www.ruby-lang.org/en/downloads/ مراجعه کنید. Irb ، پوسته تعاملی که قرار است برای این نمونه ها استفاده کنیم باید به طور پیش فرض نصب شود. برای دنبال کردن نمونه‌های کد فهرست‌شده در اینجا، باید XmlSimple نیز نصب کنید، یک کتابخانه کوچک برای تجزیه XML به ساختارهای داده Ruby. برای دریافت/نصب XmlSimple، لطفاً به http://xml-simple.rubyforge.org مراجعه کنید.

هنگامی که یک کپی از Ruby در دستگاه خود دارید، می توانید از بسته Net:HTTP برای درخواست های اولیه به سرویس های داده Google استفاده کنید. قطعه زیر نحوه انجام واردات لازم از پوسته تعاملی 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 نباشیم، من فقط می خواستم توضیح دهم که چگونه با استفاده از HTTP یک درخواست پایه Google Data API ایجاد کنیم. اکنون می‌خواهیم API‌ها را تغییر دهیم و روی صفحه‌گسترده‌ها تمرکز کنیم، زیرا اطلاعاتی که می‌توانیم ارسال و بازیابی کنیم «خط فرمان دوستانه‌تر» است.

احراز هویت | با استفاده از Google Spreadsheets API

ما دوباره با بازیابی فید عناصر ورودی شروع می کنیم. اگرچه این بار می خواهیم با صفحات گسترده خودمان کار کنیم. برای انجام این کار، ابتدا باید با سرویس Google Accounts احراز هویت کنیم.

همانطور که ممکن است از اسناد مربوط به GData Authentication به یاد بیاورید، دو راه برای احراز هویت با سرویس های Google وجود دارد. AuthSub برای برنامه های کاربردی مبتنی بر وب است و به طور خلاصه شامل یک فرآیند تبادل توکن است. مزیت واقعی AuthSub این است که برنامه شما نیازی به ذخیره اطلاعات کاربری ندارد. ClientLogin برای برنامه های "نصب شده" است. در فرآیند ClientLogin، نام کاربری و رمز عبور از طریق https به همراه رشته‌ای که سرویسی را که می‌خواهید استفاده کنید را مشخص می‌کند، به سرویس‌های Google ارسال می‌شود. سرویس Google Spreadsheets API با رشته مشخص می شود.

با بازگشت به پوسته تعاملی خود، بیایید با 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 در بالا (و همچنین در اکثر لیست های دیگر) را قطع کرده ام. برای کاوش بیشتر در این صفحه گسترده، باید چند مرحله دیگر را انجام دهیم:

  1. کلید صفحه گسترده را دریافت کنید
  2. از کلید صفحه گسترده برای دریافت خوراک کاربرگ ما استفاده کنید
  3. شناسه کاربرگ مورد نظر خود را دریافت کنید
  4. از CellFeed یا listFeed برای دسترسی به محتوای واقعی کاربرگ درخواست کنید

این ممکن است کار زیادی به نظر برسد، اما من به شما نشان خواهم داد که اگر چند روش ساده بنویسیم، همه چیز بسیار آسان است. CellFeed و listFeed دو نمایش متفاوت برای محتوای سلولی واقعی یک کاربرگ هستند. listFeed یک ردیف کامل از اطلاعات را نشان می دهد و برای ارسال داده های جدید توصیه می شود. cellFeed سلول های منفرد را نشان می دهد و برای به روز رسانی سلول های جداگانه یا به روز رسانی دسته ای برای بسیاری از سلول های منفرد (هر دو با استفاده از PUT) استفاده می شود. لطفاً برای جزئیات بیشتر به اسناد API Google Spreadsheets مراجعه کنید.

ابتدا باید کلید صفحه گسترده را استخراج کنیم (که در خروجی 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 پیدا کنیم. قبل از اینکه لیست فید را بررسی کنیم، اجازه دهید به سرعت توضیح دهم که چه داده هایی در حال حاضر در صفحه گسترده نمونه ما وجود دارد تا بدانید ما به دنبال چه چیزی هستیم:

صفحه گسترده ما بسیار ساده است و به شکل زیر است:

زبان سایت اینترنتی
جاوا http://java.com
php http://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>

برای مقایسه سریع، اجازه دهید به نحوه نمایش همان اطلاعات در CellFeed نگاه کنیم:

# 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 ورودی برگردانده می شود، یکی برای هر سلول. من تمام خروجی های دیگر را به غیر از مقدار سلول A1 که حاوی کلمه 'language' است، قطع کرده ام. همچنین به لینک ویرایش نشان داده شده در بالا توجه کنید. این پیوند حاوی یک رشته نسخه (8srvbs) در پایان است. رشته نسخه هنگام به روز رسانی داده های سلولی مهم است، همانطور که در پایان این مقاله انجام خواهیم داد. این اطمینان حاصل می کند که به روز رسانی ها بازنویسی نمی شوند. هر زمان که برای به‌روزرسانی داده‌های سلولی درخواست PUT می‌کنید، باید رشته آخرین نسخه سلول را در درخواست خود بگنجانید. یک رشته نسخه جدید پس از هر به روز رسانی بازگردانده می شود.

ارسال محتوا به listFeed

اولین چیزی که برای ارسال محتوا به آن نیاز داریم پیوند POST برای listFeed است. هنگامی که فید لیست درخواست شود، این پیوند برگردانده خواهد شد. این نشانی اینترنتی 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 را در محتوای موجود ترجیح می‌دهند. اما از آنجایی که اطلاعاتی که قبلاً از CellFeed در بالا بازیابی کردیم، فقط داده‌هایی بود که قبلاً در صفحه گسترده واقعی ما وجود داشت، چگونه می‌توانیم اطلاعات جدیدی اضافه کنیم؟ ما فقط باید برای هر سلول خالی که می‌خواهیم داده‌ها را در آن وارد کنیم، درخواستی بدهیم. قطعه زیر نحوه بازیابی سلول خالی R5C1 (ردیف 5، ستون 1) را نشان می دهد که می خواهیم اطلاعاتی در مورد زبان برنامه نویسی پایتون در آن درج کنیم.

متغیر اصلی cellfeed_uri ما فقط حاوی URI برای خود cellfeed بود. اکنون می‌خواهیم سلولی را که به دنبال ویرایش آن هستیم اضافه کنیم و رشته نسخه سلولی را برای انجام ویرایش خود به دست آوریم:

# 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 OK را دریافت کردیم. با تجزیه XML پاسخ، می‌توانیم ببینیم که برای هر فرد :batch_id که در آرایه response_data خود تنظیم کرده‌ایم، یک پیام جداگانه بازگردانده می‌شود. برای اطلاعات بیشتر در مورد پردازش دسته ای، لطفاً به پردازش دسته ای در اسناد GData مراجعه کنید.

نتیجه

همانطور که مشاهده کردید، استفاده از پوسته تعاملی Ruby برای بازی با Google Data API بسیار آسان است. ما توانستیم با استفاده از listFeed وcellFeed به صفحات گسترده و کاربرگ های خود دسترسی پیدا کنیم. علاوه بر این، ما برخی از داده های جدید را با استفاده از یک درخواست POST وارد کرده ایم و سپس روش هایی را برای انجام یک به روز رسانی دسته ای تنها با حدود 120 خط کد نوشته ایم. از این نقطه، پیچیده کردن برخی از این روش‌های ساده در کلاس‌ها و ساختن یک چارچوب قابل استفاده مجدد برای خود، نباید خیلی سخت باشد.

لطفاً اگر در مورد استفاده از این ابزارها با Google Data API مورد علاقه خود سؤالی دارید، در گروه های بحث به ما بپیوندید.

یک فایل کلاس با نمونه کدهای شرح داده شده در بالا در http://code.google.com/p/google-data-samples-ruby یافت می شود.

در مورد این مقاله بحث کنید!