wolframarnold-google-spreadsheet-ruby 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +19 -0
- data/README.rdoc +64 -0
- data/init.rb +2 -0
- data/lib/google_spreadsheet.rb +817 -0
- data/script/gen-rdoc +3 -0
- data/test/test_google_spreadsheet.rb +85 -0
- metadata +83 -0
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2010 Hiroshi Ichikawa, Wolfram Arnold
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
19
|
+
SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
This is a Ruby 1.8/1.9 library to read/write Google Spreadsheet.
|
2
|
+
|
3
|
+
|
4
|
+
= How to install
|
5
|
+
|
6
|
+
$ gem sources -a http://gemcutter.org
|
7
|
+
$ sudo gem install google-spreadsheet-ruby
|
8
|
+
|
9
|
+
Note that gimite-google-spreadsheet-ruby at gems.github.com is no longer updated, because github stopped hosting it.
|
10
|
+
|
11
|
+
|
12
|
+
= How to use
|
13
|
+
|
14
|
+
Example:
|
15
|
+
|
16
|
+
require "rubygems"
|
17
|
+
require "google_spreadsheet"
|
18
|
+
|
19
|
+
# Logs in.
|
20
|
+
# You can also use OAuth. See document of GoogleSpreadsheet.login_with_oauth for details.
|
21
|
+
session = GoogleSpreadsheet.login("username@gmail.com", "mypassword")
|
22
|
+
|
23
|
+
# First worksheet of http://spreadsheets.google.com/ccc?key=pz7XtlQC-PYx-jrVMJErTcg&hl=en
|
24
|
+
ws = session.spreadsheet_by_key("pz7XtlQC-PYx-jrVMJErTcg").worksheets[0]
|
25
|
+
|
26
|
+
# Gets content of A2 cell.
|
27
|
+
p ws[2, 1] #==> "hoge"
|
28
|
+
|
29
|
+
# Changes content of cells. Changes are not sent to the server until you call ws.save().
|
30
|
+
ws[2, 1] = "foo"
|
31
|
+
ws[2, 2] = "bar"
|
32
|
+
ws.save()
|
33
|
+
|
34
|
+
# Dumps all cells.
|
35
|
+
for row in 1..ws.num_rows
|
36
|
+
for col in 1..ws.num_cols
|
37
|
+
p ws[row, col]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Yet another way to do so.
|
42
|
+
p ws.rows #==> [["fuga", ""], ["foo", "bar]]
|
43
|
+
|
44
|
+
# Reloads the worksheet to get changes by other clients.
|
45
|
+
ws.reload()
|
46
|
+
|
47
|
+
API document: http://gimite.net/gimite/rubymess/google-spreadsheet-ruby/
|
48
|
+
|
49
|
+
|
50
|
+
= Source code
|
51
|
+
|
52
|
+
http://github.com/gimite/google-spreadsheet-ruby/tree/master
|
53
|
+
|
54
|
+
The license of this source is "New BSD Licence"
|
55
|
+
|
56
|
+
|
57
|
+
= Supported environments
|
58
|
+
|
59
|
+
Ruby 1.8.x and Ruby 1.9.x. Checked with Ruby 1.8.7 and Ruby 1.9.1.
|
60
|
+
|
61
|
+
|
62
|
+
= Author
|
63
|
+
|
64
|
+
Hiroshi Ichikawa - http://gimite.net/en/index.php?Contact
|
data/init.rb
ADDED
@@ -0,0 +1,817 @@
|
|
1
|
+
# Author: Hiroshi Ichikawa <http://gimite.net/>
|
2
|
+
# The license of this source is "New BSD Licence"
|
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 "hpricot"
|
12
|
+
require "oauth"
|
13
|
+
Net::HTTP.version_1_2
|
14
|
+
|
15
|
+
module GoogleSpreadsheet
|
16
|
+
|
17
|
+
# Authenticates with given +mail+ and +password+, and returns GoogleSpreadsheet::Session
|
18
|
+
# if succeeds. Raises GoogleSpreadsheet::AuthenticationError if fails.
|
19
|
+
# Google Apps account is supported.
|
20
|
+
def self.login(mail, password)
|
21
|
+
return Session.login(mail, password)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Authenticates with given OAuth token.
|
25
|
+
#
|
26
|
+
# For generating oauth_token, you can proceed as follow:
|
27
|
+
#
|
28
|
+
# 1) First generate OAuth consumer object with key and secret for your site by registering site with google
|
29
|
+
# @consumer = OAuth::Consumer.new( "key","secret", {:site=>"https://agree2"})
|
30
|
+
# 2) Request token with OAuth
|
31
|
+
# @request_token = @consumer.get_request_token
|
32
|
+
# session[:request_token] = @request_token
|
33
|
+
# redirect_to @request_token.authorize_url
|
34
|
+
# 3) Create an oauth access token
|
35
|
+
# @oauth_access_token = @request_token.get_access_token
|
36
|
+
# @access_token = OAuth::AccessToken.new(@consumer, @oauth_access_token.token, @oauth_access_token.secret)
|
37
|
+
#
|
38
|
+
# See these documents for details:
|
39
|
+
#
|
40
|
+
# - http://oauth.rubyforge.org/
|
41
|
+
# - http://code.google.com/apis/accounts/docs/OAuth.html
|
42
|
+
def self.login_with_oauth(oauth_token)
|
43
|
+
return Session.login_with_oauth(oauth_token)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Restores GoogleSpreadsheet::Session from +path+ and returns it.
|
47
|
+
# If +path+ doesn't exist or authentication has failed, prompts mail and password on console,
|
48
|
+
# authenticates with them, stores the session to +path+ and returns it.
|
49
|
+
#
|
50
|
+
# This method requires Highline library: http://rubyforge.org/projects/highline/
|
51
|
+
def self.saved_session(path = ENV["HOME"] + "/.ruby_google_spreadsheet.token")
|
52
|
+
tokens = {}
|
53
|
+
if File.exist?(path)
|
54
|
+
open(path) do |f|
|
55
|
+
for auth in [:wise, :writely]
|
56
|
+
line = f.gets()
|
57
|
+
tokens[auth] = line && line.chomp()
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
session = Session.new(tokens)
|
62
|
+
session.on_auth_fail = proc() do
|
63
|
+
begin
|
64
|
+
require "highline"
|
65
|
+
rescue LoadError
|
66
|
+
raise(LoadError,
|
67
|
+
"GoogleSpreadsheet.saved_session requires Highline library.\n" +
|
68
|
+
"Run\n" +
|
69
|
+
" \$ sudo gem install highline\n" +
|
70
|
+
"to install it.")
|
71
|
+
end
|
72
|
+
highline = HighLine.new()
|
73
|
+
mail = highline.ask("Mail: ")
|
74
|
+
password = highline.ask("Password: "){ |q| q.echo = false }
|
75
|
+
session.login(mail, password)
|
76
|
+
open(path, "w", 0600) do |f|
|
77
|
+
f.puts(session.auth_token(:wise))
|
78
|
+
f.puts(session.auth_token(:writely))
|
79
|
+
end
|
80
|
+
true
|
81
|
+
end
|
82
|
+
if !session.auth_token
|
83
|
+
session.on_auth_fail.call()
|
84
|
+
end
|
85
|
+
return session
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
module Util #:nodoc:
|
90
|
+
|
91
|
+
module_function
|
92
|
+
|
93
|
+
def encode_query(params)
|
94
|
+
return params.map(){ |k, v| CGI.escape(k) + "=" + CGI.escape(v) }.join("&")
|
95
|
+
end
|
96
|
+
|
97
|
+
def h(str)
|
98
|
+
return CGI.escapeHTML(str.to_s())
|
99
|
+
end
|
100
|
+
|
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
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
# Raised when spreadsheets.google.com has returned error.
|
113
|
+
class Error < RuntimeError
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
# Raised when GoogleSpreadsheet.login has failed.
|
119
|
+
class AuthenticationError < GoogleSpreadsheet::Error
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
|
124
|
+
# Use GoogleSpreadsheet.login or GoogleSpreadsheet.saved_session to get
|
125
|
+
# GoogleSpreadsheet::Session object.
|
126
|
+
class Session
|
127
|
+
|
128
|
+
include(Util)
|
129
|
+
extend(Util)
|
130
|
+
|
131
|
+
# The same as GoogleSpreadsheet.login.
|
132
|
+
def self.login(mail, password)
|
133
|
+
session = Session.new()
|
134
|
+
session.login(mail, password)
|
135
|
+
return session
|
136
|
+
end
|
137
|
+
|
138
|
+
# The same as GoogleSpreadsheet.login_with_oauth.
|
139
|
+
def self.login_with_oauth(oauth_token)
|
140
|
+
session = Session.new(nil, oauth_token)
|
141
|
+
end
|
142
|
+
|
143
|
+
# Restores session using return value of auth_tokens method of previous session.
|
144
|
+
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
|
150
|
+
end
|
151
|
+
|
152
|
+
# Authenticates with given +mail+ and +password+, and updates current session object
|
153
|
+
# if succeeds. Raises GoogleSpreadsheet::AuthenticationError if fails.
|
154
|
+
# Google Apps account is supported.
|
155
|
+
def login(mail, password)
|
156
|
+
begin
|
157
|
+
@auth_tokens = {}
|
158
|
+
authenticate(mail, password, :wise)
|
159
|
+
authenticate(mail, password, :writely)
|
160
|
+
rescue GoogleSpreadsheet::Error => ex
|
161
|
+
return true if @on_auth_fail && @on_auth_fail.call()
|
162
|
+
raise(AuthenticationError, "authentication failed for #{mail}: #{ex.message}")
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# Authentication tokens.
|
167
|
+
attr_reader(:auth_tokens)
|
168
|
+
|
169
|
+
# Authentication token.
|
170
|
+
def auth_token(auth = :wise)
|
171
|
+
return @auth_tokens[auth]
|
172
|
+
end
|
173
|
+
|
174
|
+
# Proc or Method called when authentication has failed.
|
175
|
+
# When this function returns +true+, it tries again.
|
176
|
+
attr_accessor :on_auth_fail
|
177
|
+
|
178
|
+
def auth_header(auth) #:nodoc:
|
179
|
+
if auth == :none
|
180
|
+
return {}
|
181
|
+
else
|
182
|
+
return {"Authorization" => "GoogleLogin auth=#{@auth_tokens[auth]}"}
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
# Returns list of spreadsheets for the user as array of GoogleSpreadsheet::Spreadsheet.
|
187
|
+
# You can specify query parameters described at
|
188
|
+
# http://code.google.com/apis/spreadsheets/docs/2.0/reference.html#Parameters
|
189
|
+
#
|
190
|
+
# e.g.
|
191
|
+
# session.spreadsheets
|
192
|
+
# session.spreadsheets("title" => "hoge")
|
193
|
+
def spreadsheets(params = {})
|
194
|
+
query = encode_query(params)
|
195
|
+
doc = request(:get, "https://spreadsheets.google.com/feeds/spreadsheets/private/full?#{query}")
|
196
|
+
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"])
|
201
|
+
result.push(Spreadsheet.new(self, url, title))
|
202
|
+
end
|
203
|
+
return result
|
204
|
+
end
|
205
|
+
|
206
|
+
# Returns GoogleSpreadsheet::Spreadsheet with given +key+.
|
207
|
+
#
|
208
|
+
# e.g.
|
209
|
+
# # http://spreadsheets.google.com/ccc?key=pz7XtlQC-PYx-jrVMJErTcg&hl=ja
|
210
|
+
# session.spreadsheet_by_key("pz7XtlQC-PYx-jrVMJErTcg")
|
211
|
+
def spreadsheet_by_key(key)
|
212
|
+
url = "https://spreadsheets.google.com/feeds/worksheets/#{key}/private/full"
|
213
|
+
return Spreadsheet.new(self, url)
|
214
|
+
end
|
215
|
+
|
216
|
+
# Returns GoogleSpreadsheet::Spreadsheet with given +url+. You must specify either of:
|
217
|
+
# - URL of the page you open to access the spreadsheet in your browser
|
218
|
+
# - URL of worksheet-based feed of the spreadseet
|
219
|
+
#
|
220
|
+
# e.g.
|
221
|
+
# session.spreadsheet_by_url(
|
222
|
+
# "http://spreadsheets.google.com/ccc?key=pz7XtlQC-PYx-jrVMJErTcg&hl=en")
|
223
|
+
# session.spreadsheet_by_url(
|
224
|
+
# "https://spreadsheets.google.com/feeds/worksheets/pz7XtlQC-PYx-jrVMJErTcg/private/full")
|
225
|
+
def spreadsheet_by_url(url)
|
226
|
+
# Tries to parse it as URL of human-readable spreadsheet.
|
227
|
+
uri = URI.parse(url)
|
228
|
+
if uri.host == "spreadsheets.google.com" && uri.path =~ /\/ccc$/
|
229
|
+
if (uri.query || "").split(/&/).find(){ |s| s=~ /^key=(.*)$/ }
|
230
|
+
return spreadsheet_by_key($1)
|
231
|
+
end
|
232
|
+
end
|
233
|
+
# Assumes the URL is worksheets feed URL.
|
234
|
+
return Spreadsheet.new(self, url)
|
235
|
+
end
|
236
|
+
|
237
|
+
# Returns GoogleSpreadsheet::Worksheet with given +url+.
|
238
|
+
# You must specify URL of cell-based feed of the worksheet.
|
239
|
+
#
|
240
|
+
# e.g.
|
241
|
+
# session.worksheet_by_url(
|
242
|
+
# "http://spreadsheets.google.com/feeds/cells/pz7XtlQC-PYxNmbBVgyiNWg/od6/private/full")
|
243
|
+
def worksheet_by_url(url)
|
244
|
+
return Worksheet.new(self, nil, url)
|
245
|
+
end
|
246
|
+
|
247
|
+
# Creates new spreadsheet and returns the new GoogleSpreadsheet::Spreadsheet.
|
248
|
+
#
|
249
|
+
# e.g.
|
250
|
+
# session.create_spreadsheet("My new sheet")
|
251
|
+
def create_spreadsheet(
|
252
|
+
title = "Untitled",
|
253
|
+
feed_url = "https://docs.google.com/feeds/documents/private/full")
|
254
|
+
xml = <<-"EOS"
|
255
|
+
<atom:entry xmlns:atom="http://www.w3.org/2005/Atom" xmlns:docs="http://schemas.google.com/docs/2007">
|
256
|
+
<atom:category scheme="http://schemas.google.com/g/2005#kind"
|
257
|
+
term="http://schemas.google.com/docs/2007#spreadsheet" label="spreadsheet"/>
|
258
|
+
<atom:title>#{h(title)}</atom:title>
|
259
|
+
</atom:entry>
|
260
|
+
EOS
|
261
|
+
|
262
|
+
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"])
|
265
|
+
return Spreadsheet.new(self, ss_url, title)
|
266
|
+
end
|
267
|
+
|
268
|
+
def request(method, url, params = {}) #:nodoc:
|
269
|
+
# Always uses HTTPS.
|
270
|
+
uri = URI.parse(url.gsub(%r{^http://}, "https://"))
|
271
|
+
data = params[:data]
|
272
|
+
auth = params[:auth] || :wise
|
273
|
+
if params[:header]
|
274
|
+
add_header = params[:header]
|
275
|
+
else
|
276
|
+
add_header = data ? {"Content-Type" => "application/atom+xml"} : {}
|
277
|
+
end
|
278
|
+
response_type = params[:response_type] || :xml
|
279
|
+
|
280
|
+
if @oauth_token
|
281
|
+
|
282
|
+
if method == :delete || method == :get
|
283
|
+
response = @oauth_token.__send__(method, url, add_header)
|
284
|
+
else
|
285
|
+
response = @oauth_token.__send__(method, url, data, add_header)
|
286
|
+
end
|
287
|
+
return convert_response(response, response_type)
|
288
|
+
|
289
|
+
else
|
290
|
+
|
291
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
292
|
+
http.use_ssl = true
|
293
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
294
|
+
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)
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
private
|
320
|
+
|
321
|
+
def convert_response(response, response_type)
|
322
|
+
case response_type
|
323
|
+
when :xml
|
324
|
+
return Hpricot.XML(response.body)
|
325
|
+
when :raw
|
326
|
+
return response.body
|
327
|
+
else
|
328
|
+
raise("unknown params[:response_type]: %s" % response_type)
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
def authenticate(mail, password, auth)
|
333
|
+
params = {
|
334
|
+
"accountType" => "HOSTED_OR_GOOGLE",
|
335
|
+
"Email" => mail,
|
336
|
+
"Passwd" => password,
|
337
|
+
"service" => auth.to_s(),
|
338
|
+
"source" => "Gimite-RubyGoogleSpreadsheet-1.00",
|
339
|
+
}
|
340
|
+
response = request(:post,
|
341
|
+
"https://www.google.com/accounts/ClientLogin",
|
342
|
+
:data => encode_query(params), :auth => :none, :header => {}, :response_type => :raw)
|
343
|
+
@auth_tokens[auth] = response.slice(/^Auth=(.*)$/, 1)
|
344
|
+
end
|
345
|
+
|
346
|
+
end
|
347
|
+
|
348
|
+
|
349
|
+
# Use methods in GoogleSpreadsheet::Session to get GoogleSpreadsheet::Spreadsheet object.
|
350
|
+
class Spreadsheet
|
351
|
+
|
352
|
+
include(Util)
|
353
|
+
|
354
|
+
def initialize(session, worksheets_feed_url, title = nil) #:nodoc:
|
355
|
+
@session = session
|
356
|
+
@worksheets_feed_url = worksheets_feed_url
|
357
|
+
@title = title
|
358
|
+
end
|
359
|
+
|
360
|
+
# URL of worksheet-based feed of the spreadsheet.
|
361
|
+
attr_reader(:worksheets_feed_url)
|
362
|
+
|
363
|
+
# Title of the spreadsheet. So far only available if you get this object by
|
364
|
+
# GoogleSpreadsheet::Session#spreadsheets.
|
365
|
+
attr_reader(:title)
|
366
|
+
|
367
|
+
# Key of the spreadsheet.
|
368
|
+
def key
|
369
|
+
if !(@worksheets_feed_url =~
|
370
|
+
%r{^https?://spreadsheets.google.com/feeds/worksheets/(.*)/private/full$})
|
371
|
+
raise(GoogleSpreadsheet::Error,
|
372
|
+
"worksheets feed URL is in unknown format: #{@worksheets_feed_url}")
|
373
|
+
end
|
374
|
+
return $1
|
375
|
+
end
|
376
|
+
|
377
|
+
# Tables feed URL of the spreadsheet.
|
378
|
+
def tables_feed_url
|
379
|
+
return "https://spreadsheets.google.com/feeds/#{self.key}/tables"
|
380
|
+
end
|
381
|
+
|
382
|
+
# URL of feed used in document list feed API.
|
383
|
+
def document_feed_url
|
384
|
+
return "https://docs.google.com/feeds/documents/private/full/spreadsheet%3A#{self.key}"
|
385
|
+
end
|
386
|
+
|
387
|
+
# Creates copy of this spreadsheet with the given name.
|
388
|
+
def duplicate(new_name = nil)
|
389
|
+
new_name ||= (@title ? "Copy of " + @title : "Untitled")
|
390
|
+
get_url = "https://spreadsheets.google.com/feeds/download/spreadsheets/Export?key=#{key}&exportFormat=ods"
|
391
|
+
ods = @session.request(:get, get_url, :response_type => :raw)
|
392
|
+
|
393
|
+
url = "https://docs.google.com/feeds/documents/private/full"
|
394
|
+
header = {
|
395
|
+
"Content-Type" => "application/x-vnd.oasis.opendocument.spreadsheet",
|
396
|
+
"Slug" => URI.encode(new_name),
|
397
|
+
}
|
398
|
+
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"])
|
401
|
+
return Spreadsheet.new(@session, ss_url, title)
|
402
|
+
end
|
403
|
+
|
404
|
+
# If +permanent+ is +false+, moves the spreadsheet to the trash.
|
405
|
+
# If +permanent+ is +true+, deletes the spreadsheet permanently.
|
406
|
+
def delete(permanent = false)
|
407
|
+
@session.request(:delete,
|
408
|
+
self.document_feed_url + (permanent ? "?delete=true" : ""),
|
409
|
+
:auth => :writely, :header => {"If-Match" => "*"})
|
410
|
+
end
|
411
|
+
|
412
|
+
# Returns worksheets of the spreadsheet as array of GoogleSpreadsheet::Worksheet.
|
413
|
+
def worksheets
|
414
|
+
doc = @session.request(:get, @worksheets_feed_url)
|
415
|
+
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"])
|
420
|
+
result.push(Worksheet.new(@session, self, url, title))
|
421
|
+
end
|
422
|
+
return result.freeze()
|
423
|
+
end
|
424
|
+
|
425
|
+
# Adds a new worksheet to the spreadsheet. Returns added GoogleSpreadsheet::Worksheet.
|
426
|
+
def add_worksheet(title, max_rows = 100, max_cols = 20)
|
427
|
+
xml = <<-"EOS"
|
428
|
+
<entry xmlns='http://www.w3.org/2005/Atom'
|
429
|
+
xmlns:gs='http://schemas.google.com/spreadsheets/2006'>
|
430
|
+
<title>#{h(title)}</title>
|
431
|
+
<gs:rowCount>#{h(max_rows)}</gs:rowCount>
|
432
|
+
<gs:colCount>#{h(max_cols)}</gs:colCount>
|
433
|
+
</entry>
|
434
|
+
EOS
|
435
|
+
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"])
|
438
|
+
return Worksheet.new(@session, self, url, title)
|
439
|
+
end
|
440
|
+
|
441
|
+
# Returns list of tables in the spreadsheet.
|
442
|
+
def tables
|
443
|
+
doc = @session.request(:get, self.tables_feed_url)
|
444
|
+
return doc.search("entry").map(){ |e| Table.new(@session, e) }.freeze()
|
445
|
+
end
|
446
|
+
|
447
|
+
end
|
448
|
+
|
449
|
+
# Use GoogleSpreadsheet::Worksheet#add_table to create table.
|
450
|
+
# Use GoogleSpreadsheet::Worksheet#tables to get GoogleSpreadsheet::Table objects.
|
451
|
+
class Table
|
452
|
+
|
453
|
+
include(Util)
|
454
|
+
|
455
|
+
def initialize(session, entry) #:nodoc:
|
456
|
+
@columns = {}
|
457
|
+
@worksheet_title = as_utf8(entry.search("gs:worksheet")[0]["name"])
|
458
|
+
@records_url = as_utf8(entry.search("content")[0]["src"])
|
459
|
+
@session = session
|
460
|
+
end
|
461
|
+
|
462
|
+
# Title of the worksheet the table belongs to.
|
463
|
+
attr_reader(:worksheet_title)
|
464
|
+
|
465
|
+
# Adds a record.
|
466
|
+
def add_record(values)
|
467
|
+
fields = ""
|
468
|
+
values.each do |name, value|
|
469
|
+
fields += "<gs:field name='#{h(name)}'>#{h(value)}</gs:field>"
|
470
|
+
end
|
471
|
+
xml =<<-EOS
|
472
|
+
<entry
|
473
|
+
xmlns="http://www.w3.org/2005/Atom"
|
474
|
+
xmlns:gs="http://schemas.google.com/spreadsheets/2006">
|
475
|
+
#{fields}
|
476
|
+
</entry>
|
477
|
+
EOS
|
478
|
+
@session.request(:post, @records_url, :data => xml)
|
479
|
+
end
|
480
|
+
|
481
|
+
# Returns records in the table.
|
482
|
+
def records
|
483
|
+
doc = @session.request(:get, @records_url)
|
484
|
+
return doc.search("entry").map(){ |e| Record.new(@session, e) }
|
485
|
+
end
|
486
|
+
|
487
|
+
end
|
488
|
+
|
489
|
+
# Use GoogleSpreadsheet::Table#records to get GoogleSpreadsheet::Record objects.
|
490
|
+
class Record < Hash
|
491
|
+
|
492
|
+
def initialize(session, entry) #:nodoc:
|
493
|
+
@session = session
|
494
|
+
for field in entry.search("gs:field")
|
495
|
+
self[as_utf8(field["name"])] = as_utf8(field.inner_text)
|
496
|
+
end
|
497
|
+
end
|
498
|
+
|
499
|
+
def inspect #:nodoc:
|
500
|
+
content = self.map(){ |k, v| "%p => %p" % [k, v] }.join(", ")
|
501
|
+
return "\#<%p:{%s}>" % [self.class, content]
|
502
|
+
end
|
503
|
+
|
504
|
+
end
|
505
|
+
|
506
|
+
# Use GoogleSpreadsheet::Spreadsheet#worksheets to get GoogleSpreadsheet::Worksheet object.
|
507
|
+
class Worksheet
|
508
|
+
|
509
|
+
include(Util)
|
510
|
+
|
511
|
+
def initialize(session, spreadsheet, cells_feed_url, title = nil) #:nodoc:
|
512
|
+
@session = session
|
513
|
+
@spreadsheet = spreadsheet
|
514
|
+
@cells_feed_url = cells_feed_url
|
515
|
+
@title = title
|
516
|
+
|
517
|
+
@cells = nil
|
518
|
+
@input_values = nil
|
519
|
+
@modified = Set.new()
|
520
|
+
end
|
521
|
+
|
522
|
+
# URL of cell-based feed of the worksheet.
|
523
|
+
attr_reader(:cells_feed_url)
|
524
|
+
|
525
|
+
# URL of worksheet feed URL of the worksheet.
|
526
|
+
def worksheet_feed_url
|
527
|
+
# I don't know good way to get worksheet feed URL from cells feed URL.
|
528
|
+
# Probably it would be cleaner to keep worksheet feed URL and get cells feed URL
|
529
|
+
# from it.
|
530
|
+
if !(@cells_feed_url =~
|
531
|
+
%r{^https?://spreadsheets.google.com/feeds/cells/(.*)/(.*)/private/full$})
|
532
|
+
raise(GoogleSpreadsheet::Error,
|
533
|
+
"cells feed URL is in unknown format: #{@cells_feed_url}")
|
534
|
+
end
|
535
|
+
return "https://spreadsheets.google.com/feeds/worksheets/#{$1}/private/full/#{$2}"
|
536
|
+
end
|
537
|
+
|
538
|
+
# GoogleSpreadsheet::Spreadsheet which this worksheet belongs to.
|
539
|
+
def spreadsheet
|
540
|
+
if !@spreadsheet
|
541
|
+
if !(@cells_feed_url =~
|
542
|
+
%r{^https?://spreadsheets.google.com/feeds/cells/(.*)/(.*)/private/full$})
|
543
|
+
raise(GoogleSpreadsheet::Error,
|
544
|
+
"cells feed URL is in unknown format: #{@cells_feed_url}")
|
545
|
+
end
|
546
|
+
@spreadsheet = @session.spreadsheet_by_key($1)
|
547
|
+
end
|
548
|
+
return @spreadsheet
|
549
|
+
end
|
550
|
+
|
551
|
+
# Returns content of the cell as String. Top-left cell is [1, 1].
|
552
|
+
def [](row, col)
|
553
|
+
return self.cells[[row, col]] || ""
|
554
|
+
end
|
555
|
+
|
556
|
+
# Updates content of the cell.
|
557
|
+
# Note that update is not sent to the server until you call save().
|
558
|
+
# Top-left cell is [1, 1].
|
559
|
+
#
|
560
|
+
# e.g.
|
561
|
+
# worksheet[2, 1] = "hoge"
|
562
|
+
# worksheet[1, 3] = "=A1+B1"
|
563
|
+
def []=(row, col, value)
|
564
|
+
reload() if !@cells
|
565
|
+
@cells[[row, col]] = value
|
566
|
+
@input_values[[row, col]] = value
|
567
|
+
@modified.add([row, col])
|
568
|
+
self.max_rows = row if row > @max_rows
|
569
|
+
self.max_cols = col if col > @max_cols
|
570
|
+
end
|
571
|
+
|
572
|
+
# Returns the value or the formula of the cell. Top-left cell is [1, 1].
|
573
|
+
#
|
574
|
+
# If user input "=A1+B1" to cell [1, 3], worksheet[1, 3] is "3" for example and
|
575
|
+
# worksheet.input_value(1, 3) is "=RC[-2]+RC[-1]".
|
576
|
+
def input_value(row, col)
|
577
|
+
reload() if !@cells
|
578
|
+
return @input_values[[row, col]] || ""
|
579
|
+
end
|
580
|
+
|
581
|
+
# Row number of the bottom-most non-empty row.
|
582
|
+
def num_rows
|
583
|
+
reload() if !@cells
|
584
|
+
return @cells.keys.map(){ |r, c| r }.max || 0
|
585
|
+
end
|
586
|
+
|
587
|
+
# Column number of the right-most non-empty column.
|
588
|
+
def num_cols
|
589
|
+
reload() if !@cells
|
590
|
+
return @cells.keys.map(){ |r, c| c }.max || 0
|
591
|
+
end
|
592
|
+
|
593
|
+
# Number of rows including empty rows.
|
594
|
+
def max_rows
|
595
|
+
reload() if !@cells
|
596
|
+
return @max_rows
|
597
|
+
end
|
598
|
+
|
599
|
+
# Updates number of rows.
|
600
|
+
# Note that update is not sent to the server until you call save().
|
601
|
+
def max_rows=(rows)
|
602
|
+
reload() if !@cells
|
603
|
+
@max_rows = rows
|
604
|
+
@meta_modified = true
|
605
|
+
end
|
606
|
+
|
607
|
+
# Number of columns including empty columns.
|
608
|
+
def max_cols
|
609
|
+
reload() if !@cells
|
610
|
+
return @max_cols
|
611
|
+
end
|
612
|
+
|
613
|
+
# Updates number of columns.
|
614
|
+
# Note that update is not sent to the server until you call save().
|
615
|
+
def max_cols=(cols)
|
616
|
+
reload() if !@cells
|
617
|
+
@max_cols = cols
|
618
|
+
@meta_modified = true
|
619
|
+
end
|
620
|
+
|
621
|
+
# Title of the worksheet (shown as tab label in Web interface).
|
622
|
+
def title
|
623
|
+
reload() if !@title
|
624
|
+
return @title
|
625
|
+
end
|
626
|
+
|
627
|
+
# Updates title of the worksheet.
|
628
|
+
# Note that update is not sent to the server until you call save().
|
629
|
+
def title=(title)
|
630
|
+
reload() if !@cells
|
631
|
+
@title = title
|
632
|
+
@meta_modified = true
|
633
|
+
end
|
634
|
+
|
635
|
+
def cells #:nodoc:
|
636
|
+
reload() if !@cells
|
637
|
+
return @cells
|
638
|
+
end
|
639
|
+
|
640
|
+
# An array of spreadsheet rows. Each row contains an array of
|
641
|
+
# columns. Note that resulting array is 0-origin so
|
642
|
+
# worksheet.rows[0][0] == worksheet[1, 1].
|
643
|
+
def rows(skip = 0)
|
644
|
+
nc = self.num_cols
|
645
|
+
result = ((1 + skip)..self.num_rows).map() do |row|
|
646
|
+
(1..nc).map(){ |col| self[row, col] }.freeze()
|
647
|
+
end
|
648
|
+
return result.freeze()
|
649
|
+
end
|
650
|
+
|
651
|
+
# Reloads content of the worksheets from the server.
|
652
|
+
# Note that changes you made by []= is discarded if you haven't called save().
|
653
|
+
def reload()
|
654
|
+
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)
|
658
|
+
|
659
|
+
@cells = {}
|
660
|
+
@input_values = {}
|
661
|
+
for entry in doc.search("entry")
|
662
|
+
cell = entry.search("gs:cell")[0]
|
663
|
+
row = cell["row"].to_i()
|
664
|
+
col = cell["col"].to_i()
|
665
|
+
@cells[[row, col]] = as_utf8(cell.inner_text)
|
666
|
+
@input_values[[row, col]] = as_utf8(cell["inputValue"])
|
667
|
+
end
|
668
|
+
@modified.clear()
|
669
|
+
@meta_modified = false
|
670
|
+
return true
|
671
|
+
end
|
672
|
+
|
673
|
+
# Saves your changes made by []=, etc. to the server.
|
674
|
+
def save()
|
675
|
+
sent = false
|
676
|
+
|
677
|
+
if @meta_modified
|
678
|
+
|
679
|
+
ws_doc = @session.request(:get, self.worksheet_feed_url)
|
680
|
+
edit_url = ws_doc.search("link[@rel='edit']")[0]["href"]
|
681
|
+
xml = <<-"EOS"
|
682
|
+
<entry xmlns='http://www.w3.org/2005/Atom'
|
683
|
+
xmlns:gs='http://schemas.google.com/spreadsheets/2006'>
|
684
|
+
<title>#{h(self.title)}</title>
|
685
|
+
<gs:rowCount>#{h(self.max_rows)}</gs:rowCount>
|
686
|
+
<gs:colCount>#{h(self.max_cols)}</gs:colCount>
|
687
|
+
</entry>
|
688
|
+
EOS
|
689
|
+
|
690
|
+
@session.request(:put, edit_url, :data => xml)
|
691
|
+
|
692
|
+
@meta_modified = false
|
693
|
+
sent = true
|
694
|
+
|
695
|
+
end
|
696
|
+
|
697
|
+
if !@modified.empty?
|
698
|
+
|
699
|
+
# Gets id and edit URL for each cell.
|
700
|
+
# Note that return-empty=true is required to get those info for empty cells.
|
701
|
+
cell_entries = {}
|
702
|
+
rows = @modified.map(){ |r, c| r }
|
703
|
+
cols = @modified.map(){ |r, c| c }
|
704
|
+
url = "#{@cells_feed_url}?return-empty=true&min-row=#{rows.min}&max-row=#{rows.max}" +
|
705
|
+
"&min-col=#{cols.min}&max-col=#{cols.max}"
|
706
|
+
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()
|
710
|
+
cell_entries[[row, col]] = entry
|
711
|
+
end
|
712
|
+
|
713
|
+
# Updates cell values using batch operation.
|
714
|
+
# If the data is large, we split it into multiple operations, otherwise batch may fail.
|
715
|
+
@modified.each_slice(250) do |chunk|
|
716
|
+
|
717
|
+
xml = <<-EOS
|
718
|
+
<feed xmlns="http://www.w3.org/2005/Atom"
|
719
|
+
xmlns:batch="http://schemas.google.com/gdata/batch"
|
720
|
+
xmlns:gs="http://schemas.google.com/spreadsheets/2006">
|
721
|
+
<id>#{h(@cells_feed_url)}</id>
|
722
|
+
EOS
|
723
|
+
for row, col in chunk
|
724
|
+
value = @cells[[row, col]]
|
725
|
+
entry = cell_entries[[row, col]]
|
726
|
+
id = entry.search("id").text
|
727
|
+
edit_url = entry.search("link[@rel='edit']")[0]["href"]
|
728
|
+
xml << <<-EOS
|
729
|
+
<entry>
|
730
|
+
<batch:id>#{h(row)},#{h(col)}</batch:id>
|
731
|
+
<batch:operation type="update"/>
|
732
|
+
<id>#{h(id)}</id>
|
733
|
+
<link rel="edit" type="application/atom+xml"
|
734
|
+
href="#{h(edit_url)}"/>
|
735
|
+
<gs:cell row="#{h(row)}" col="#{h(col)}" inputValue="#{h(value)}"/>
|
736
|
+
</entry>
|
737
|
+
EOS
|
738
|
+
end
|
739
|
+
xml << <<-"EOS"
|
740
|
+
</feed>
|
741
|
+
EOS
|
742
|
+
|
743
|
+
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]
|
746
|
+
if interrupted
|
747
|
+
raise(GoogleSpreadsheet::Error, "Update has failed: %s" %
|
748
|
+
interrupted["reason"])
|
749
|
+
end
|
750
|
+
if !(entry.search("batch:status")[0]["code"] =~ /^2/)
|
751
|
+
raise(GoogleSpreadsheet::Error, "Updating cell %s has failed: %s" %
|
752
|
+
[entry.search("atom:id").text, entry.search("batch:status")[0]["reason"]])
|
753
|
+
end
|
754
|
+
end
|
755
|
+
|
756
|
+
end
|
757
|
+
|
758
|
+
@modified.clear()
|
759
|
+
sent = true
|
760
|
+
|
761
|
+
end
|
762
|
+
return sent
|
763
|
+
end
|
764
|
+
|
765
|
+
# Calls save() and reload().
|
766
|
+
def synchronize()
|
767
|
+
save()
|
768
|
+
reload()
|
769
|
+
end
|
770
|
+
|
771
|
+
# Deletes this worksheet. Deletion takes effect right away without calling save().
|
772
|
+
def delete()
|
773
|
+
ws_doc = @session.request(:get, self.worksheet_feed_url)
|
774
|
+
edit_url = ws_doc.search("link[@rel='edit']")[0]["href"]
|
775
|
+
@session.request(:delete, edit_url)
|
776
|
+
end
|
777
|
+
|
778
|
+
# Returns true if you have changes made by []= which haven't been saved.
|
779
|
+
def dirty?
|
780
|
+
return !@modified.empty?
|
781
|
+
end
|
782
|
+
|
783
|
+
# Creates table for the worksheet and returns GoogleSpreadsheet::Table.
|
784
|
+
# See this document for details:
|
785
|
+
# http://code.google.com/intl/en/apis/spreadsheets/docs/3.0/developers_guide_protocol.html#TableFeeds
|
786
|
+
def add_table(table_title, summary, columns)
|
787
|
+
column_xml = ""
|
788
|
+
columns.each do |index, name|
|
789
|
+
column_xml += "<gs:column index='#{h(index)}' name='#{h(name)}'/>\n"
|
790
|
+
end
|
791
|
+
|
792
|
+
xml = <<-"EOS"
|
793
|
+
<entry xmlns="http://www.w3.org/2005/Atom"
|
794
|
+
xmlns:gs="http://schemas.google.com/spreadsheets/2006">
|
795
|
+
<title type='text'>#{h(table_title)}</title>
|
796
|
+
<summary type='text'>#{h(summary)}</summary>
|
797
|
+
<gs:worksheet name='#{h(self.title)}' />
|
798
|
+
<gs:header row='1' />
|
799
|
+
<gs:data numRows='0' startRow='2'>
|
800
|
+
#{column_xml}
|
801
|
+
</gs:data>
|
802
|
+
</entry>
|
803
|
+
EOS
|
804
|
+
|
805
|
+
result = @session.request(:post, self.spreadsheet.tables_feed_url, :data => xml)
|
806
|
+
return Table.new(@session, result)
|
807
|
+
end
|
808
|
+
|
809
|
+
# Returns list of tables for the workwheet.
|
810
|
+
def tables
|
811
|
+
return self.spreadsheet.tables.select(){ |t| t.worksheet_title == self.title }
|
812
|
+
end
|
813
|
+
|
814
|
+
end
|
815
|
+
|
816
|
+
|
817
|
+
end
|
data/script/gen-rdoc
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__) + "/../lib")
|
2
|
+
require "test/unit"
|
3
|
+
require "google_spreadsheet"
|
4
|
+
require "rubygems"
|
5
|
+
require "highline"
|
6
|
+
|
7
|
+
|
8
|
+
class TC_GoogleSpreadsheet < Test::Unit::TestCase
|
9
|
+
|
10
|
+
USE_SAVED_SESSION = false
|
11
|
+
|
12
|
+
def test_all()
|
13
|
+
puts("This test will create spreadsheets with your account, read/write them")
|
14
|
+
puts("and finally delete them (if everything goes well).")
|
15
|
+
if USE_SAVED_SESSION
|
16
|
+
session = GoogleSpreadsheet.saved_session
|
17
|
+
else
|
18
|
+
highline = HighLine.new()
|
19
|
+
mail = highline.ask("Mail: ")
|
20
|
+
password = highline.ask("Password: "){ |q| q.echo = false }
|
21
|
+
session = GoogleSpreadsheet.login(mail, password)
|
22
|
+
end
|
23
|
+
|
24
|
+
ss_title = "google-spreadsheet-ruby test " + Time.now.strftime("%Y-%m-%d-%H-%M-%S")
|
25
|
+
ss = session.create_spreadsheet(ss_title)
|
26
|
+
assert_equal(ss_title, ss.title)
|
27
|
+
|
28
|
+
ws = ss.worksheets[0]
|
29
|
+
assert_equal(ss.worksheets_feed_url, ws.spreadsheet.worksheets_feed_url)
|
30
|
+
ws.title = "hoge"
|
31
|
+
ws.max_rows = 20
|
32
|
+
ws.max_cols = 10
|
33
|
+
ws[1, 1] = "3"
|
34
|
+
ws[1, 2] = "5"
|
35
|
+
ws[1, 3] = "=A1+B1"
|
36
|
+
assert_equal(20, ws.max_rows)
|
37
|
+
assert_equal(10, ws.max_cols)
|
38
|
+
assert_equal(1, ws.num_rows)
|
39
|
+
assert_equal(3, ws.num_cols)
|
40
|
+
assert_equal("3", ws[1, 1])
|
41
|
+
assert_equal("5", ws[1, 2])
|
42
|
+
ws.save()
|
43
|
+
|
44
|
+
ws.reload()
|
45
|
+
assert_equal(20, ws.max_rows)
|
46
|
+
assert_equal(10, ws.max_cols)
|
47
|
+
assert_equal(1, ws.num_rows)
|
48
|
+
assert_equal(3, ws.num_cols)
|
49
|
+
assert_equal("3", ws[1, 1])
|
50
|
+
assert_equal("5", ws[1, 2])
|
51
|
+
assert_equal("8", ws[1, 3])
|
52
|
+
|
53
|
+
ss2 = session.spreadsheet_by_key(ss.key)
|
54
|
+
assert_equal(ss.worksheets_feed_url, ss2.worksheets_feed_url)
|
55
|
+
assert_equal("hoge", ss2.worksheets[0].title)
|
56
|
+
assert_equal("3", ss2.worksheets[0][1, 1])
|
57
|
+
ss3 = session.spreadsheet_by_url("http://spreadsheets.google.com/ccc?key=#{ss.key}&hl=en")
|
58
|
+
assert_equal(ss.worksheets_feed_url, ss3.worksheets_feed_url)
|
59
|
+
ss4 = session.spreadsheet_by_url(ss.worksheets_feed_url)
|
60
|
+
assert_equal(ss.worksheets_feed_url, ss4.worksheets_feed_url)
|
61
|
+
|
62
|
+
assert_not_nil(session.spreadsheets.find(){ |s| s.title == ss_title })
|
63
|
+
assert_not_nil(session.spreadsheets("title" => ss_title).
|
64
|
+
find(){ |s| s.title == ss_title })
|
65
|
+
|
66
|
+
ws2 = session.worksheet_by_url(ws.cells_feed_url)
|
67
|
+
assert_equal(ws.cells_feed_url, ws2.cells_feed_url)
|
68
|
+
assert_equal("hoge", ws2.title)
|
69
|
+
|
70
|
+
ss_copy_title = "google-spreadsheet-ruby test copy " + Time.now.strftime("%Y-%m-%d-%H-%M-%S")
|
71
|
+
ss_copy = ss.duplicate(ss_copy_title)
|
72
|
+
assert_not_nil(session.spreadsheets("title" => ss_copy_title).
|
73
|
+
find(){ |s| s.title == ss_copy_title })
|
74
|
+
assert_equal("3", ss_copy.worksheets[0][1, 1])
|
75
|
+
|
76
|
+
ss.delete()
|
77
|
+
assert_nil(session.spreadsheets("title" => ss_title).
|
78
|
+
find(){ |s| s.title == ss_title })
|
79
|
+
ss_copy.delete(true)
|
80
|
+
assert_nil(session.spreadsheets("title" => ss_copy_title).
|
81
|
+
find(){ |s| s.title == ss_copy_title })
|
82
|
+
ss.delete(true)
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
metadata
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: wolframarnold-google-spreadsheet-ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Hiroshi Ichikawa
|
8
|
+
- Wolfram Arnold
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2010-02-02 00:00:00 -08:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: hpricot
|
18
|
+
type: :runtime
|
19
|
+
version_requirement:
|
20
|
+
version_requirements: !ruby/object:Gem::Requirement
|
21
|
+
requirements:
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: "0.3"
|
25
|
+
version:
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: oauth
|
28
|
+
type: :runtime
|
29
|
+
version_requirement:
|
30
|
+
version_requirements: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 0.3.6
|
35
|
+
version:
|
36
|
+
description: This is a library to read/write Google Spreadsheet.
|
37
|
+
email:
|
38
|
+
- gimite+github@gmail.com
|
39
|
+
- wolfram@rubyfocus.biz
|
40
|
+
executables: []
|
41
|
+
|
42
|
+
extensions: []
|
43
|
+
|
44
|
+
extra_rdoc_files:
|
45
|
+
- README.rdoc
|
46
|
+
files:
|
47
|
+
- README.rdoc
|
48
|
+
- LICENSE
|
49
|
+
- init.rb
|
50
|
+
- lib/google_spreadsheet.rb
|
51
|
+
- script/gen-rdoc
|
52
|
+
- test/test_google_spreadsheet.rb
|
53
|
+
has_rdoc: true
|
54
|
+
homepage: http://github.com/wolframarnold/google-spreadsheet-ruby/tree/master
|
55
|
+
licenses: []
|
56
|
+
|
57
|
+
post_install_message:
|
58
|
+
rdoc_options:
|
59
|
+
- --main
|
60
|
+
- README.rdoc
|
61
|
+
require_paths:
|
62
|
+
- lib
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: "0"
|
68
|
+
version:
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: "0"
|
74
|
+
version:
|
75
|
+
requirements: []
|
76
|
+
|
77
|
+
rubyforge_project:
|
78
|
+
rubygems_version: 1.3.5
|
79
|
+
signing_key:
|
80
|
+
specification_version: 3
|
81
|
+
summary: This is a library to read/write Google Spreadsheet.
|
82
|
+
test_files:
|
83
|
+
- test/test_google_spreadsheet.rb
|