google-spreadsheet-ruby 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (2) hide show
  1. data/lib/google_spreadsheet.rb +128 -94
  2. metadata +40 -15
@@ -8,7 +8,8 @@ require "open-uri"
8
8
  require "cgi"
9
9
  require "uri"
10
10
  require "rubygems"
11
- require "hpricot"
11
+ require 'nokogiri'
12
+
12
13
  require "oauth"
13
14
  Net::HTTP.version_1_2
14
15
 
@@ -98,14 +99,6 @@ module GoogleSpreadsheet
98
99
  return CGI.escapeHTML(str.to_s())
99
100
  end
100
101
 
101
- def as_utf8(str)
102
- if str.respond_to?(:force_encoding)
103
- str.force_encoding("UTF-8")
104
- else
105
- str
106
- end
107
- end
108
-
109
102
  end
110
103
 
111
104
 
@@ -142,11 +135,8 @@ module GoogleSpreadsheet
142
135
 
143
136
  # Restores session using return value of auth_tokens method of previous session.
144
137
  def initialize(auth_tokens = nil, oauth_token = nil)
145
- if oauth_token
146
- @oauth_token = oauth_token
147
- else
148
- @auth_tokens = auth_tokens
149
- end
138
+ @oauth_token = oauth_token
139
+ @auth_tokens = auth_tokens || {}
150
140
  end
151
141
 
152
142
  # Authenticates with given +mail+ and +password+, and updates current session object
@@ -176,10 +166,11 @@ module GoogleSpreadsheet
176
166
  attr_accessor :on_auth_fail
177
167
 
178
168
  def auth_header(auth) #:nodoc:
179
- if auth == :none
180
- return {}
169
+ token = auth == :none ? nil : @auth_tokens[auth]
170
+ if token
171
+ return {"Authorization" => "GoogleLogin auth=#{token}"}
181
172
  else
182
- return {"Authorization" => "GoogleLogin auth=#{@auth_tokens[auth]}"}
173
+ return {}
183
174
  end
184
175
  end
185
176
 
@@ -194,10 +185,10 @@ module GoogleSpreadsheet
194
185
  query = encode_query(params)
195
186
  doc = request(:get, "https://spreadsheets.google.com/feeds/spreadsheets/private/full?#{query}")
196
187
  result = []
197
- for entry in doc.search("entry")
198
- title = as_utf8(entry.search("title").text)
199
- url = as_utf8(entry.search(
200
- "link[@rel='http://schemas.google.com/spreadsheets/2006#worksheetsfeed']")[0]["href"])
188
+ doc.css("feed > entry").each() do |entry|
189
+ title = entry.css("title").text
190
+ url = entry.css(
191
+ "link[@rel='http://schemas.google.com/spreadsheets/2006#worksheetsfeed']")[0]["href"]
201
192
  result.push(Spreadsheet.new(self, url, title))
202
193
  end
203
194
  return result
@@ -260,68 +251,70 @@ module GoogleSpreadsheet
260
251
  EOS
261
252
 
262
253
  doc = request(:post, feed_url, :data => xml, :auth => :writely)
263
- ss_url = as_utf8(doc.search(
264
- "link[@rel='http://schemas.google.com/spreadsheets/2006#worksheetsfeed']")[0]["href"])
254
+ ss_url = doc.css(
255
+ "link[@rel='http://schemas.google.com/spreadsheets/2006#worksheetsfeed']").first['href']
265
256
  return Spreadsheet.new(self, ss_url, title)
266
257
  end
267
258
 
268
259
  def request(method, url, params = {}) #:nodoc:
269
260
  # Always uses HTTPS.
270
- uri = URI.parse(url.gsub(%r{^http://}, "https://"))
261
+ url = url.gsub(%r{^http://}, "https://")
271
262
  data = params[:data]
272
263
  auth = params[:auth] || :wise
273
264
  if params[:header]
274
- add_header = params[:header]
265
+ extra_header = params[:header]
266
+ elsif data
267
+ extra_header = {"Content-Type" => "application/atom+xml"}
275
268
  else
276
- add_header = data ? {"Content-Type" => "application/atom+xml"} : {}
269
+ extra_header = {}
277
270
  end
278
271
  response_type = params[:response_type] || :xml
279
272
 
273
+ while true
274
+ response = request_raw(method, url, data, extra_header, auth)
275
+ if response.code == "401" && @on_auth_fail && @on_auth_fail.call()
276
+ next
277
+ end
278
+ if !(response.code =~ /^2/)
279
+ raise(
280
+ response.code == "401" ? AuthenticationError : GoogleSpreadsheet::Error,
281
+ "Response code #{response.code} for #{method} #{url}: " +
282
+ CGI.unescapeHTML(response.body))
283
+ end
284
+ return convert_response(response, response_type)
285
+ end
286
+ end
287
+
288
+ private
289
+
290
+ def request_raw(method, url, data, extra_header, auth)
280
291
  if @oauth_token
281
-
282
292
  if method == :delete || method == :get
283
- response = @oauth_token.__send__(method, url, add_header)
293
+ return @oauth_token.__send__(method, url, extra_header)
284
294
  else
285
- response = @oauth_token.__send__(method, url, data, add_header)
295
+ return @oauth_token.__send__(method, url, data, extra_header)
286
296
  end
287
- return convert_response(response, response_type)
288
-
289
297
  else
290
-
298
+ uri = URI.parse(url)
291
299
  http = Net::HTTP.new(uri.host, uri.port)
292
300
  http.use_ssl = true
293
301
  http.verify_mode = OpenSSL::SSL::VERIFY_NONE
294
302
  http.start() do
295
- while true
296
- path = uri.path + (uri.query ? "?#{uri.query}" : "")
297
- header = auth_header(auth).merge(add_header)
298
- if method == :delete || method == :get
299
- response = http.__send__(method, path, header)
300
- else
301
- response = http.__send__(method, path, data, header)
302
- end
303
- if response.code == "401" && @on_auth_fail && @on_auth_fail.call()
304
- next
305
- end
306
- if !(response.code =~ /^2/)
307
- raise(
308
- response.code == "401" ? AuthenticationError : GoogleSpreadsheet::Error,
309
- "Response code #{response.code} for #{method} #{url}: " +
310
- CGI.unescapeHTML(response.body))
311
- end
312
- return convert_response(response, response_type)
303
+ path = uri.path + (uri.query ? "?#{uri.query}" : "")
304
+ header = auth_header(auth).merge(extra_header)
305
+ if method == :delete || method == :get
306
+ return http.__send__(method, path, header)
307
+ else
308
+ return http.__send__(method, path, data, header)
313
309
  end
314
310
  end
315
-
316
311
  end
317
312
  end
318
313
 
319
- private
320
-
321
314
  def convert_response(response, response_type)
322
315
  case response_type
323
316
  when :xml
324
- return Hpricot.XML(response.body)
317
+ return Nokogiri.XML(response.body)
325
318
  when :raw
326
319
  return response.body
327
320
  else
@@ -337,9 +330,10 @@ module GoogleSpreadsheet
337
330
  "service" => auth.to_s(),
338
331
  "source" => "Gimite-RubyGoogleSpreadsheet-1.00",
339
332
  }
333
+ header = {"Content-Type" => "application/x-www-form-urlencoded"}
340
334
  response = request(:post,
341
335
  "https://www.google.com/accounts/ClientLogin",
342
- :data => encode_query(params), :auth => :none, :header => {}, :response_type => :raw)
336
+ :data => encode_query(params), :auth => :none, :header => header, :response_type => :raw)
343
337
  @auth_tokens[auth] = response.slice(/^Auth=(.*)$/, 1)
344
338
  end
345
339
 
@@ -378,12 +372,12 @@ module GoogleSpreadsheet
378
372
  def tables_feed_url
379
373
  return "https://spreadsheets.google.com/feeds/#{self.key}/tables"
380
374
  end
381
-
375
+
382
376
  # URL of feed used in document list feed API.
383
377
  def document_feed_url
384
378
  return "https://docs.google.com/feeds/documents/private/full/spreadsheet%3A#{self.key}"
385
379
  end
386
-
380
+
387
381
  # Creates copy of this spreadsheet with the given name.
388
382
  def duplicate(new_name = nil)
389
383
  new_name ||= (@title ? "Copy of " + @title : "Untitled")
@@ -396,8 +390,8 @@ module GoogleSpreadsheet
396
390
  "Slug" => URI.encode(new_name),
397
391
  }
398
392
  doc = @session.request(:post, url, :data => ods, :auth => :writely, :header => header)
399
- ss_url = as_utf8(doc.search(
400
- "link[@rel='http://schemas.google.com/spreadsheets/2006#worksheetsfeed']")[0]["href"])
393
+ ss_url = doc.css(
394
+ "link[@rel='http://schemas.google.com/spreadsheets/2006#worksheetsfeed']").first['href']
401
395
  return Spreadsheet.new(@session, ss_url, title)
402
396
  end
403
397
 
@@ -409,14 +403,32 @@ module GoogleSpreadsheet
409
403
  :auth => :writely, :header => {"If-Match" => "*"})
410
404
  end
411
405
 
406
+ # Renames title of the spreadsheet.
407
+ def rename(title)
408
+ doc = @session.request(:get, self.document_feed_url)
409
+ edit_url = doc.css("link[@rel='edit']").first['href']
410
+ xml = <<-"EOS"
411
+ <atom:entry
412
+ xmlns:atom="http://www.w3.org/2005/Atom"
413
+ xmlns:docs="http://schemas.google.com/docs/2007">
414
+ <atom:category
415
+ scheme="http://schemas.google.com/g/2005#kind"
416
+ term="http://schemas.google.com/docs/2007#spreadsheet" label="spreadsheet"/>
417
+ <atom:title>#{h(title)}</atom:title>
418
+ </atom:entry>
419
+ EOS
420
+
421
+ @session.request(:put, edit_url, :data => xml)
422
+ end
423
+
412
424
  # Returns worksheets of the spreadsheet as array of GoogleSpreadsheet::Worksheet.
413
425
  def worksheets
414
426
  doc = @session.request(:get, @worksheets_feed_url)
415
427
  result = []
416
- for entry in doc.search("entry")
417
- title = as_utf8(entry.search("title").text)
418
- url = as_utf8(entry.search(
419
- "link[@rel='http://schemas.google.com/spreadsheets/2006#cellsfeed']")[0]["href"])
428
+ doc.css('entry').each() do |entry|
429
+ title = entry.css('title').text
430
+ url = entry.css(
431
+ "link[@rel='http://schemas.google.com/spreadsheets/2006#cellsfeed']").first['href']
420
432
  result.push(Worksheet.new(@session, self, url, title))
421
433
  end
422
434
  return result.freeze()
@@ -433,15 +445,15 @@ module GoogleSpreadsheet
433
445
  </entry>
434
446
  EOS
435
447
  doc = @session.request(:post, @worksheets_feed_url, :data => xml)
436
- url = as_utf8(doc.search(
437
- "link[@rel='http://schemas.google.com/spreadsheets/2006#cellsfeed']")[0]["href"])
448
+ url = doc.css(
449
+ "link[@rel='http://schemas.google.com/spreadsheets/2006#cellsfeed']").first['href']
438
450
  return Worksheet.new(@session, self, url, title)
439
451
  end
440
452
 
441
453
  # Returns list of tables in the spreadsheet.
442
454
  def tables
443
455
  doc = @session.request(:get, self.tables_feed_url)
444
- return doc.search("entry").map(){ |e| Table.new(@session, e) }.freeze()
456
+ return doc.css('entry').map(){ |e| Table.new(@session, e) }.freeze()
445
457
  end
446
458
 
447
459
  end
@@ -454,8 +466,9 @@ module GoogleSpreadsheet
454
466
 
455
467
  def initialize(session, entry) #:nodoc:
456
468
  @columns = {}
457
- @worksheet_title = as_utf8(entry.search("gs:worksheet")[0]["name"])
458
- @records_url = as_utf8(entry.search("content")[0]["src"])
469
+ @worksheet_title = entry.css('gs|worksheet').first['name']
470
+ @records_url = entry.css("content")[0]["src"]
471
+ @edit_url = entry.css("link[@rel='edit']")[0]['href']
459
472
  @session = session
460
473
  end
461
474
 
@@ -465,7 +478,7 @@ module GoogleSpreadsheet
465
478
  # Adds a record.
466
479
  def add_record(values)
467
480
  fields = ""
468
- values.each do |name, value|
481
+ values.each() do |name, value|
469
482
  fields += "<gs:field name='#{h(name)}'>#{h(value)}</gs:field>"
470
483
  end
471
484
  xml =<<-EOS
@@ -481,18 +494,24 @@ module GoogleSpreadsheet
481
494
  # Returns records in the table.
482
495
  def records
483
496
  doc = @session.request(:get, @records_url)
484
- return doc.search("entry").map(){ |e| Record.new(@session, e) }
497
+ return doc.css('entry').map(){ |e| Record.new(@session, e) }
498
+ end
499
+
500
+ # Deletes this table. Deletion takes effect right away without calling save().
501
+ def delete
502
+ @session.request(:delete, @edit_url, :header => {"If-Match" => "*"})
485
503
  end
486
504
 
487
505
  end
488
506
 
489
507
  # Use GoogleSpreadsheet::Table#records to get GoogleSpreadsheet::Record objects.
490
508
  class Record < Hash
509
+ include(Util)
491
510
 
492
511
  def initialize(session, entry) #:nodoc:
493
512
  @session = session
494
- for field in entry.search("gs:field")
495
- self[as_utf8(field["name"])] = as_utf8(field.inner_text)
513
+ entry.css('gs|field').each() do |field|
514
+ self[field["name"]] = field.inner_text
496
515
  end
497
516
  end
498
517
 
@@ -652,18 +671,19 @@ module GoogleSpreadsheet
652
671
  # Note that changes you made by []= is discarded if you haven't called save().
653
672
  def reload()
654
673
  doc = @session.request(:get, @cells_feed_url)
655
- @max_rows = doc.search("gs:rowCount").text.to_i()
656
- @max_cols = doc.search("gs:colCount").text.to_i()
657
- @title = as_utf8(doc.search("/feed/title").text)
674
+ @max_rows = doc.css('gs|rowCount').text.to_i
675
+ @max_cols = doc.css('gs|colCount').text.to_i
676
+ @title = doc.css('feed > title')[0].text
658
677
 
659
678
  @cells = {}
660
679
  @input_values = {}
661
- for entry in doc.search("entry")
662
- cell = entry.search("gs:cell")[0]
680
+ doc.css('feed > entry').each() do |entry|
681
+ cell = entry.css('gs|cell').first
663
682
  row = cell["row"].to_i()
664
683
  col = cell["col"].to_i()
665
- @cells[[row, col]] = as_utf8(cell.inner_text)
666
- @input_values[[row, col]] = as_utf8(cell["inputValue"])
684
+ @cells[[row, col]] = cell.inner_text
685
+ @input_values[[row, col]] = cell["inputValue"]
686
+
667
687
  end
668
688
  @modified.clear()
669
689
  @meta_modified = false
@@ -677,7 +697,7 @@ module GoogleSpreadsheet
677
697
  if @meta_modified
678
698
 
679
699
  ws_doc = @session.request(:get, self.worksheet_feed_url)
680
- edit_url = ws_doc.search("link[@rel='edit']")[0]["href"]
700
+ edit_url = ws_doc.css("link[@rel='edit']").first['href']
681
701
  xml = <<-"EOS"
682
702
  <entry xmlns='http://www.w3.org/2005/Atom'
683
703
  xmlns:gs='http://schemas.google.com/spreadsheets/2006'>
@@ -704,9 +724,10 @@ module GoogleSpreadsheet
704
724
  url = "#{@cells_feed_url}?return-empty=true&min-row=#{rows.min}&max-row=#{rows.max}" +
705
725
  "&min-col=#{cols.min}&max-col=#{cols.max}"
706
726
  doc = @session.request(:get, url)
707
- for entry in doc.search("entry")
708
- row = entry.search("gs:cell")[0]["row"].to_i()
709
- col = entry.search("gs:cell")[0]["col"].to_i()
727
+
728
+ doc.css('entry').each() do |entry|
729
+ row = entry.css('gs|cell').first['row'].to_i
730
+ col = entry.css('gs|cell').first['col'].to_i
710
731
  cell_entries[[row, col]] = entry
711
732
  end
712
733
 
@@ -723,8 +744,8 @@ module GoogleSpreadsheet
723
744
  for row, col in chunk
724
745
  value = @cells[[row, col]]
725
746
  entry = cell_entries[[row, col]]
726
- id = entry.search("id").text
727
- edit_url = entry.search("link[@rel='edit']")[0]["href"]
747
+ id = entry.css('id').text
748
+ edit_url = entry.css("link[@rel='edit']").first['href']
728
749
  xml << <<-EOS
729
750
  <entry>
730
751
  <batch:id>#{h(row)},#{h(col)}</batch:id>
@@ -741,15 +762,15 @@ module GoogleSpreadsheet
741
762
  EOS
742
763
 
743
764
  result = @session.request(:post, "#{@cells_feed_url}/batch", :data => xml)
744
- for entry in result.search("atom:entry")
745
- interrupted = entry.search("batch:interrupted")[0]
765
+ result.css('atom|entry').each() do |entry|
766
+ interrupted = entry.css('batch|interrupted').first
746
767
  if interrupted
747
768
  raise(GoogleSpreadsheet::Error, "Update has failed: %s" %
748
769
  interrupted["reason"])
749
770
  end
750
- if !(entry.search("batch:status")[0]["code"] =~ /^2/)
771
+ if !(entry.css('batch|status').first['code'] =~ /^2/)
751
772
  raise(GoogleSpreadsheet::Error, "Updating cell %s has failed: %s" %
752
- [entry.search("atom:id").text, entry.search("batch:status")[0]["reason"]])
773
+ [entry.css('atom|id').text, entry.css('batch|status').first['reason']])
753
774
  end
754
775
  end
755
776
 
@@ -771,7 +792,7 @@ module GoogleSpreadsheet
771
792
  # Deletes this worksheet. Deletion takes effect right away without calling save().
772
793
  def delete()
773
794
  ws_doc = @session.request(:get, self.worksheet_feed_url)
774
- edit_url = ws_doc.search("link[@rel='edit']")[0]["href"]
795
+ edit_url = ws_doc.css("link[@rel='edit']").first['href']
775
796
  @session.request(:delete, edit_url)
776
797
  end
777
798
 
@@ -783,9 +804,12 @@ module GoogleSpreadsheet
783
804
  # Creates table for the worksheet and returns GoogleSpreadsheet::Table.
784
805
  # See this document for details:
785
806
  # http://code.google.com/intl/en/apis/spreadsheets/docs/3.0/developers_guide_protocol.html#TableFeeds
786
- def add_table(table_title, summary, columns)
807
+ def add_table(table_title, summary, columns, options)
808
+ default_options = { :header_row => 1, :num_rows => 0, :start_row => 2}
809
+ options = default_options.merge(options)
810
+
787
811
  column_xml = ""
788
- columns.each do |index, name|
812
+ columns.each() do |index, name|
789
813
  column_xml += "<gs:column index='#{h(index)}' name='#{h(name)}'/>\n"
790
814
  end
791
815
 
@@ -795,8 +819,8 @@ module GoogleSpreadsheet
795
819
  <title type='text'>#{h(table_title)}</title>
796
820
  <summary type='text'>#{h(summary)}</summary>
797
821
  <gs:worksheet name='#{h(self.title)}' />
798
- <gs:header row='1' />
799
- <gs:data numRows='0' startRow='2'>
822
+ <gs:header row='#{options[:header_row]}' />
823
+ <gs:data numRows='#{options[:num_rows]}' startRow='#{options[:start_row]}'>
800
824
  #{column_xml}
801
825
  </gs:data>
802
826
  </entry>
@@ -811,6 +835,16 @@ module GoogleSpreadsheet
811
835
  return self.spreadsheet.tables.select(){ |t| t.worksheet_title == self.title }
812
836
  end
813
837
 
838
+ # List feed URL of the worksheet.
839
+ def list_feed_url
840
+ # Gets the worksheets metafeed.
841
+ entry = @session.request(:get, self.worksheet_feed_url)
842
+
843
+ # Gets the URL of list-based feed for the given spreadsheet.
844
+ return entry.css(
845
+ "link[@rel='http://schemas.google.com/spreadsheets/2006#listfeed']").first['href']
846
+ end
847
+
814
848
  end
815
849
 
816
850
 
metadata CHANGED
@@ -1,7 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: google-spreadsheet-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ hash: 31
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 2
10
+ version: 0.1.2
5
11
  platform: ruby
6
12
  authors:
7
13
  - Hiroshi Ichikawa
@@ -9,29 +15,42 @@ autorequire:
9
15
  bindir: bin
10
16
  cert_chain: []
11
17
 
12
- date: 2010-01-31 00:00:00 +09:00
18
+ date: 2010-09-24 00:00:00 +09:00
13
19
  default_executable:
14
20
  dependencies:
15
21
  - !ruby/object:Gem::Dependency
16
- name: hpricot
17
- type: :runtime
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
22
+ name: nokogiri
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
20
26
  requirements:
21
27
  - - ">="
22
28
  - !ruby/object:Gem::Version
23
- version: "0.3"
24
- version:
29
+ hash: 113
30
+ segments:
31
+ - 1
32
+ - 4
33
+ - 3
34
+ - 1
35
+ version: 1.4.3.1
36
+ type: :runtime
37
+ version_requirements: *id001
25
38
  - !ruby/object:Gem::Dependency
26
39
  name: oauth
27
- type: :runtime
28
- version_requirement:
29
- version_requirements: !ruby/object:Gem::Requirement
40
+ prerelease: false
41
+ requirement: &id002 !ruby/object:Gem::Requirement
42
+ none: false
30
43
  requirements:
31
44
  - - ">="
32
45
  - !ruby/object:Gem::Version
46
+ hash: 31
47
+ segments:
48
+ - 0
49
+ - 3
50
+ - 6
33
51
  version: 0.3.6
34
- version:
52
+ type: :runtime
53
+ version_requirements: *id002
35
54
  description: This is a library to read/write Google Spreadsheet.
36
55
  email:
37
56
  - gimite+github@gmail.com
@@ -55,21 +74,27 @@ rdoc_options:
55
74
  require_paths:
56
75
  - lib
57
76
  required_ruby_version: !ruby/object:Gem::Requirement
77
+ none: false
58
78
  requirements:
59
79
  - - ">="
60
80
  - !ruby/object:Gem::Version
81
+ hash: 3
82
+ segments:
83
+ - 0
61
84
  version: "0"
62
- version:
63
85
  required_rubygems_version: !ruby/object:Gem::Requirement
86
+ none: false
64
87
  requirements:
65
88
  - - ">="
66
89
  - !ruby/object:Gem::Version
90
+ hash: 3
91
+ segments:
92
+ - 0
67
93
  version: "0"
68
- version:
69
94
  requirements: []
70
95
 
71
96
  rubyforge_project:
72
- rubygems_version: 1.3.5
97
+ rubygems_version: 1.3.7
73
98
  signing_key:
74
99
  specification_version: 2
75
100
  summary: This is a library to read/write Google Spreadsheet.