google-spreadsheet-ruby 0.1.5 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/README.rdoc +2 -3
  2. data/lib/google_spreadsheet.rb +212 -63
  3. metadata +48 -67
data/README.rdoc CHANGED
@@ -3,7 +3,6 @@ This is a Ruby 1.8/1.9 library to read/write Google Spreadsheet.
3
3
 
4
4
  = How to install
5
5
 
6
- $ gem sources -a http://gemcutter.org
7
6
  $ sudo gem install google-spreadsheet-ruby
8
7
 
9
8
  Note that gimite-google-spreadsheet-ruby at gems.github.com is no longer updated, because github stopped hosting it.
@@ -49,14 +48,14 @@ API document: http://gimite.net/gimite/rubymess/google-spreadsheet-ruby/
49
48
 
50
49
  = Source code
51
50
 
52
- http://github.com/gimite/google-spreadsheet-ruby/tree/master
51
+ http://github.com/gimite/google-spreadsheet-ruby
53
52
 
54
53
  The license of this source is "New BSD Licence"
55
54
 
56
55
 
57
56
  = Supported environments
58
57
 
59
- Ruby 1.8.x and Ruby 1.9.x. Checked with Ruby 1.8.7 and Ruby 1.9.1.
58
+ Ruby 1.8.x and Ruby 1.9.x. Checked with Ruby 1.8.7 and Ruby 1.9.3.
60
59
 
61
60
 
62
61
  = Author
@@ -10,6 +10,7 @@ require "uri"
10
10
  require "rubygems"
11
11
  require "nokogiri"
12
12
  require "oauth"
13
+ require "oauth2"
13
14
  Net::HTTP.version_1_2
14
15
 
15
16
  module GoogleSpreadsheet
@@ -26,9 +27,31 @@ module GoogleSpreadsheet
26
27
  return Session.login(mail, password, proxy)
27
28
  end
28
29
 
29
- # Authenticates with given OAuth token.
30
+ # Authenticates with given OAuth1 or OAuth2 token.
30
31
  #
31
- # For generating oauth_token, you can proceed as follow:
32
+ # OAuth2 code example:
33
+ #
34
+ # client = OAuth2::Client.new(
35
+ # your_client_id, your_client_secret,
36
+ # :site => "https://accounts.google.com",
37
+ # :token_url => "/o/oauth2/token",
38
+ # :authorize_url => "/o/oauth2/auth")
39
+ # auth_url = client.auth_code.authorize_url(
40
+ # :redirect_uri => "http://example.com/",
41
+ # "scope" => "https://spreadsheets.google.com/feeds https://docs.google.com/feeds/")
42
+ # # Redirect the user to auth_url and get authorization code from redirect URL.
43
+ # auth_token = client.auth_code.get_token(
44
+ # authorization_code, :redirect_uri => "http://example.com/")
45
+ # session = GoogleSpreadsheet.login_with_oauth(auth_token)
46
+ #
47
+ # Or, from existing refresh token:
48
+ #
49
+ # access_token = OAuth2::AccessToken.from_hash(client,
50
+ # {:refresh_token => refresh_token, :expires_at => expires_at})
51
+ # access_token = access_token.refresh!
52
+ # session = GoogleSpreadsheet.login_with_oauth(access_token)
53
+ #
54
+ # OAuth1 code example:
32
55
  #
33
56
  # 1) First generate OAuth consumer object with key and secret for your site by registering site with google
34
57
  # @consumer = OAuth::Consumer.new( "key","secret", {:site=>"https://agree2"})
@@ -42,12 +65,21 @@ module GoogleSpreadsheet
42
65
  #
43
66
  # See these documents for details:
44
67
  #
68
+ # - https://github.com/intridea/oauth2
69
+ # - http://code.google.com/apis/accounts/docs/OAuth2.html
45
70
  # - http://oauth.rubyforge.org/
46
71
  # - http://code.google.com/apis/accounts/docs/OAuth.html
47
72
  def self.login_with_oauth(oauth_token)
48
73
  return Session.login_with_oauth(oauth_token)
49
74
  end
50
75
 
76
+ # Restores session using return value of auth_tokens method of previous session.
77
+ #
78
+ # See GoogleSpreadsheet.login for description of parameter +proxy+.
79
+ def self.restore_session(auth_tokens, proxy = nil)
80
+ return Session.restore_session(auth_tokens, proxy)
81
+ end
82
+
51
83
  # Restores GoogleSpreadsheet::Session from +path+ and returns it.
52
84
  # If +path+ doesn't exist or authentication has failed, prompts mail and password on console,
53
85
  # authenticates with them, stores the session to +path+ and returns it.
@@ -100,6 +132,13 @@ module GoogleSpreadsheet
100
132
  def encode_query(params)
101
133
  return params.map(){ |k, v| CGI.escape(k) + "=" + CGI.escape(v) }.join("&")
102
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
103
142
 
104
143
  def h(str)
105
144
  return CGI.escapeHTML(str.to_s())
@@ -118,6 +157,89 @@ module GoogleSpreadsheet
118
157
  class AuthenticationError < GoogleSpreadsheet::Error
119
158
 
120
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
121
243
 
122
244
 
123
245
  # Use GoogleSpreadsheet.login or GoogleSpreadsheet.saved_session to get
@@ -129,29 +251,36 @@ module GoogleSpreadsheet
129
251
 
130
252
  # The same as GoogleSpreadsheet.login.
131
253
  def self.login(mail, password, proxy = nil)
132
- session = Session.new(nil, nil, proxy)
254
+ session = Session.new(nil, ClientLoginFetcher.new({}, proxy))
133
255
  session.login(mail, password)
134
256
  return session
135
257
  end
136
258
 
137
259
  # The same as GoogleSpreadsheet.login_with_oauth.
138
260
  def self.login_with_oauth(oauth_token)
139
- session = Session.new(nil, 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)
140
271
  end
141
272
 
142
- # Restores session using return value of auth_tokens method of previous session.
143
- #
144
- # See GoogleSpreadsheet.login for description of parameter +proxy+.
145
- def initialize(auth_tokens = nil, oauth_token = nil, proxy = nil)
146
- @oauth_token = oauth_token
147
- @auth_tokens = auth_tokens || {}
148
- if proxy
149
- @proxy = proxy
150
- elsif ENV["http_proxy"] && !ENV["http_proxy"].empty?
151
- proxy_url = URI.parse(ENV["http_proxy"])
152
- @proxy = Net::HTTP.Proxy(proxy_url.host, proxy_url.port)
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
153
282
  else
154
- @proxy = Net::HTTP
283
+ @fetcher = ClientLoginFetcher.new(auth_tokens || {}, proxy)
155
284
  end
156
285
  end
157
286
 
@@ -159,10 +288,15 @@ module GoogleSpreadsheet
159
288
  # if succeeds. Raises GoogleSpreadsheet::AuthenticationError if fails.
160
289
  # Google Apps account is supported.
161
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 or login_with_oauth2.")
294
+ end
162
295
  begin
163
- @auth_tokens = {}
164
- authenticate(mail, password, :wise)
165
- authenticate(mail, password, :writely)
296
+ @fetcher.auth_tokens = {
297
+ :wise => authenticate(mail, password, :wise),
298
+ :writely => authenticate(mail, password, :writely),
299
+ }
166
300
  rescue GoogleSpreadsheet::Error => ex
167
301
  return true if @on_auth_fail && @on_auth_fail.call()
168
302
  raise(AuthenticationError, "authentication failed for #{mail}: #{ex.message}")
@@ -170,26 +304,24 @@ module GoogleSpreadsheet
170
304
  end
171
305
 
172
306
  # Authentication tokens.
173
- attr_reader(:auth_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 or login_with_oauth2.")
312
+ end
313
+ return @fetcher.auth_tokens
314
+ end
174
315
 
175
316
  # Authentication token.
176
317
  def auth_token(auth = :wise)
177
- return @auth_tokens[auth]
318
+ return self.auth_tokens[auth]
178
319
  end
179
320
 
180
321
  # Proc or Method called when authentication has failed.
181
322
  # When this function returns +true+, it tries again.
182
323
  attr_accessor :on_auth_fail
183
324
 
184
- def auth_header(auth) #:nodoc:
185
- token = auth == :none ? nil : @auth_tokens[auth]
186
- if token
187
- return {"Authorization" => "GoogleLogin auth=#{token}"}
188
- else
189
- return {}
190
- end
191
- end
192
-
193
325
  # Returns list of spreadsheets for the user as array of GoogleSpreadsheet::Spreadsheet.
194
326
  # You can specify query parameters described at
195
327
  # http://code.google.com/apis/spreadsheets/docs/2.0/reference.html#Parameters
@@ -251,6 +383,17 @@ module GoogleSpreadsheet
251
383
  return Worksheet.new(self, nil, url)
252
384
  end
253
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
+
254
397
  # Creates new spreadsheet and returns the new GoogleSpreadsheet::Spreadsheet.
255
398
  #
256
399
  # e.g.
@@ -287,7 +430,7 @@ module GoogleSpreadsheet
287
430
  response_type = params[:response_type] || :xml
288
431
 
289
432
  while true
290
- response = request_raw(method, url, data, extra_header, auth)
433
+ response = @fetcher.request_raw(method, url, data, extra_header, auth)
291
434
  if response.code == "401" && @on_auth_fail && @on_auth_fail.call()
292
435
  next
293
436
  end
@@ -303,30 +446,6 @@ module GoogleSpreadsheet
303
446
 
304
447
  private
305
448
 
306
- def request_raw(method, url, data, extra_header, auth)
307
- if @oauth_token
308
- if method == :delete || method == :get
309
- return @oauth_token.__send__(method, url, extra_header)
310
- else
311
- return @oauth_token.__send__(method, url, data, extra_header)
312
- end
313
- else
314
- uri = URI.parse(url)
315
- http = @proxy.new(uri.host, uri.port)
316
- http.use_ssl = true
317
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
318
- http.start() do
319
- path = uri.path + (uri.query ? "?#{uri.query}" : "")
320
- header = auth_header(auth).merge(extra_header)
321
- if method == :delete || method == :get
322
- return http.__send__(method, path, header)
323
- else
324
- return http.__send__(method, path, data, header)
325
- end
326
- end
327
- end
328
- end
329
-
330
449
  def convert_response(response, response_type)
331
450
  case response_type
332
451
  when :xml
@@ -350,7 +469,7 @@ module GoogleSpreadsheet
350
469
  response = request(:post,
351
470
  "https://www.google.com/accounts/ClientLogin",
352
471
  :data => encode_query(params), :auth => :none, :header => header, :response_type => :raw)
353
- @auth_tokens[auth] = response.slice(/^Auth=(.*)$/, 1)
472
+ return response.slice(/^Auth=(.*)$/, 1)
354
473
  end
355
474
 
356
475
  end
@@ -459,7 +578,7 @@ module GoogleSpreadsheet
459
578
  "link[@rel='http://schemas.google.com/spreadsheets/2006#worksheetsfeed']").first["href"]
460
579
  return Spreadsheet.new(@session, ss_url, new_title)
461
580
  end
462
-
581
+
463
582
  # If +permanent+ is +false+, moves the spreadsheet to the trash.
464
583
  # If +permanent+ is +true+, deletes the spreadsheet permanently.
465
584
  def delete(permanent = false)
@@ -562,7 +681,35 @@ module GoogleSpreadsheet
562
681
  end
563
682
 
564
683
  end
684
+
685
+ # Use GoogleSpreadsheet::Session#collection_by_url to get GoogleSpreadsheet::Collection object.
686
+ class Collection
687
+
688
+ include(Util)
689
+
690
+ def initialize(session, collection_feed_url) #:nodoc:
691
+ @session = session
692
+ @collection_feed_url = collection_feed_url
693
+ end
694
+
695
+ # Adds the given GoogleSpreadsheet::Spreadsheet to the collection.
696
+ def add(spreadsheet)
697
+ contents_url = concat_url(@collection_feed_url, "/contents")
698
+ header = {"GData-Version" => "3.0", "Content-Type" => "application/atom+xml"}
699
+ xml = <<-"EOS"
700
+ <entry xmlns="http://www.w3.org/2005/Atom">
701
+ <id>#{h(spreadsheet.document_feed_url)}</id>
702
+ </entry>
703
+ EOS
704
+ @session.request(
705
+ :post, contents_url, :data => xml, :header => header, :auth => :writely)
706
+ return nil
707
+ end
708
+
709
+ # TODO Add other operations.
565
710
 
711
+ end
712
+
566
713
  # DEPRECATED: Table and Record feeds are deprecated and they will not be available after
567
714
  # March 2012.
568
715
  #
@@ -658,18 +805,18 @@ module GoogleSpreadsheet
658
805
  # Probably it would be cleaner to keep worksheet feed URL and get cells feed URL
659
806
  # from it.
660
807
  if !(@cells_feed_url =~
661
- %r{^https?://spreadsheets.google.com/feeds/cells/(.*)/(.*)/private/full$})
808
+ %r{^https?://spreadsheets.google.com/feeds/cells/(.*)/(.*)/private/full((\?.*)?)$})
662
809
  raise(GoogleSpreadsheet::Error,
663
810
  "cells feed URL is in unknown format: #{@cells_feed_url}")
664
811
  end
665
- return "https://spreadsheets.google.com/feeds/worksheets/#{$1}/private/full/#{$2}"
812
+ return "https://spreadsheets.google.com/feeds/worksheets/#{$1}/private/full/#{$2}#{$3}"
666
813
  end
667
814
 
668
815
  # GoogleSpreadsheet::Spreadsheet which this worksheet belongs to.
669
816
  def spreadsheet
670
817
  if !@spreadsheet
671
818
  if !(@cells_feed_url =~
672
- %r{^https?://spreadsheets.google.com/feeds/cells/(.*)/(.*)/private/full$})
819
+ %r{^https?://spreadsheets.google.com/feeds/cells/(.*)/(.*)/private/full(\?.*)?$})
673
820
  raise(GoogleSpreadsheet::Error,
674
821
  "cells feed URL is in unknown format: #{@cells_feed_url}")
675
822
  end
@@ -832,8 +979,9 @@ module GoogleSpreadsheet
832
979
  cell_entries = {}
833
980
  rows = @modified.map(){ |r, c| r }
834
981
  cols = @modified.map(){ |r, c| c }
835
- url = "#{@cells_feed_url}?return-empty=true&min-row=#{rows.min}&max-row=#{rows.max}" +
836
- "&min-col=#{cols.min}&max-col=#{cols.max}"
982
+ url = concat_url(@cells_feed_url,
983
+ "?return-empty=true&min-row=#{rows.min}&max-row=#{rows.max}" +
984
+ "&min-col=#{cols.min}&max-col=#{cols.max}")
837
985
  doc = @session.request(:get, url)
838
986
 
839
987
  doc.css('entry').each() do |entry|
@@ -872,7 +1020,8 @@ module GoogleSpreadsheet
872
1020
  </feed>
873
1021
  EOS
874
1022
 
875
- result = @session.request(:post, "#{@cells_feed_url}/batch", :data => xml)
1023
+ batch_url = concat_url(@cells_feed_url, "/batch")
1024
+ result = @session.request(:post, batch_url, :data => xml)
876
1025
  result.css('atom|entry').each() do |entry|
877
1026
  interrupted = entry.css('batch|interrupted').first
878
1027
  if interrupted
metadata CHANGED
@@ -1,102 +1,83 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: google-spreadsheet-ruby
3
- version: !ruby/object:Gem::Version
4
- hash: 17
5
- prerelease: false
6
- segments:
7
- - 0
8
- - 1
9
- - 5
10
- version: 0.1.5
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.6
5
+ prerelease:
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Hiroshi Ichikawa
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2011-05-14 00:00:00 +09:00
19
- default_executable:
20
- dependencies:
21
- - !ruby/object:Gem::Dependency
12
+ date: 2011-11-27 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
22
15
  name: nokogiri
23
- prerelease: false
24
- requirement: &id001 !ruby/object:Gem::Requirement
16
+ requirement: &81815340 !ruby/object:Gem::Requirement
25
17
  none: false
26
- requirements:
27
- - - ">="
28
- - !ruby/object:Gem::Version
29
- hash: 113
30
- segments:
31
- - 1
32
- - 4
33
- - 3
34
- - 1
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
35
21
  version: 1.4.3.1
36
22
  type: :runtime
37
- version_requirements: *id001
38
- - !ruby/object:Gem::Dependency
39
- name: oauth
40
23
  prerelease: false
41
- requirement: &id002 !ruby/object:Gem::Requirement
24
+ version_requirements: *81815340
25
+ - !ruby/object:Gem::Dependency
26
+ name: oauth
27
+ requirement: &81815050 !ruby/object:Gem::Requirement
42
28
  none: false
43
- requirements:
44
- - - ">="
45
- - !ruby/object:Gem::Version
46
- hash: 31
47
- segments:
48
- - 0
49
- - 3
50
- - 6
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
51
32
  version: 0.3.6
52
33
  type: :runtime
53
- version_requirements: *id002
34
+ prerelease: false
35
+ version_requirements: *81815050
36
+ - !ruby/object:Gem::Dependency
37
+ name: oauth2
38
+ requirement: &81814770 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: 0.5.0
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *81814770
54
47
  description: This is a library to read/write Google Spreadsheet.
55
- email:
48
+ email:
56
49
  - gimite+github@gmail.com
57
50
  executables: []
58
-
59
51
  extensions: []
60
-
61
- extra_rdoc_files:
52
+ extra_rdoc_files:
62
53
  - README.rdoc
63
- files:
54
+ files:
64
55
  - README.rdoc
65
56
  - lib/google_spreadsheet.rb
66
- has_rdoc: true
67
57
  homepage: https://github.com/gimite/google-spreadsheet-ruby
68
58
  licenses: []
69
-
70
59
  post_install_message:
71
- rdoc_options:
60
+ rdoc_options:
72
61
  - --main
73
62
  - README.rdoc
74
- require_paths:
63
+ require_paths:
75
64
  - lib
76
- required_ruby_version: !ruby/object:Gem::Requirement
65
+ required_ruby_version: !ruby/object:Gem::Requirement
77
66
  none: false
78
- requirements:
79
- - - ">="
80
- - !ruby/object:Gem::Version
81
- hash: 3
82
- segments:
83
- - 0
84
- version: "0"
85
- required_rubygems_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ! '>='
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
72
  none: false
87
- requirements:
88
- - - ">="
89
- - !ruby/object:Gem::Version
90
- hash: 3
91
- segments:
92
- - 0
93
- version: "0"
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
94
77
  requirements: []
95
-
96
78
  rubyforge_project:
97
- rubygems_version: 1.3.7
79
+ rubygems_version: 1.8.11
98
80
  signing_key:
99
81
  specification_version: 3
100
82
  summary: This is a library to read/write Google Spreadsheet.
101
83
  test_files: []
102
-