wolframarnold-google-spreadsheet-ruby 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|