google-spreadsheet-ruby 0.1.8 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -16,16 +16,19 @@ Example:
16
16
  require "google_spreadsheet"
17
17
 
18
18
  # Logs in.
19
- # You can also use OAuth. See document of GoogleSpreadsheet.login_with_oauth for details.
19
+ # You can also use OAuth. See document of
20
+ # GoogleSpreadsheet.login_with_oauth for details.
20
21
  session = GoogleSpreadsheet.login("username@gmail.com", "mypassword")
21
22
 
22
- # First worksheet of http://spreadsheets.google.com/ccc?key=pz7XtlQC-PYx-jrVMJErTcg&hl=en
23
+ # First worksheet of
24
+ # https://docs.google.com/spreadsheet/ccc?key=pz7XtlQC-PYx-jrVMJErTcg
23
25
  ws = session.spreadsheet_by_key("pz7XtlQC-PYx-jrVMJErTcg").worksheets[0]
24
26
 
25
27
  # Gets content of A2 cell.
26
28
  p ws[2, 1] #==> "hoge"
27
29
 
28
- # Changes content of cells. Changes are not sent to the server until you call ws.save().
30
+ # Changes content of cells.
31
+ # Changes are not sent to the server until you call ws.save().
29
32
  ws[2, 1] = "foo"
30
33
  ws[2, 2] = "bar"
31
34
  ws.save()
@@ -0,0 +1,20 @@
1
+ module GoogleSpreadsheet
2
+
3
+ class Acl
4
+
5
+ # Returns the number of entries.
6
+ def size
7
+ end
8
+
9
+ # Returns GoogleSpreadsheet::AclEntry object at +index+.
10
+ def [](index)
11
+ end
12
+
13
+ # Iterates over GoogleSpreadsheet::AclEntry objects.
14
+ def each(&block)
15
+ yield(entry)
16
+ end
17
+
18
+ end
19
+
20
+ end
@@ -0,0 +1,33 @@
1
+ module GoogleSpreadsheet
2
+
3
+ class AclEntry
4
+
5
+ # Type of the scope. One of:
6
+ #
7
+ # - "user": scope is a user's email address.
8
+ # - "group": scope is a Google Group email address.
9
+ # - "domain": scope is a Google Apps domain.
10
+ # - "default": Publicly shared with all users. scope is +nil+.
11
+ attr_reader(:scope_type)
12
+
13
+ # The scope. See scope_type.
14
+ attr_reader(:scope)
15
+
16
+ # The role given to the scope. One of:
17
+ # - "owner": The owner.
18
+ # - "writer": With read/write access.
19
+ # - "reader": With read-only access.
20
+ attr_reader(:role)
21
+
22
+ # Title of the entry.
23
+ attr_reader(:title)
24
+
25
+ # Edit URL of the entry.
26
+ attr_reader(:edit_url)
27
+
28
+ # E-tag of the entry.
29
+ attr_reader(:etag)
30
+
31
+ end
32
+
33
+ end
@@ -1,17 +1,8 @@
1
1
  # Author: Hiroshi Ichikawa <http://gimite.net/>
2
2
  # The license of this source is "New BSD Licence"
3
3
 
4
- require "enumerator"
5
- require "set"
6
- require "net/https"
7
- require "open-uri"
8
- require "cgi"
9
- require "uri"
10
- require "rubygems"
11
- require "nokogiri"
12
- require "oauth"
13
- require "oauth2"
14
- Net::HTTP.version_1_2
4
+ require "google_spreadsheet/session"
5
+
15
6
 
16
7
  module GoogleSpreadsheet
17
8
 
@@ -53,15 +44,17 @@ module GoogleSpreadsheet
53
44
  #
54
45
  # OAuth1 code example:
55
46
  #
56
- # 1) First generate OAuth consumer object with key and secret for your site by registering site with google
47
+ # 1) First generate OAuth consumer object with key and secret for your site by registering site
48
+ # with Google.
57
49
  # @consumer = OAuth::Consumer.new( "key","secret", {:site=>"https://agree2"})
58
- # 2) Request token with OAuth
50
+ # 2) Request token with OAuth.
59
51
  # @request_token = @consumer.get_request_token
60
52
  # session[:request_token] = @request_token
61
53
  # redirect_to @request_token.authorize_url
62
- # 3) Create an oauth access token
54
+ # 3) Create an oauth access token.
63
55
  # @oauth_access_token = @request_token.get_access_token
64
- # @access_token = OAuth::AccessToken.new(@consumer, @oauth_access_token.token, @oauth_access_token.secret)
56
+ # @access_token = OAuth::AccessToken.new(
57
+ # @consumer, @oauth_access_token.token, @oauth_access_token.secret)
65
58
  #
66
59
  # See these documents for details:
67
60
  #
@@ -123,1227 +116,5 @@ module GoogleSpreadsheet
123
116
  end
124
117
  return session
125
118
  end
126
-
127
-
128
- module Util #:nodoc:
129
-
130
- module_function
131
-
132
- def encode_query(params)
133
- return params.map(){ |k, v| CGI.escape(k) + "=" + CGI.escape(v) }.join("&")
134
- end
135
-
136
- def concat_url(url, piece)
137
- (url_base, url_query) = url.split(/\?/, 2)
138
- (piece_base, piece_query) = piece.split(/\?/, 2)
139
- result_query = [url_query, piece_query].select(){ |s| s && !s.empty? }.join("&")
140
- return url_base + piece_base + (result_query.empty? ? "" : "?#{result_query}")
141
- end
142
-
143
- def h(str)
144
- return CGI.escapeHTML(str.to_s())
145
- end
146
-
147
- end
148
-
149
-
150
- # Raised when spreadsheets.google.com has returned error.
151
- class Error < RuntimeError
152
-
153
- end
154
-
155
-
156
- # Raised when GoogleSpreadsheet.login has failed.
157
- class AuthenticationError < GoogleSpreadsheet::Error
158
-
159
- end
160
-
161
-
162
- class ClientLoginFetcher #:nodoc:
163
-
164
- def initialize(auth_tokens, proxy)
165
- @auth_tokens = auth_tokens
166
- if proxy
167
- @proxy = proxy
168
- elsif ENV["http_proxy"] && !ENV["http_proxy"].empty?
169
- proxy_url = URI.parse(ENV["http_proxy"])
170
- @proxy = Net::HTTP.Proxy(proxy_url.host, proxy_url.port)
171
- else
172
- @proxy = Net::HTTP
173
- end
174
- end
175
-
176
- attr_accessor(:auth_tokens)
177
-
178
- def request_raw(method, url, data, extra_header, auth)
179
- uri = URI.parse(url)
180
- http = @proxy.new(uri.host, uri.port)
181
- http.use_ssl = true
182
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
183
- http.start() do
184
- path = uri.path + (uri.query ? "?#{uri.query}" : "")
185
- header = auth_header(auth).merge(extra_header)
186
- if method == :delete || method == :get
187
- return http.__send__(method, path, header)
188
- else
189
- return http.__send__(method, path, data, header)
190
- end
191
- end
192
- end
193
-
194
- private
195
-
196
- def auth_header(auth)
197
- token = auth == :none ? nil : @auth_tokens[auth]
198
- if token
199
- return {"Authorization" => "GoogleLogin auth=#{token}"}
200
- else
201
- return {}
202
- end
203
- end
204
-
205
- end
206
-
207
-
208
- class OAuth1Fetcher #:nodoc:
209
-
210
- def initialize(oauth1_token)
211
- @oauth1_token = oauth1_token
212
- end
213
-
214
- def request_raw(method, url, data, extra_header, auth)
215
- if method == :delete || method == :get
216
- return @oauth1_token.__send__(method, url, extra_header)
217
- else
218
- return @oauth1_token.__send__(method, url, data, extra_header)
219
- end
220
- end
221
-
222
- end
223
-
224
-
225
- class OAuth2Fetcher #:nodoc:
226
-
227
- Response = Struct.new(:code, :body)
228
-
229
- def initialize(oauth2_token)
230
- @oauth2_token = oauth2_token
231
- end
232
-
233
- def request_raw(method, url, data, extra_header, auth)
234
- if method == :delete || method == :get
235
- raw_res = @oauth2_token.request(method, url, {:header => extra_header})
236
- else
237
- raw_res = @oauth2_token.request(method, url, {:header => extra_header, :body => data})
238
- end
239
- return Response.new(raw_res.status.to_s(), raw_res.body)
240
- end
241
-
242
- end
243
-
244
-
245
- # Use GoogleSpreadsheet.login or GoogleSpreadsheet.saved_session to get
246
- # GoogleSpreadsheet::Session object.
247
- class Session
248
-
249
- include(Util)
250
- extend(Util)
251
-
252
- # The same as GoogleSpreadsheet.login.
253
- def self.login(mail, password, proxy = nil)
254
- session = Session.new(nil, ClientLoginFetcher.new({}, proxy))
255
- session.login(mail, password)
256
- return session
257
- end
258
-
259
- # The same as GoogleSpreadsheet.login_with_oauth.
260
- def self.login_with_oauth(oauth_token)
261
- case oauth_token
262
- when OAuth::AccessToken
263
- fetcher = OAuth1Fetcher.new(oauth_token)
264
- when OAuth2::AccessToken
265
- fetcher = OAuth2Fetcher.new(oauth_token)
266
- else
267
- raise(GoogleSpreadsheet::Error,
268
- "oauth_token is neither OAuth::Token nor OAuth2::Token: %p" % oauth_token)
269
- end
270
- return Session.new(nil, fetcher)
271
- end
272
-
273
- # The same as GoogleSpreadsheet.restore_session.
274
- def self.restore_session(auth_tokens, proxy = nil)
275
- return Session.new(auth_tokens, nil, proxy)
276
- end
277
-
278
- # DEPRECATED: Use GoogleSpreadsheet.restore_session instead.
279
- def initialize(auth_tokens = nil, fetcher = nil, proxy = nil)
280
- if fetcher
281
- @fetcher = fetcher
282
- else
283
- @fetcher = ClientLoginFetcher.new(auth_tokens || {}, proxy)
284
- end
285
- end
286
-
287
- # Authenticates with given +mail+ and +password+, and updates current session object
288
- # if succeeds. Raises GoogleSpreadsheet::AuthenticationError if fails.
289
- # Google Apps account is supported.
290
- def login(mail, password)
291
- if !@fetcher.is_a?(ClientLoginFetcher)
292
- raise(GoogleSpreadsheet::Error,
293
- "Cannot call login for session created by login_with_oauth.")
294
- end
295
- begin
296
- @fetcher.auth_tokens = {
297
- :wise => authenticate(mail, password, :wise),
298
- :writely => authenticate(mail, password, :writely),
299
- }
300
- rescue GoogleSpreadsheet::Error => ex
301
- return true if @on_auth_fail && @on_auth_fail.call()
302
- raise(AuthenticationError, "authentication failed for #{mail}: #{ex.message}")
303
- end
304
- end
305
-
306
- # Authentication tokens.
307
- def auth_tokens
308
- if !@fetcher.is_a?(ClientLoginFetcher)
309
- raise(GoogleSpreadsheet::Error,
310
- "Cannot call auth_tokens for session created by " +
311
- "login_with_oauth.")
312
- end
313
- return @fetcher.auth_tokens
314
- end
315
-
316
- # Authentication token.
317
- def auth_token(auth = :wise)
318
- return self.auth_tokens[auth]
319
- end
320
-
321
- # Proc or Method called when authentication has failed.
322
- # When this function returns +true+, it tries again.
323
- attr_accessor :on_auth_fail
324
-
325
- # Returns list of spreadsheets for the user as array of GoogleSpreadsheet::Spreadsheet.
326
- # You can specify query parameters described at
327
- # http://code.google.com/apis/spreadsheets/docs/2.0/reference.html#Parameters
328
- #
329
- # e.g.
330
- # session.spreadsheets
331
- # session.spreadsheets("title" => "hoge")
332
- def spreadsheets(params = {})
333
- query = encode_query(params)
334
- doc = request(:get, "https://spreadsheets.google.com/feeds/spreadsheets/private/full?#{query}")
335
- result = []
336
- doc.css("feed > entry").each() do |entry|
337
- title = entry.css("title").text
338
- url = entry.css(
339
- "link[@rel='http://schemas.google.com/spreadsheets/2006#worksheetsfeed']")[0]["href"]
340
- result.push(Spreadsheet.new(self, url, title))
341
- end
342
- return result
343
- end
344
-
345
- # Returns GoogleSpreadsheet::Spreadsheet with given +key+.
346
- #
347
- # e.g.
348
- # # http://spreadsheets.google.com/ccc?key=pz7XtlQC-PYx-jrVMJErTcg&hl=ja
349
- # session.spreadsheet_by_key("pz7XtlQC-PYx-jrVMJErTcg")
350
- def spreadsheet_by_key(key)
351
- url = "https://spreadsheets.google.com/feeds/worksheets/#{key}/private/full"
352
- return Spreadsheet.new(self, url)
353
- end
354
-
355
- # Returns GoogleSpreadsheet::Spreadsheet with given +url+. You must specify either of:
356
- # - URL of the page you open to access the spreadsheet in your browser
357
- # - URL of worksheet-based feed of the spreadseet
358
- #
359
- # e.g.
360
- # session.spreadsheet_by_url(
361
- # "http://spreadsheets.google.com/ccc?key=pz7XtlQC-PYx-jrVMJErTcg&hl=en")
362
- # session.spreadsheet_by_url(
363
- # "https://spreadsheets.google.com/feeds/worksheets/pz7XtlQC-PYx-jrVMJErTcg/private/full")
364
- def spreadsheet_by_url(url)
365
- # Tries to parse it as URL of human-readable spreadsheet.
366
- uri = URI.parse(url)
367
- if uri.host == "spreadsheets.google.com" && uri.path =~ /\/ccc$/
368
- if (uri.query || "").split(/&/).find(){ |s| s=~ /^key=(.*)$/ }
369
- return spreadsheet_by_key($1)
370
- end
371
- end
372
- # Assumes the URL is worksheets feed URL.
373
- return Spreadsheet.new(self, url)
374
- end
375
-
376
- # Returns GoogleSpreadsheet::Worksheet with given +url+.
377
- # You must specify URL of cell-based feed of the worksheet.
378
- #
379
- # e.g.
380
- # session.worksheet_by_url(
381
- # "http://spreadsheets.google.com/feeds/cells/pz7XtlQC-PYxNmbBVgyiNWg/od6/private/full")
382
- def worksheet_by_url(url)
383
- return Worksheet.new(self, nil, url)
384
- end
385
-
386
- # Returns GoogleSpreadsheet::Collection with given +url+.
387
- # You must specify URL of collection (folder) feed.
388
- #
389
- # e.g.
390
- # session.collection_by_url(
391
- # "http://docs.google.com/feeds/default/private/full/folder%3A" +
392
- # "0B9GfDpQ2pBVUODNmOGE0NjIzMWU3ZC00NmUyLTk5NzEtYaFkZjY1MjAyxjMc")
393
- def collection_by_url(url)
394
- return Collection.new(self, url)
395
- end
396
-
397
- # Creates new spreadsheet and returns the new GoogleSpreadsheet::Spreadsheet.
398
- #
399
- # e.g.
400
- # session.create_spreadsheet("My new sheet")
401
- def create_spreadsheet(
402
- title = "Untitled",
403
- feed_url = "https://docs.google.com/feeds/documents/private/full")
404
- xml = <<-"EOS"
405
- <atom:entry xmlns:atom="http://www.w3.org/2005/Atom" xmlns:docs="http://schemas.google.com/docs/2007">
406
- <atom:category scheme="http://schemas.google.com/g/2005#kind"
407
- term="http://schemas.google.com/docs/2007#spreadsheet" label="spreadsheet"/>
408
- <atom:title>#{h(title)}</atom:title>
409
- </atom:entry>
410
- EOS
411
-
412
- doc = request(:post, feed_url, :data => xml, :auth => :writely)
413
- ss_url = doc.css(
414
- "link[@rel='http://schemas.google.com/spreadsheets/2006#worksheetsfeed']").first['href']
415
- return Spreadsheet.new(self, ss_url, title)
416
- end
417
-
418
- def request(method, url, params = {}) #:nodoc:
419
- # Always uses HTTPS.
420
- url = url.gsub(%r{^http://}, "https://")
421
- data = params[:data]
422
- auth = params[:auth] || :wise
423
- if params[:header]
424
- extra_header = params[:header]
425
- elsif data
426
- extra_header = {"Content-Type" => "application/atom+xml"}
427
- else
428
- extra_header = {}
429
- end
430
- response_type = params[:response_type] || :xml
431
-
432
- while true
433
- response = @fetcher.request_raw(method, url, data, extra_header, auth)
434
- if response.code == "401" && @on_auth_fail && @on_auth_fail.call()
435
- next
436
- end
437
- if !(response.code =~ /^2/)
438
- raise(
439
- response.code == "401" ? AuthenticationError : GoogleSpreadsheet::Error,
440
- "Response code #{response.code} for #{method} #{url}: " +
441
- CGI.unescapeHTML(response.body))
442
- end
443
- return convert_response(response, response_type)
444
- end
445
- end
446
-
447
- def inspect
448
- return '#<%p:0x%x>' % [self.class, self.object_id]
449
- end
450
-
451
- private
452
-
453
- def convert_response(response, response_type)
454
- case response_type
455
- when :xml
456
- return Nokogiri.XML(response.body)
457
- when :raw
458
- return response.body
459
- else
460
- raise("unknown params[:response_type]: %s" % response_type)
461
- end
462
- end
463
-
464
- def authenticate(mail, password, auth)
465
- params = {
466
- "accountType" => "HOSTED_OR_GOOGLE",
467
- "Email" => mail,
468
- "Passwd" => password,
469
- "service" => auth.to_s(),
470
- "source" => "Gimite-RubyGoogleSpreadsheet-1.00",
471
- }
472
- header = {"Content-Type" => "application/x-www-form-urlencoded"}
473
- response = request(:post,
474
- "https://www.google.com/accounts/ClientLogin",
475
- :data => encode_query(params), :auth => :none, :header => header, :response_type => :raw)
476
- return response.slice(/^Auth=(.*)$/, 1)
477
- end
478
-
479
- end
480
-
481
-
482
- # Use methods in GoogleSpreadsheet::Session to get GoogleSpreadsheet::Spreadsheet object.
483
- class Spreadsheet
484
-
485
- include(Util)
486
-
487
- SUPPORTED_EXPORT_FORMAT = Set.new(["xls", "csv", "pdf", "ods", "tsv", "html"])
488
-
489
- def initialize(session, worksheets_feed_url, title = nil) #:nodoc:
490
- @session = session
491
- @worksheets_feed_url = worksheets_feed_url
492
- @title = title
493
- end
494
-
495
- # URL of worksheet-based feed of the spreadsheet.
496
- attr_reader(:worksheets_feed_url)
497
-
498
- # Title of the spreadsheet.
499
- #
500
- # Set params[:reload] to true to force reloading the title.
501
- def title(params = {})
502
- if !@title || params[:reload]
503
- @title = spreadsheet_feed_entry(params).css("title").text
504
- end
505
- return @title
506
- end
507
-
508
- # Key of the spreadsheet.
509
- def key
510
- if !(@worksheets_feed_url =~
511
- %r{^https?://spreadsheets.google.com/feeds/worksheets/(.*)/private/.*$})
512
- raise(GoogleSpreadsheet::Error,
513
- "worksheets feed URL is in unknown format: #{@worksheets_feed_url}")
514
- end
515
- return $1
516
- end
517
-
518
- # Spreadsheet feed URL of the spreadsheet.
519
- def spreadsheet_feed_url
520
- return "https://spreadsheets.google.com/feeds/spreadsheets/private/full/#{self.key}"
521
- end
522
-
523
- # URL which you can open the spreadsheet in a Web browser with.
524
- #
525
- # e.g. "http://spreadsheets.google.com/ccc?key=pz7XtlQC-PYx-jrVMJErTcg"
526
- def human_url
527
- # Uses Document feed because Spreadsheet feed returns wrong URL for Apps account.
528
- return self.document_feed_entry.css("link[@rel='alternate']").first["href"]
529
- end
530
-
531
- # DEPRECATED: Table and Record feeds are deprecated and they will not be available after
532
- # March 2012.
533
- #
534
- # Tables feed URL of the spreadsheet.
535
- def tables_feed_url
536
- warn(
537
- "DEPRECATED: Google Spreadsheet Table and Record feeds are deprecated and they will " +
538
- "not be available after March 2012.")
539
- return "https://spreadsheets.google.com/feeds/#{self.key}/tables"
540
- end
541
-
542
- # URL of feed used in document list feed API.
543
- def document_feed_url
544
- return "https://docs.google.com/feeds/documents/private/full/spreadsheet%3A#{self.key}"
545
- end
546
-
547
- # <entry> element of spreadsheet feed as Nokogiri::XML::Element.
548
- #
549
- # Set params[:reload] to true to force reloading the feed.
550
- def spreadsheet_feed_entry(params = {})
551
- if !@spreadsheet_feed_entry || params[:reload]
552
- @spreadsheet_feed_entry =
553
- @session.request(:get, self.spreadsheet_feed_url).css("entry").first
554
- end
555
- return @spreadsheet_feed_entry
556
- end
557
-
558
- # <entry> element of document list feed as Nokogiri::XML::Element.
559
- #
560
- # Set params[:reload] to true to force reloading the feed.
561
- def document_feed_entry(params = {})
562
- if !@document_feed_entry || params[:reload]
563
- @document_feed_entry =
564
- @session.request(:get, self.document_feed_url, :auth => :writely).css("entry").first
565
- end
566
- return @document_feed_entry
567
- end
568
-
569
- # Creates copy of this spreadsheet with the given title.
570
- def duplicate(new_title = nil)
571
- new_title ||= (self.title ? "Copy of " + self.title : "Untitled")
572
- post_url = "https://docs.google.com/feeds/default/private/full/"
573
- header = {"GData-Version" => "3.0", "Content-Type" => "application/atom+xml"}
574
- xml = <<-"EOS"
575
- <entry xmlns='http://www.w3.org/2005/Atom'>
576
- <id>#{h(self.document_feed_url)}</id>
577
- <title>#{h(new_title)}</title>
578
- </entry>
579
- EOS
580
- doc = @session.request(:post, post_url, :data => xml, :header => header, :auth => :writely)
581
- ss_url = doc.css(
582
- "link[@rel='http://schemas.google.com/spreadsheets/2006#worksheetsfeed']").first["href"]
583
- return Spreadsheet.new(@session, ss_url, new_title)
584
- end
585
-
586
- # If +permanent+ is +false+, moves the spreadsheet to the trash.
587
- # If +permanent+ is +true+, deletes the spreadsheet permanently.
588
- def delete(permanent = false)
589
- @session.request(:delete,
590
- self.document_feed_url + (permanent ? "?delete=true" : ""),
591
- :auth => :writely, :header => {"If-Match" => "*"})
592
- end
593
-
594
- # Renames title of the spreadsheet.
595
- def rename(title)
596
- doc = @session.request(:get, self.document_feed_url, :auth => :writely)
597
- edit_url = doc.css("link[@rel='edit']").first["href"]
598
- xml = <<-"EOS"
599
- <atom:entry
600
- xmlns:atom="http://www.w3.org/2005/Atom"
601
- xmlns:docs="http://schemas.google.com/docs/2007">
602
- <atom:category
603
- scheme="http://schemas.google.com/g/2005#kind"
604
- term="http://schemas.google.com/docs/2007#spreadsheet" label="spreadsheet"/>
605
- <atom:title>#{h(title)}</atom:title>
606
- </atom:entry>
607
- EOS
608
-
609
- @session.request(:put, edit_url, :data => xml, :auth => :writely)
610
- end
611
-
612
- alias title= rename
613
-
614
- # Exports the spreadsheet in +format+ and returns it as String.
615
- #
616
- # +format+ can be either "xls", "csv", "pdf", "ods", "tsv" or "html".
617
- # In format such as "csv", only the worksheet specified with +worksheet_index+ is exported.
618
- def export_as_string(format, worksheet_index = nil)
619
- gid_param = worksheet_index ? "&gid=#{worksheet_index}" : ""
620
- url =
621
- "https://spreadsheets.google.com/feeds/download/spreadsheets/Export" +
622
- "?key=#{key}&exportFormat=#{format}#{gid_param}"
623
- return @session.request(:get, url, :response_type => :raw)
624
- end
625
-
626
- # Exports the spreadsheet in +format+ as a local file.
627
- #
628
- # +format+ can be either "xls", "csv", "pdf", "ods", "tsv" or "html".
629
- # If +format+ is nil, it is guessed from the file name.
630
- # In format such as "csv", only the worksheet specified with +worksheet_index+ is exported.
631
- def export_as_file(local_path, format = nil, worksheet_index = nil)
632
- if !format
633
- format = File.extname(local_path).gsub(/^\./, "")
634
- if !SUPPORTED_EXPORT_FORMAT.include?(format)
635
- raise(ArgumentError,
636
- ("Cannot guess format from the file name: %s\n" +
637
- "Specify format argument explicitly") %
638
- local_path)
639
- end
640
- end
641
- open(local_path, "wb") do |f|
642
- f.write(export_as_string(format, worksheet_index))
643
- end
644
- end
645
-
646
- # Returns worksheets of the spreadsheet as array of GoogleSpreadsheet::Worksheet.
647
- def worksheets
648
- doc = @session.request(:get, @worksheets_feed_url)
649
- result = []
650
- doc.css('entry').each() do |entry|
651
- title = entry.css('title').text
652
- url = entry.css(
653
- "link[@rel='http://schemas.google.com/spreadsheets/2006#cellsfeed']").first['href']
654
- result.push(Worksheet.new(@session, self, url, title))
655
- end
656
- return result.freeze()
657
- end
658
-
659
- # Returns a GoogleSpreadsheet::Worksheet with the given title in the spreadsheet.
660
- #
661
- # Returns nil if not found. Returns the first one when multiple worksheets with the
662
- # title are found.
663
- def worksheet_by_title(title)
664
- return self.worksheets.find(){ |ws| ws.title == title }
665
- end
666
-
667
- # Adds a new worksheet to the spreadsheet. Returns added GoogleSpreadsheet::Worksheet.
668
- def add_worksheet(title, max_rows = 100, max_cols = 20)
669
- xml = <<-"EOS"
670
- <entry xmlns='http://www.w3.org/2005/Atom'
671
- xmlns:gs='http://schemas.google.com/spreadsheets/2006'>
672
- <title>#{h(title)}</title>
673
- <gs:rowCount>#{h(max_rows)}</gs:rowCount>
674
- <gs:colCount>#{h(max_cols)}</gs:colCount>
675
- </entry>
676
- EOS
677
- doc = @session.request(:post, @worksheets_feed_url, :data => xml)
678
- url = doc.css(
679
- "link[@rel='http://schemas.google.com/spreadsheets/2006#cellsfeed']").first['href']
680
- return Worksheet.new(@session, self, url, title)
681
- end
682
-
683
- # DEPRECATED: Table and Record feeds are deprecated and they will not be available after
684
- # March 2012.
685
- #
686
- # Returns list of tables in the spreadsheet.
687
- def tables
688
- warn(
689
- "DEPRECATED: Google Spreadsheet Table and Record feeds are deprecated and they will " +
690
- "not be available after March 2012.")
691
- doc = @session.request(:get, self.tables_feed_url)
692
- return doc.css('entry').map(){ |e| Table.new(@session, e) }.freeze()
693
- end
694
-
695
- def inspect
696
- fields = {:worksheets_feed_url => self.worksheets_feed_url}
697
- fields[:title] = @title if @title
698
- return '#<%p %s>' % [self.class, fields.map(){ |k, v| "%s=%p" % [k, v] }.join(", ")]
699
- end
700
-
701
- end
702
-
703
- # Use GoogleSpreadsheet::Session#collection_by_url to get GoogleSpreadsheet::Collection object.
704
- class Collection
705
-
706
- include(Util)
707
-
708
- def initialize(session, collection_feed_url) #:nodoc:
709
- @session = session
710
- @collection_feed_url = collection_feed_url
711
- end
712
-
713
- # Adds the given GoogleSpreadsheet::Spreadsheet to the collection.
714
- def add(spreadsheet)
715
- contents_url = concat_url(@collection_feed_url, "/contents")
716
- header = {"GData-Version" => "3.0", "Content-Type" => "application/atom+xml"}
717
- xml = <<-"EOS"
718
- <entry xmlns="http://www.w3.org/2005/Atom">
719
- <id>#{h(spreadsheet.document_feed_url)}</id>
720
- </entry>
721
- EOS
722
- @session.request(
723
- :post, contents_url, :data => xml, :header => header, :auth => :writely)
724
- return nil
725
- end
726
-
727
- # Returns all the spreadsheets in the collection.
728
- def spreadsheets
729
- contents_url = concat_url(@collection_feed_url, "/contents")
730
- header = {"GData-Version" => "3.0", "Content-Type" => "application/atom+xml"}
731
- doc = @session.request(:get, contents_url, :header => header, :auth => :writely)
732
-
733
- return doc.css("feed > entry").map() do |entry|
734
- title = entry.css("title").text
735
- url = entry.css(
736
- "link[@rel='http://schemas.google.com/spreadsheets/2006#worksheetsfeed']")[0]["href"]
737
- GoogleSpreadsheet::Spreadsheet.new(@session, url, title)
738
- end
739
- end
740
-
741
- # TODO Add other operations.
742
-
743
- end
744
-
745
- # DEPRECATED: Table and Record feeds are deprecated and they will not be available after
746
- # March 2012.
747
- #
748
- # Use GoogleSpreadsheet::Worksheet#add_table to create table.
749
- # Use GoogleSpreadsheet::Worksheet#tables to get GoogleSpreadsheet::Table objects.
750
- class Table
751
-
752
- include(Util)
753
-
754
- def initialize(session, entry) #:nodoc:
755
- @columns = {}
756
- @worksheet_title = entry.css('gs|worksheet').first['name']
757
- @records_url = entry.css("content")[0]["src"]
758
- @edit_url = entry.css("link[@rel='edit']")[0]['href']
759
- @session = session
760
- end
761
-
762
- # Title of the worksheet the table belongs to.
763
- attr_reader(:worksheet_title)
764
-
765
- # Adds a record.
766
- def add_record(values)
767
- fields = ""
768
- values.each() do |name, value|
769
- fields += "<gs:field name='#{h(name)}'>#{h(value)}</gs:field>"
770
- end
771
- xml =<<-EOS
772
- <entry
773
- xmlns="http://www.w3.org/2005/Atom"
774
- xmlns:gs="http://schemas.google.com/spreadsheets/2006">
775
- #{fields}
776
- </entry>
777
- EOS
778
- @session.request(:post, @records_url, :data => xml)
779
- end
780
-
781
- # Returns records in the table.
782
- def records
783
- doc = @session.request(:get, @records_url)
784
- return doc.css('entry').map(){ |e| Record.new(@session, e) }
785
- end
786
-
787
- # Deletes this table. Deletion takes effect right away without calling save().
788
- def delete
789
- @session.request(:delete, @edit_url, :header => {"If-Match" => "*"})
790
- end
791
-
792
- end
793
-
794
- # DEPRECATED: Table and Record feeds are deprecated and they will not be available after
795
- # March 2012.
796
- #
797
- # Use GoogleSpreadsheet::Table#records to get GoogleSpreadsheet::Record objects.
798
- class Record < Hash
799
- include(Util)
800
-
801
- def initialize(session, entry) #:nodoc:
802
- @session = session
803
- entry.css('gs|field').each() do |field|
804
- self[field["name"]] = field.inner_text
805
- end
806
- end
807
-
808
- def inspect #:nodoc:
809
- content = self.map(){ |k, v| "%p => %p" % [k, v] }.join(", ")
810
- return "\#<%p:{%s}>" % [self.class, content]
811
- end
812
-
813
- end
814
-
815
- # A worksheet (i.e. a tab) in a spreadsheet.
816
- # Use GoogleSpreadsheet::Spreadsheet#worksheets to get GoogleSpreadsheet::Worksheet object.
817
- class Worksheet
818
-
819
- include(Util)
820
-
821
- def initialize(session, spreadsheet, cells_feed_url, title = nil) #:nodoc:
822
- @session = session
823
- @spreadsheet = spreadsheet
824
- @cells_feed_url = cells_feed_url
825
- @title = title
826
-
827
- @cells = nil
828
- @input_values = nil
829
- @modified = Set.new()
830
- @list = nil
831
- end
832
-
833
- # URL of cell-based feed of the worksheet.
834
- attr_reader(:cells_feed_url)
835
-
836
- # URL of worksheet feed URL of the worksheet.
837
- def worksheet_feed_url
838
- # I don't know good way to get worksheet feed URL from cells feed URL.
839
- # Probably it would be cleaner to keep worksheet feed URL and get cells feed URL
840
- # from it.
841
- if !(@cells_feed_url =~
842
- %r{^https?://spreadsheets.google.com/feeds/cells/(.*)/(.*)/private/full((\?.*)?)$})
843
- raise(GoogleSpreadsheet::Error,
844
- "cells feed URL is in unknown format: #{@cells_feed_url}")
845
- end
846
- return "https://spreadsheets.google.com/feeds/worksheets/#{$1}/private/full/#{$2}#{$3}"
847
- end
848
-
849
- # GoogleSpreadsheet::Spreadsheet which this worksheet belongs to.
850
- def spreadsheet
851
- if !@spreadsheet
852
- if !(@cells_feed_url =~
853
- %r{^https?://spreadsheets.google.com/feeds/cells/(.*)/(.*)/private/full(\?.*)?$})
854
- raise(GoogleSpreadsheet::Error,
855
- "cells feed URL is in unknown format: #{@cells_feed_url}")
856
- end
857
- @spreadsheet = @session.spreadsheet_by_key($1)
858
- end
859
- return @spreadsheet
860
- end
861
-
862
- # Returns content of the cell as String. Top-left cell is [1, 1].
863
- def [](row, col)
864
- return self.cells[[row, col]] || ""
865
- end
866
-
867
- # Updates content of the cell.
868
- # Note that update is not sent to the server until you call save().
869
- # Top-left cell is [1, 1].
870
- #
871
- # e.g.
872
- # worksheet[2, 1] = "hoge"
873
- # worksheet[1, 3] = "=A1+B1"
874
- def []=(row, col, value)
875
- reload() if !@cells
876
- @cells[[row, col]] = value
877
- @input_values[[row, col]] = value
878
- @modified.add([row, col])
879
- self.max_rows = row if row > @max_rows
880
- self.max_cols = col if col > @max_cols
881
- end
882
-
883
- # Returns the value or the formula of the cell. Top-left cell is [1, 1].
884
- #
885
- # If user input "=A1+B1" to cell [1, 3]:
886
- # worksheet[1, 3] #=> "3" for example
887
- # worksheet.input_value(1, 3) #=> "=RC[-2]+RC[-1]"
888
- def input_value(row, col)
889
- reload() if !@cells
890
- return @input_values[[row, col]] || ""
891
- end
892
-
893
- # Row number of the bottom-most non-empty row.
894
- def num_rows
895
- reload() if !@cells
896
- return @input_values.select(){ |(r, c), v| !v.empty? }.map(){ |(r, c), v| r }.max || 0
897
- end
898
-
899
- # Column number of the right-most non-empty column.
900
- def num_cols
901
- reload() if !@cells
902
- return @input_values.select(){ |(r, c), v| !v.empty? }.map(){ |(r, c), v| c }.max || 0
903
- end
904
-
905
- # Number of rows including empty rows.
906
- def max_rows
907
- reload() if !@cells
908
- return @max_rows
909
- end
910
-
911
- # Updates number of rows.
912
- # Note that update is not sent to the server until you call save().
913
- def max_rows=(rows)
914
- reload() if !@cells
915
- @max_rows = rows
916
- @meta_modified = true
917
- end
918
-
919
- # Number of columns including empty columns.
920
- def max_cols
921
- reload() if !@cells
922
- return @max_cols
923
- end
924
-
925
- # Updates number of columns.
926
- # Note that update is not sent to the server until you call save().
927
- def max_cols=(cols)
928
- reload() if !@cells
929
- @max_cols = cols
930
- @meta_modified = true
931
- end
932
-
933
- # Title of the worksheet (shown as tab label in Web interface).
934
- def title
935
- reload() if !@title
936
- return @title
937
- end
938
-
939
- # Updates title of the worksheet.
940
- # Note that update is not sent to the server until you call save().
941
- def title=(title)
942
- reload() if !@cells
943
- @title = title
944
- @meta_modified = true
945
- end
946
-
947
- def cells #:nodoc:
948
- reload() if !@cells
949
- return @cells
950
- end
951
-
952
- # An array of spreadsheet rows. Each row contains an array of
953
- # columns. Note that resulting array is 0-origin so
954
- # worksheet.rows[0][0] == worksheet[1, 1].
955
- def rows(skip = 0)
956
- nc = self.num_cols
957
- result = ((1 + skip)..self.num_rows).map() do |row|
958
- (1..nc).map(){ |col| self[row, col] }.freeze()
959
- end
960
- return result.freeze()
961
- end
962
-
963
- # Reloads content of the worksheets from the server.
964
- # Note that changes you made by []= etc. is discarded if you haven't called save().
965
- def reload()
966
- doc = @session.request(:get, @cells_feed_url)
967
- @max_rows = doc.css('gs|rowCount').text.to_i
968
- @max_cols = doc.css('gs|colCount').text.to_i
969
- @title = doc.css('feed > title')[0].text
970
-
971
- @cells = {}
972
- @input_values = {}
973
- doc.css('feed > entry').each() do |entry|
974
- cell = entry.css('gs|cell').first
975
- row = cell["row"].to_i()
976
- col = cell["col"].to_i()
977
- @cells[[row, col]] = cell.inner_text
978
- @input_values[[row, col]] = cell["inputValue"]
979
- end
980
- @modified.clear()
981
- @meta_modified = false
982
- return true
983
- end
984
-
985
- # Saves your changes made by []=, etc. to the server.
986
- def save()
987
- sent = false
988
-
989
- if @meta_modified
990
-
991
- ws_doc = @session.request(:get, self.worksheet_feed_url)
992
- edit_url = ws_doc.css("link[@rel='edit']").first['href']
993
- xml = <<-"EOS"
994
- <entry xmlns='http://www.w3.org/2005/Atom'
995
- xmlns:gs='http://schemas.google.com/spreadsheets/2006'>
996
- <title>#{h(self.title)}</title>
997
- <gs:rowCount>#{h(self.max_rows)}</gs:rowCount>
998
- <gs:colCount>#{h(self.max_cols)}</gs:colCount>
999
- </entry>
1000
- EOS
1001
-
1002
- @session.request(:put, edit_url, :data => xml)
1003
-
1004
- @meta_modified = false
1005
- sent = true
1006
-
1007
- end
1008
-
1009
- if !@modified.empty?
1010
-
1011
- # Gets id and edit URL for each cell.
1012
- # Note that return-empty=true is required to get those info for empty cells.
1013
- cell_entries = {}
1014
- rows = @modified.map(){ |r, c| r }
1015
- cols = @modified.map(){ |r, c| c }
1016
- url = concat_url(@cells_feed_url,
1017
- "?return-empty=true&min-row=#{rows.min}&max-row=#{rows.max}" +
1018
- "&min-col=#{cols.min}&max-col=#{cols.max}")
1019
- doc = @session.request(:get, url)
1020
-
1021
- doc.css('entry').each() do |entry|
1022
- row = entry.css('gs|cell').first['row'].to_i
1023
- col = entry.css('gs|cell').first['col'].to_i
1024
- cell_entries[[row, col]] = entry
1025
- end
1026
-
1027
- # Updates cell values using batch operation.
1028
- # If the data is large, we split it into multiple operations, otherwise batch may fail.
1029
- @modified.each_slice(250) do |chunk|
1030
-
1031
- xml = <<-EOS
1032
- <feed xmlns="http://www.w3.org/2005/Atom"
1033
- xmlns:batch="http://schemas.google.com/gdata/batch"
1034
- xmlns:gs="http://schemas.google.com/spreadsheets/2006">
1035
- <id>#{h(@cells_feed_url)}</id>
1036
- EOS
1037
- for row, col in chunk
1038
- value = @cells[[row, col]]
1039
- entry = cell_entries[[row, col]]
1040
- id = entry.css('id').text
1041
- edit_url = entry.css("link[@rel='edit']").first['href']
1042
- xml << <<-EOS
1043
- <entry>
1044
- <batch:id>#{h(row)},#{h(col)}</batch:id>
1045
- <batch:operation type="update"/>
1046
- <id>#{h(id)}</id>
1047
- <link rel="edit" type="application/atom+xml"
1048
- href="#{h(edit_url)}"/>
1049
- <gs:cell row="#{h(row)}" col="#{h(col)}" inputValue="#{h(value)}"/>
1050
- </entry>
1051
- EOS
1052
- end
1053
- xml << <<-"EOS"
1054
- </feed>
1055
- EOS
1056
-
1057
- batch_url = concat_url(@cells_feed_url, "/batch")
1058
- result = @session.request(:post, batch_url, :data => xml)
1059
- result.css('atom|entry').each() do |entry|
1060
- interrupted = entry.css('batch|interrupted').first
1061
- if interrupted
1062
- raise(GoogleSpreadsheet::Error, "Update has failed: %s" %
1063
- interrupted["reason"])
1064
- end
1065
- if !(entry.css('batch|status').first['code'] =~ /^2/)
1066
- raise(GoogleSpreadsheet::Error, "Updating cell %s has failed: %s" %
1067
- [entry.css('atom|id').text, entry.css('batch|status').first['reason']])
1068
- end
1069
- end
1070
-
1071
- end
1072
-
1073
- @modified.clear()
1074
- sent = true
1075
-
1076
- end
1077
- return sent
1078
- end
1079
-
1080
- # Calls save() and reload().
1081
- def synchronize()
1082
- save()
1083
- reload()
1084
- end
1085
-
1086
- # Deletes this worksheet. Deletion takes effect right away without calling save().
1087
- def delete()
1088
- ws_doc = @session.request(:get, self.worksheet_feed_url)
1089
- edit_url = ws_doc.css("link[@rel='edit']").first['href']
1090
- @session.request(:delete, edit_url)
1091
- end
1092
-
1093
- # Returns true if you have changes made by []= which haven't been saved.
1094
- def dirty?
1095
- return !@modified.empty?
1096
- end
1097
-
1098
- # DEPRECATED: Table and Record feeds are deprecated and they will not be available after
1099
- # March 2012.
1100
- #
1101
- # Creates table for the worksheet and returns GoogleSpreadsheet::Table.
1102
- # See this document for details:
1103
- # http://code.google.com/intl/en/apis/spreadsheets/docs/3.0/developers_guide_protocol.html#TableFeeds
1104
- def add_table(table_title, summary, columns, options)
1105
- warn(
1106
- "DEPRECATED: Google Spreadsheet Table and Record feeds are deprecated and they will " +
1107
- "not be available after March 2012.")
1108
- default_options = { :header_row => 1, :num_rows => 0, :start_row => 2}
1109
- options = default_options.merge(options)
1110
-
1111
- column_xml = ""
1112
- columns.each() do |index, name|
1113
- column_xml += "<gs:column index='#{h(index)}' name='#{h(name)}'/>\n"
1114
- end
1115
-
1116
- xml = <<-"EOS"
1117
- <entry xmlns="http://www.w3.org/2005/Atom"
1118
- xmlns:gs="http://schemas.google.com/spreadsheets/2006">
1119
- <title type='text'>#{h(table_title)}</title>
1120
- <summary type='text'>#{h(summary)}</summary>
1121
- <gs:worksheet name='#{h(self.title)}' />
1122
- <gs:header row='#{options[:header_row]}' />
1123
- <gs:data numRows='#{options[:num_rows]}' startRow='#{options[:start_row]}'>
1124
- #{column_xml}
1125
- </gs:data>
1126
- </entry>
1127
- EOS
1128
-
1129
- result = @session.request(:post, self.spreadsheet.tables_feed_url, :data => xml)
1130
- return Table.new(@session, result)
1131
- end
1132
-
1133
- # Returns list of tables for the workwheet.
1134
- def tables
1135
- return self.spreadsheet.tables.select(){ |t| t.worksheet_title == self.title }
1136
- end
1137
-
1138
- # List feed URL of the worksheet.
1139
- def list_feed_url
1140
- # Gets the worksheets metafeed.
1141
- entry = @session.request(:get, self.worksheet_feed_url)
1142
-
1143
- # Gets the URL of list-based feed for the given spreadsheet.
1144
- return entry.css(
1145
- "link[@rel='http://schemas.google.com/spreadsheets/2006#listfeed']").first['href']
1146
- end
1147
-
1148
- # Provides access to cells using column names, assuming the first row contains column
1149
- # names. Returned object is GoogleSpreadsheet::List which you can use mostly as
1150
- # Array of Hash.
1151
- #
1152
- # e.g. Assuming the first row is ["x", "y"]:
1153
- # worksheet.list[0]["x"] #=> "1" # i.e. worksheet[2, 1]
1154
- # worksheet.list[0]["y"] #=> "2" # i.e. worksheet[2, 2]
1155
- # worksheet.list[1]["x"] = "3" # i.e. worksheet[3, 1] = "3"
1156
- # worksheet.list[1]["y"] = "4" # i.e. worksheet[3, 2] = "4"
1157
- # worksheet.list.push({"x" => "5", "y" => "6"})
1158
- #
1159
- # Note that update is not sent to the server until you call save().
1160
- def list
1161
- return @list ||= List.new(self)
1162
- end
1163
-
1164
- def inspect
1165
- fields = {:worksheet_feed_url => self.worksheet_feed_url}
1166
- fields[:title] = @title if @title
1167
- return '#<%p %s>' % [self.class, fields.map(){ |k, v| "%s=%p" % [k, v] }.join(", ")]
1168
- end
1169
-
1170
- end
1171
-
1172
-
1173
- # Provides access to cells using column names.
1174
- # Use GoogleSpreadsheet::Worksheet#list to get GoogleSpreadsheet::List object.
1175
- #--
1176
- # This is implemented as wrapper of GoogleSpreadsheet::Worksheet i.e. using cells
1177
- # feed, not list feed. In this way, we can easily provide consistent API as
1178
- # GoogleSpreadsheet::Worksheet using save()/reload().
1179
- class List
1180
-
1181
- include(Enumerable)
1182
-
1183
- def initialize(worksheet) #:nodoc:
1184
- @worksheet = worksheet
1185
- end
1186
-
1187
- # Number of non-empty rows in the worksheet excluding the first row.
1188
- def size
1189
- return @worksheet.num_rows - 1
1190
- end
1191
-
1192
- # Returns Hash-like object (GoogleSpreadsheet::ListRow) for the row with the
1193
- # index. Keys of the object are colum names (the first row).
1194
- # The second row has index 0.
1195
- #
1196
- # Note that updates to the returned object are not sent to the server until
1197
- # you call GoogleSpreadsheet::Worksheet#save().
1198
- def [](index)
1199
- return ListRow.new(self, index)
1200
- end
1201
-
1202
- # Updates the row with the index with the given Hash object.
1203
- # Keys of +hash+ are colum names (the first row).
1204
- # The second row has index 0.
1205
- #
1206
- # Note that update is not sent to the server until
1207
- # you call GoogleSpreadsheet::Worksheet#save().
1208
- def []=(index, hash)
1209
- self[index].replace(hash)
1210
- end
1211
-
1212
- # Iterates over Hash-like object (GoogleSpreadsheet::ListRow) for each row
1213
- # (except for the first row).
1214
- # Keys of the object are colum names (the first row).
1215
- def each(&block)
1216
- for i in 0...self.size
1217
- yield(self[i])
1218
- end
1219
- end
1220
-
1221
- # Column names i.e. the contents of the first row.
1222
- # Duplicates are removed.
1223
- def keys
1224
- return (1..@worksheet.num_cols).map(){ |i| @worksheet[1, i] }.uniq()
1225
- end
1226
-
1227
- # Updates column names i.e. the contents of the first row.
1228
- #
1229
- # Note that update is not sent to the server until
1230
- # you call GoogleSpreadsheet::Worksheet#save().
1231
- def keys=(ary)
1232
- for i in 1..ary.size
1233
- @worksheet[1, i] = ary[i - 1]
1234
- end
1235
- for i in (ary.size + 1)..@worksheet.num_cols
1236
- @worksheet[1, i] = ""
1237
- end
1238
- end
1239
-
1240
- # Adds a new row to the bottom.
1241
- # Keys of +hash+ are colum names (the first row).
1242
- # Returns GoogleSpreadsheet::ListRow for the new row.
1243
- #
1244
- # Note that update is not sent to the server until
1245
- # you call GoogleSpreadsheet::Worksheet#save().
1246
- def push(hash)
1247
- row = self[self.size]
1248
- row.update(hash)
1249
- return row
1250
- end
1251
-
1252
- # Returns all rows (except for the first row) as Array of Hash.
1253
- # Keys of Hash objects are colum names (the first row).
1254
- def to_hash_array()
1255
- return self.map(){ |r| r.to_hash() }
1256
- end
1257
-
1258
- def get(index, key) #:nodoc:
1259
- return @worksheet[index + 2, key_to_col(key)]
1260
- end
1261
-
1262
- def set(index, key, value) #:nodoc:
1263
- @worksheet[index + 2, key_to_col(key)] = value
1264
- end
1265
-
1266
- private
1267
-
1268
- def key_to_col(key)
1269
- key = key.to_s()
1270
- col = (1..@worksheet.num_cols).find(){ |c| @worksheet[1, c] == key }
1271
- raise(GoogleSpreadsheet::Error, "colunm doesn't exist: %p" % key) if !col
1272
- return col
1273
- end
1274
-
1275
- end
1276
-
1277
- # Hash-like object returned by GoogleSpreadsheet::List#[].
1278
- class ListRow
1279
-
1280
- include(Enumerable)
1281
- extend(Forwardable)
1282
-
1283
- def_delegators(:to_hash,
1284
- :keys, :values, :each_key, :each_value, :each, :each_pair, :hash,
1285
- :assoc, :fetch, :flatten, :key, :invert, :size, :length, :rassoc,
1286
- :merge, :reject, :select, :sort, :to_a, :values_at)
1287
-
1288
- def initialize(list, index) #:nodoc:
1289
- @list = list
1290
- @index = index
1291
- end
1292
-
1293
- def [](key)
1294
- return @list.get(@index, key)
1295
- end
1296
-
1297
- def []=(key, value)
1298
- @list.set(@index, key, value)
1299
- end
1300
-
1301
- def has_key?(key)
1302
- return @list.keys.include?(key)
1303
- end
1304
-
1305
- alias include? has_key?
1306
- alias key? has_key?
1307
- alias member? has_key?
1308
-
1309
- def update(hash)
1310
- for k, v in hash
1311
- self[k] = v
1312
- end
1313
- end
1314
-
1315
- alias merge! update
1316
-
1317
- def replace(hash)
1318
- clear()
1319
- update(hash)
1320
- end
1321
-
1322
- def clear()
1323
- for key in @list.keys
1324
- self[key] = ""
1325
- end
1326
- end
1327
-
1328
- def to_hash()
1329
- result = {}
1330
- for key in @list.keys
1331
- result[key] = self[key]
1332
- end
1333
- return result
1334
- end
1335
-
1336
- def ==(other)
1337
- return self.class == other.class && self.to_hash() == other.to_hash()
1338
- end
1339
-
1340
- alias === ==
1341
- alias eql? ==
1342
-
1343
- def inspect
1344
- return "\#<%p %p>" % [self.class, to_hash()]
1345
- end
1346
-
1347
- end
1348
119
 
1349
120
  end