Kwiecień 2008 r.
- Wprowadzenie
- Pobieranie i instalowanie Ruby
- Uwierzytelnianie | Korzystanie z interfejsu Spreadsheets API
- Uzyskiwanie arkuszy
- Publikowanie treści w pliku listFeed
- Aktualizowanie treści za pomocą pliku cellsFeed
- Podsumowanie
Wprowadzenie
Ruby to dynamiczny język skryptowy, który w ostatnich latach zyskał dużą popularność dzięki popularnemu frameworkowi Rails do tworzenia stron internetowych. Z tego artykułu dowiesz się, jak używać języka Ruby do interakcji z usługami interfejsu Google Data API. Nie będziemy się skupiać na Rails, ale raczej na wyjaśnianiu podstawowych poleceń HTTP i struktury naszych plików danych. Wszystkie przykłady przedstawione w tym dokumencie można wykonać w wierszu poleceń za pomocą interaktywnej powłoki Ruby – irb.
Jak być może pamiętasz z artykułu o cURL, interfejsy Google Data API używają protokołu publikowania Atom do reprezentowania, tworzenia i aktualizowania zasobów internetowych. Zaletą tego protokołu jest to, że do formułowania żądań używane są standardowe czasowniki HTTP, na które odpowiada się standardowymi kodami stanu HTTP.
W tym artykule będziemy używać czasowników GET do pobierania treści, POST do przesyłania nowych treści i PUT do aktualizowania istniejących treści. Niektóre standardowe kody, które możesz napotkać podczas korzystania z interfejsów Google Data API, to 200, który oznacza powodzenie pobierania pliku danych lub wpisu, oraz 201, który oznacza powodzenie tworzenia lub aktualizowania zasobu. Jeśli coś pójdzie nie tak, np. zostanie wysłane nieprawidłowe żądanie, zostanie zwrócony kod 400 (oznaczający „Nieprawidłowe żądanie”). W treści odpowiedzi znajdziesz bardziej szczegółowy komunikat wyjaśniający, co dokładnie poszło nie tak.
Ruby udostępnia przydatną opcję debugowania w ramach modułu „Net”. Aby te przykłady kodu nie były zbyt długie, nie włączam tu tej funkcji.
Pobieranie i instalowanie Ruby
Jeśli używasz Linuksa, możesz zainstalować Ruby za pomocą większości systemów zarządzania pakietami. W przypadku innych systemów operacyjnych i aby uzyskać pełny kod źródłowy, odwiedź stronę http://www.ruby-lang.org/en/downloads/. Irb, czyli interaktywna powłoka, której będziemy używać w tych przykładach, powinna być zainstalowana domyślnie. Aby korzystać z przykładów kodu podanych poniżej, musisz też zainstalować XmlSimple
, czyli małą bibliotekę do analizowania kodu XML i przekształcania go w struktury danych Ruby. Aby pobrać lub zainstalować XmlSimple, wejdź na stronę http://xml-simple.rubyforge.org/.
Gdy na komputerze będzie działać kopia Ruby, możesz użyć pakietu Net:HTTP
, aby wysyłać podstawowe żądania do usług danych Google. Fragment kodu poniżej pokazuje, jak wykonać niezbędne importy z interaktywnej powłoki Ruby. Wymagamy pakietu „net/http”, analizujemy adres URL najpopularniejszego kanału wideo z YouTube, a następnie wykonujemy żądanie 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>
W wierszu poleceń powinno się wyświetlić sporo kodu XML. Zapewne zauważysz, że wszystkie elementy znajdują się w elemencie <feed> i są określane jako elementy <entry>. Nie przejmujmy się jeszcze formatowaniem XML. Chcę tylko wyjaśnić, jak wysłać podstawowe żądanie do interfejsu Google Data API za pomocą protokołu HTTP. Teraz przełączymy się na interfejsy API Arkuszy, ponieważ informacje, które możemy wysyłać i pobierać, są bardziej „przyjazne dla wiersza poleceń”.
Uwierzytelnianie | Korzystanie z interfejsu Google Spreadsheets API
Ponownie zaczniemy od pobrania pliku danych z elementami wpisu. Tym razem jednak będziemy chcieli pracować z własnymi arkuszami kalkulacyjnymi. Aby to zrobić, musimy najpierw uwierzytelnić się w usłudze kont Google.
Jak być może pamiętasz z dokumentacji dotyczącej uwierzytelniania GData, istnieją 2 sposoby uwierzytelniania w usługach Google. AuthSub jest przeznaczony dla aplikacji internetowych i w skrócie polega na procesie wymiany tokenów. Prawdziwą zaletą AuthSub jest to, że aplikacja nie musi przechowywać danych logowania użytkownika. ClientLogin jest przeznaczony dla aplikacji „zainstalowanych”. W procesie ClientLogin nazwa użytkownika i hasło są wysyłane do usług Google przez https wraz z ciągiem znaków identyfikującym usługę, z której chcesz korzystać. Usługa interfejsu Google Spreadsheets API jest identyfikowana przez ciąg znaków wise.
Wróćmy do interaktywnej powłoki i uwierzytelnijmy się w Google. Pamiętaj, że do wysłania prośby o uwierzytelnienie i danych logowania używamy protokołu 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 ]
OK. Po uwierzytelnieniu spróbujmy pobrać własne arkusze kalkulacyjne, wysyłając żądanie do
http://spreadsheets.google.com/feeds/spreadsheets/private/full
Ponieważ jest to uwierzytelnione żądanie, chcemy też przekazać nasze nagłówki. W rzeczywistości, ponieważ będziemy wysyłać kilka żądań dotyczących różnych plików danych, możemy zawrzeć tę funkcję w prostej funkcji, którą nazwiemy 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> ...
Ponownie widzimy dużo kodu XML, który powyżej został przeze mnie pominięty, ponieważ nie musisz się martwić jego rozszyfrowaniem z poziomu wiersza poleceń. Aby ułatwić korzystanie z tej funkcji, przeanalizujmy ją i przekształćmy w strukturę danych za pomocą 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 ]
Uzyskiwanie arkuszy
Jak widać w powyższych wynikach, mój plik danych zawiera 6 arkuszy kalkulacyjnych. Aby ten artykuł był krótki, skróciłem powyższy wynik XML (podobnie jak w większości innych list). Aby dokładniej przeanalizować ten arkusz kalkulacyjny, musimy wykonać jeszcze kilka czynności:
- Uzyskiwanie klucza arkusza kalkulacyjnego
- Użyj klucza arkusza kalkulacyjnego, aby uzyskać plik danych arkusza
- Uzyskaj identyfikator arkusza, którego chcesz użyć.
- Aby uzyskać dostęp do rzeczywistej zawartości arkusza, wyślij żądanie dotyczące pliku cellsFeed lub listFeed.
Może się to wydawać pracochłonne, ale pokażę Ci, że jeśli napiszemy kilka prostych metod, wszystko będzie dość łatwe. cellsFeed i listFeed to 2 różne reprezentacje rzeczywistej zawartości komórek arkusza. listFeed reprezentuje cały wiersz informacji i jest zalecany do przesyłania nowych danych metodą POST. Element cellFeed reprezentuje poszczególne komórki i jest używany do aktualizowania pojedynczych komórek lub wielu komórek naraz (w obu przypadkach używana jest metoda PUT). Więcej informacji znajdziesz w dokumentacji interfejsu Google Spreadsheets API.
Najpierw musimy wyodrębnić klucz arkusza kalkulacyjnego (wyróżniony w powyższym wyjściu XML), aby następnie uzyskać plik danych arkusza:
# 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 ]
Jak widać, teraz możemy znaleźć linki (highlighted above
) do uzyskania dostępu do listFeed i cellsFeed. Zanim przejdziemy do listFeed, wyjaśnię, jakie dane znajdują się obecnie w naszym przykładowym arkuszu kalkulacyjnym, abyś wiedział(-a), czego szukamy:
Nasz arkusz kalkulacyjny jest bardzo prosty i wygląda tak:
language | witryna |
---|---|
java | http://java.com |
php | http://php.net |
A tak te dane wyglądają w pliku danych o produktach:
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 ]
Jak widać, funkcja listFeed zwraca zawartość arkusza, tworząc wpis dla każdego wiersza. Zakłada on, że pierwszy wiersz arkusza kalkulacyjnego zawiera nagłówki komórek, a następnie dynamicznie generuje nagłówki XML na podstawie danych w tym wierszu. Wyjaśnienie znajdziesz w tym przykładzie pliku 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>
Aby szybko porównać, zobaczmy, jak te same informacje są przedstawione w pliku danych o komórkach:
# 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 ]
Jak widać, zwrócono 6 wpisów, po jednym dla każdej komórki. Usunąłem wszystkie inne dane wyjściowe oprócz wartości komórki A1, która zawiera słowo „language”. Zwróć też uwagę na link Edytuj widoczny powyżej. Ten link zawiera na końcu ciąg znaków wersji (8srvbs). Ciąg wersji jest ważny podczas aktualizowania danych komórki, co zrobimy na końcu tego artykułu. Dzięki temu aktualizacje nie zostaną nadpisane. Za każdym razem, gdy wysyłasz żądanie PUT w celu zaktualizowania danych komórki, musisz dołączyć do niego ciąg tekstowy z najnowszą wersją komórki. Po każdej aktualizacji zwracany jest nowy ciąg wersji.
publikowanie treści na liścieFeed,
Pierwszą rzeczą, której potrzebujemy, aby opublikować treści, jest link POST do elementu listFeed. Ten link zostanie zwrócony, gdy zostanie wysłana prośba o kanał listy. Będzie on zawierać adres URL http://schemas.google.com/g/2005#post
jako wartość atrybutu rel
. Musisz przeanalizować ten element linku i wyodrębnić z niego atrybut href
. Najpierw utworzymy małą metodę, która ułatwi publikowanie:
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>
Stan 201 oznacza, że nasz post został opublikowany.
Aktualizowanie treści za pomocą pliku cellsFeed
Z dokumentacji wynika, że w przypadku istniejących treści plik danych o komórkach preferuje żądania PUT. Jednak informacje, które wcześniej pobraliśmy z obiektu cellsFeed, to tylko dane, które już znajdowały się w naszym arkuszu kalkulacyjnym. Jak więc dodać nowe informacje? Wystarczy, że wyślemy prośbę dotyczącą każdej pustej komórki, do której chcemy wprowadzić dane. Fragment kodu poniżej pokazuje, jak pobrać pustą komórkę R5C1 (wiersz 5, kolumna 1), w której chcemy wstawić informacje o języku programowania Python.
Nasza pierwotna zmienna cellfeed_uri
zawierała tylko identyfikator URI samego pliku danych komórek. Teraz chcemy dołączyć komórkę, którą chcemy edytować, i uzyskać ciąg wersji tej komórki, aby wprowadzić zmiany:
# 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>"
Jak widać na powyższej liście kodu, ciąg wersji to 47pc
. (Może być konieczne przewinięcie do końca w prawo). Aby ułatwić sobie zadanie, utwórzmy wygodną metodę, która pobierze ciąg wersji dla dowolnej komórki, która nas interesuje:
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
Przy okazji możemy też napisać metodę do wykonywania żądania PUT, a jeszcze lepiej, napiszmy metodę do wykonywania całej aktualizacji zbiorczej. Nasza funkcja będzie przyjmować tablicę skrótów zawierających te zmienne:
:batch_id
– unikalny identyfikator każdej części żądania zbiorczego.:cell_id
– identyfikator komórki do zaktualizowania w formacie R#C#, gdzie komórka A1 jest reprezentowana jako R1C1.:data
– dane, które chcemy wstawić.
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 ]
Jak widać, nasze żądanie wsadowe zostało zrealizowane, ponieważ otrzymaliśmy kod odpowiedzi 200 OK. Analizując kod XML odpowiedzi, możemy zauważyć, że dla każdego elementu :batch_id
, który ustawiliśmy w naszej tablicy response_data
, zwracana jest osobna wiadomość. Więcej informacji o przetwarzaniu wsadowym znajdziesz w dokumentacji Przetwarzanie wsadowe w GData.
Podsumowanie
Jak widzisz, korzystanie z interaktywnej powłoki Ruby do eksperymentowania z interfejsami Google Data API jest bardzo proste. Mogliśmy uzyskać dostęp do arkuszy kalkulacyjnych i arkuszy za pomocą listFeed i cellsFeed. Dodaliśmy też nowe dane za pomocą żądania POST, a następnie napisaliśmy metody umożliwiające przeprowadzenie aktualizacji zbiorczej za pomocą około 120 wierszy kodu. Od tego momentu nie powinno być zbyt trudno opakować niektórych z tych prostych metod w klasy i stworzyć własne ramy wielokrotnego użytku.
Jeśli masz pytania dotyczące korzystania z tych narzędzi z ulubionym interfejsem Google Data API, dołącz do grup dyskusyjnych.
Plik klasy z przykładowymi fragmentami kodu opisanymi powyżej znajdziesz na stronie http://code.google.com/p/google-data-samples-ruby.