google_drive 2.1.8 → 2.1.9
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.
- checksums.yaml +4 -4
- data/lib/google_drive.rb +9 -4
- data/lib/google_drive/acl.rb +16 -11
- data/lib/google_drive/acl_entry.rb +18 -16
- data/lib/google_drive/api_client_fetcher.rb +3 -3
- data/lib/google_drive/collection.rb +50 -39
- data/lib/google_drive/config.rb +2 -2
- data/lib/google_drive/file.rb +35 -25
- data/lib/google_drive/list.rb +3 -1
- data/lib/google_drive/list_row.rb +11 -10
- data/lib/google_drive/response_code_error.rb +4 -1
- data/lib/google_drive/session.rb +191 -122
- data/lib/google_drive/spreadsheet.rb +34 -24
- data/lib/google_drive/util.rb +44 -25
- data/lib/google_drive/worksheet.rb +126 -53
- metadata +4 -4
@@ -12,12 +12,13 @@ require 'google_drive/file'
|
|
12
12
|
module GoogleDrive
|
13
13
|
# A spreadsheet.
|
14
14
|
#
|
15
|
-
# e.g., Use methods spreadsheet_by_title, spreadsheet_by_url,
|
16
|
-
# to get GoogleDrive::Spreadsheet
|
15
|
+
# e.g., Use methods spreadsheet_by_title, spreadsheet_by_url,
|
16
|
+
# create_spreadsheet in GoogleDrive::Session to get GoogleDrive::Spreadsheet
|
17
|
+
# object.
|
17
18
|
class Spreadsheet < GoogleDrive::File
|
18
19
|
include(Util)
|
19
20
|
|
20
|
-
SUPPORTED_EXPORT_FORMAT = Set.new(%w
|
21
|
+
SUPPORTED_EXPORT_FORMAT = Set.new(%w[xlsx csv pdf])
|
21
22
|
|
22
23
|
# Key of the spreadsheet.
|
23
24
|
def key
|
@@ -26,13 +27,15 @@ module GoogleDrive
|
|
26
27
|
|
27
28
|
# URL of worksheet-based feed of the spreadsheet.
|
28
29
|
def worksheets_feed_url
|
29
|
-
|
30
|
-
id
|
30
|
+
format(
|
31
|
+
'https://spreadsheets.google.com/feeds/worksheets/%s/private/full', id
|
32
|
+
)
|
31
33
|
end
|
32
34
|
|
33
35
|
# URL of feed used in the deprecated document list feed API.
|
34
36
|
def document_feed_url
|
35
|
-
'https://docs.google.com/feeds/documents/private/full/' +
|
37
|
+
'https://docs.google.com/feeds/documents/private/full/' +
|
38
|
+
CGI.escape(resource_id)
|
36
39
|
end
|
37
40
|
|
38
41
|
# Spreadsheet feed URL of the spreadsheet.
|
@@ -44,17 +47,20 @@ module GoogleDrive
|
|
44
47
|
def worksheets
|
45
48
|
doc = @session.request(:get, worksheets_feed_url)
|
46
49
|
if doc.root.name != 'feed'
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
+
raise(GoogleDrive::Error,
|
51
|
+
format(
|
52
|
+
"%s doesn't look like a worksheets feed URL because its root " \
|
53
|
+
'is not <feed>.',
|
54
|
+
worksheets_feed_url
|
55
|
+
))
|
50
56
|
end
|
51
57
|
doc.css('entry').map { |e| Worksheet.new(@session, self, e) }.freeze
|
52
58
|
end
|
53
59
|
|
54
60
|
# Returns a GoogleDrive::Worksheet with the given title in the spreadsheet.
|
55
61
|
#
|
56
|
-
# Returns nil if not found. Returns the first one when multiple worksheets
|
57
|
-
# title are found.
|
62
|
+
# Returns nil if not found. Returns the first one when multiple worksheets
|
63
|
+
# with the title are found.
|
58
64
|
def worksheet_by_title(title)
|
59
65
|
worksheets.find { |ws| ws.title == title }
|
60
66
|
end
|
@@ -67,7 +73,8 @@ module GoogleDrive
|
|
67
73
|
worksheets.find { |ws| ws.gid == gid }
|
68
74
|
end
|
69
75
|
|
70
|
-
# Adds a new worksheet to the spreadsheet. Returns added
|
76
|
+
# Adds a new worksheet to the spreadsheet. Returns added
|
77
|
+
# GoogleDrive::Worksheet.
|
71
78
|
def add_worksheet(title, max_rows = 100, max_cols = 20)
|
72
79
|
xml = <<-"EOS"
|
73
80
|
<entry xmlns='http://www.w3.org/2005/Atom'
|
@@ -82,27 +89,30 @@ module GoogleDrive
|
|
82
89
|
end
|
83
90
|
|
84
91
|
# Not available for GoogleDrive::Spreadsheet. Use export_as_file instead.
|
85
|
-
def download_to_file(
|
92
|
+
def download_to_file(_path, _params = {})
|
86
93
|
raise(
|
87
|
-
|
88
|
-
|
89
|
-
|
94
|
+
NotImplementedError,
|
95
|
+
'download_to_file is not available for GoogleDrive::Spreadsheet. ' \
|
96
|
+
'Use export_as_file instead.'
|
97
|
+
)
|
90
98
|
end
|
91
99
|
|
92
100
|
# Not available for GoogleDrive::Spreadsheet. Use export_as_string instead.
|
93
|
-
def download_to_string(
|
101
|
+
def download_to_string(_params = {})
|
94
102
|
raise(
|
95
|
-
|
96
|
-
|
97
|
-
|
103
|
+
NotImplementedError,
|
104
|
+
'download_to_string is not available for GoogleDrive::Spreadsheet. ' \
|
105
|
+
'Use export_as_string instead.'
|
106
|
+
)
|
98
107
|
end
|
99
108
|
|
100
109
|
# Not available for GoogleDrive::Spreadsheet. Use export_to_io instead.
|
101
|
-
def download_to_io(
|
110
|
+
def download_to_io(_io, _params = {})
|
102
111
|
raise(
|
103
|
-
|
104
|
-
|
105
|
-
|
112
|
+
NotImplementedError,
|
113
|
+
'download_to_io is not available for GoogleDrive::Spreadsheet. ' \
|
114
|
+
'Use export_to_io instead.'
|
115
|
+
)
|
106
116
|
end
|
107
117
|
end
|
108
118
|
end
|
data/lib/google_drive/util.rb
CHANGED
@@ -28,7 +28,7 @@ module GoogleDrive
|
|
28
28
|
'.html' => 'text/html',
|
29
29
|
'.zip' => 'application/zip',
|
30
30
|
'.swf' => 'application/x-shockwave-flash'
|
31
|
-
}
|
31
|
+
}.freeze
|
32
32
|
|
33
33
|
IMPORTABLE_CONTENT_TYPE_MAP = {
|
34
34
|
'application/x-vnd.oasis.opendocument.presentation' => 'application/vnd.google-apps.presentation',
|
@@ -74,19 +74,22 @@ module GoogleDrive
|
|
74
74
|
'text/csv' => 'application/vnd.google-apps.spreadsheet',
|
75
75
|
'application/vnd.oasis.opendocument.presentation' => 'application/vnd.google-apps.presentation',
|
76
76
|
'image/jpg' => 'application/vnd.google-apps.document',
|
77
|
-
'text/richtext' => 'application/vnd.google-apps.document'
|
78
|
-
}
|
77
|
+
'text/richtext' => 'application/vnd.google-apps.document'
|
78
|
+
}.freeze
|
79
79
|
|
80
80
|
module_function
|
81
81
|
|
82
82
|
def encode_query(params)
|
83
|
-
params
|
83
|
+
params
|
84
|
+
.map { |k, v| CGI.escape(k.to_s) + '=' + CGI.escape(v.to_s) }
|
85
|
+
.join('&')
|
84
86
|
end
|
85
87
|
|
86
88
|
def concat_url(url, piece)
|
87
89
|
(url_base, url_query) = url.split(/\?/, 2)
|
88
90
|
(piece_base, piece_query) = piece.split(/\?/, 2)
|
89
|
-
result_query =
|
91
|
+
result_query =
|
92
|
+
[url_query, piece_query].select { |s| s && !s.empty? }.join('&')
|
90
93
|
(url_base || '') +
|
91
94
|
(piece_base || '') +
|
92
95
|
(result_query.empty? ? '' : "?#{result_query}")
|
@@ -101,40 +104,52 @@ module GoogleDrive
|
|
101
104
|
case arg
|
102
105
|
|
103
106
|
when String
|
104
|
-
|
107
|
+
arg
|
105
108
|
|
106
109
|
when Array
|
107
110
|
if arg[0].scan(/\?/).size != arg.size - 1
|
108
|
-
|
111
|
+
raise(
|
109
112
|
ArgumentError,
|
110
|
-
|
113
|
+
format(
|
114
|
+
"The number of placeholders doesn't match the number of " \
|
115
|
+
'arguments: %p',
|
116
|
+
arg
|
117
|
+
)
|
118
|
+
)
|
111
119
|
end
|
112
120
|
i = 1
|
113
|
-
|
121
|
+
arg[0].gsub(/\?/) do
|
114
122
|
v = arg[i]
|
115
123
|
i += 1
|
116
124
|
case v
|
117
125
|
when String
|
118
|
-
"'%s'"
|
126
|
+
format("'%s'", v.gsub(/['\\]/) { '\\' + $& })
|
119
127
|
when Time
|
120
|
-
"'%s'"
|
128
|
+
format("'%s'", v.iso8601)
|
121
129
|
when TrueClass
|
122
130
|
'true'
|
123
131
|
when FalseClass
|
124
132
|
'false'
|
125
133
|
else
|
126
|
-
|
134
|
+
raise(
|
135
|
+
ArgumentError,
|
136
|
+
format('Expected String, Time, true or false, but got %p', v)
|
137
|
+
)
|
127
138
|
end
|
128
139
|
end
|
129
140
|
|
130
141
|
else
|
131
|
-
|
142
|
+
raise(
|
143
|
+
ArgumentError, format('Expected String or Array, but got %p', arg)
|
144
|
+
)
|
132
145
|
|
133
146
|
end
|
134
147
|
end
|
135
148
|
|
136
149
|
def construct_and_query(args)
|
137
|
-
args
|
150
|
+
args
|
151
|
+
.select { |a| a }.map { |a| format('(%s)', construct_query(a)) }
|
152
|
+
.join(' and ')
|
138
153
|
end
|
139
154
|
|
140
155
|
def convert_params(params)
|
@@ -180,21 +195,26 @@ module GoogleDrive
|
|
180
195
|
when 'showdeleted'
|
181
196
|
old_terms.push('trashed = false') if v.to_s == 'false'
|
182
197
|
when 'ocr', 'targetLanguage', 'sourceLanguage'
|
183
|
-
|
198
|
+
raise(
|
199
|
+
ArgumentError, format("'%s' parameter is no longer supported.", k)
|
200
|
+
)
|
184
201
|
else
|
185
202
|
# e.g., 'pageToken' -> :page_token
|
186
|
-
new_key = k
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
203
|
+
new_key = k
|
204
|
+
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
205
|
+
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
206
|
+
.downcase
|
207
|
+
.intern
|
191
208
|
new_params[new_key] = v
|
192
209
|
end
|
193
210
|
end
|
194
211
|
|
195
212
|
unless old_terms.empty?
|
196
213
|
if new_params.key?(:q)
|
197
|
-
|
214
|
+
raise(
|
215
|
+
ArgumentError,
|
216
|
+
"Cannot specify both 'q' parameter and old query parameters."
|
217
|
+
)
|
198
218
|
else
|
199
219
|
new_params[:q] = construct_and_query(old_terms)
|
200
220
|
end
|
@@ -213,10 +233,9 @@ module GoogleDrive
|
|
213
233
|
sc = get_singleton_class(obj)
|
214
234
|
names = api_obj.public_methods(false) - exceptions
|
215
235
|
names.each do |name|
|
216
|
-
if
|
217
|
-
|
218
|
-
|
219
|
-
end
|
236
|
+
next if name.to_s =~ /=$/
|
237
|
+
sc.__send__(:define_method, name) do
|
238
|
+
api_obj.__send__(name)
|
220
239
|
end
|
221
240
|
end
|
222
241
|
end
|
@@ -11,7 +11,8 @@ require 'google_drive/list'
|
|
11
11
|
|
12
12
|
module GoogleDrive
|
13
13
|
# A worksheet (i.e. a tab) in a spreadsheet.
|
14
|
-
# Use GoogleDrive::Spreadsheet#worksheets to get GoogleDrive::Worksheet
|
14
|
+
# Use GoogleDrive::Spreadsheet#worksheets to get GoogleDrive::Worksheet
|
15
|
+
# object.
|
15
16
|
class Worksheet
|
16
17
|
include(Util)
|
17
18
|
|
@@ -19,7 +20,7 @@ module GoogleDrive
|
|
19
20
|
# A regexp which matches an invalid character in XML 1.0:
|
20
21
|
# https://en.wikipedia.org/wiki/Valid_characters_in_XML#XML_1.0
|
21
22
|
XML_INVAILD_CHAR_REGEXP =
|
22
|
-
|
23
|
+
/[^\u0009\u000a\u000d\u0020-\ud7ff\ue000-\ufffd\u{10000}-\u{10ffff}]/
|
23
24
|
|
24
25
|
# @api private
|
25
26
|
def initialize(session, spreadsheet, worksheet_feed_entry)
|
@@ -46,7 +47,8 @@ module GoogleDrive
|
|
46
47
|
# URL of cell-based feed of the worksheet.
|
47
48
|
def cells_feed_url
|
48
49
|
@worksheet_feed_entry.css(
|
49
|
-
"link[rel='http://schemas.google.com/spreadsheets/2006#cellsfeed']"
|
50
|
+
"link[rel='http://schemas.google.com/spreadsheets/2006#cellsfeed']"
|
51
|
+
)[0]['href']
|
50
52
|
end
|
51
53
|
|
52
54
|
# URL of worksheet feed URL of the worksheet.
|
@@ -57,7 +59,8 @@ module GoogleDrive
|
|
57
59
|
# URL to export the worksheet as CSV.
|
58
60
|
def csv_export_url
|
59
61
|
@worksheet_feed_entry.css(
|
60
|
-
"link[rel='http://schemas.google.com/spreadsheets/2006#exportcsv']"
|
62
|
+
"link[rel='http://schemas.google.com/spreadsheets/2006#exportcsv']"
|
63
|
+
)[0]['href']
|
61
64
|
end
|
62
65
|
|
63
66
|
# Exports the worksheet as String in CSV format.
|
@@ -79,15 +82,17 @@ module GoogleDrive
|
|
79
82
|
|
80
83
|
# URL to view/edit the worksheet in a Web browser.
|
81
84
|
def human_url
|
82
|
-
"%s\#gid=%s"
|
85
|
+
format("%s\#gid=%s", spreadsheet.human_url, gid)
|
83
86
|
end
|
84
87
|
|
85
88
|
# GoogleDrive::Spreadsheet which this worksheet belongs to.
|
86
89
|
def spreadsheet
|
87
90
|
unless @spreadsheet
|
88
|
-
unless worksheet_feed_url =~
|
89
|
-
|
90
|
-
|
91
|
+
unless worksheet_feed_url =~
|
92
|
+
%r{https?://spreadsheets\.google\.com/feeds/worksheets/(.*)/(.*)$}
|
93
|
+
raise(GoogleDrive::Error,
|
94
|
+
'Worksheet feed URL is in unknown format: ' \
|
95
|
+
"#{worksheet_feed_url}")
|
91
96
|
end
|
92
97
|
@spreadsheet = @session.file_by_id(Regexp.last_match(1))
|
93
98
|
end
|
@@ -106,8 +111,9 @@ module GoogleDrive
|
|
106
111
|
end
|
107
112
|
|
108
113
|
# Updates content of the cell.
|
109
|
-
# Arguments in the bracket must be either (row number, column number) or
|
110
|
-
# Note that update is not sent to the server until you call
|
114
|
+
# Arguments in the bracket must be either (row number, column number) or
|
115
|
+
# cell name. Note that update is not sent to the server until you call
|
116
|
+
# save().
|
111
117
|
# Top-left cell is [1, 1].
|
112
118
|
#
|
113
119
|
# e.g.
|
@@ -163,12 +169,15 @@ module GoogleDrive
|
|
163
169
|
# (row number, column number) or cell name. Top-left cell is [1, 1].
|
164
170
|
#
|
165
171
|
# e.g.
|
166
|
-
# worksheet[1, 3]
|
167
|
-
#
|
172
|
+
# worksheet[1, 3]
|
173
|
+
# #=> "3,0" # it depends on locale, currency...
|
174
|
+
# worksheet.numeric_value(1, 3)
|
175
|
+
# #=> 3.0
|
168
176
|
#
|
169
177
|
# Returns nil if the cell is empty or contains non-number.
|
170
178
|
#
|
171
|
-
# If you modify the cell, its numeric_value is nil until you call save()
|
179
|
+
# If you modify the cell, its numeric_value is nil until you call save()
|
180
|
+
# and reload().
|
172
181
|
#
|
173
182
|
# For details, see:
|
174
183
|
# https://developers.google.com/google-apps/spreadsheets/#working_with_cell-based_feeds
|
@@ -183,7 +192,12 @@ module GoogleDrive
|
|
183
192
|
reload_cells unless @cells
|
184
193
|
# Memoizes it because this can be bottle-neck.
|
185
194
|
# https://github.com/gimite/google-drive-ruby/pull/49
|
186
|
-
@num_rows ||=
|
195
|
+
@num_rows ||=
|
196
|
+
@input_values
|
197
|
+
.reject { |(_r, _c), v| v.empty? }
|
198
|
+
.map { |(r, _c), _v| r }
|
199
|
+
.max ||
|
200
|
+
0
|
187
201
|
end
|
188
202
|
|
189
203
|
# Column number of the right-most non-empty column.
|
@@ -191,7 +205,12 @@ module GoogleDrive
|
|
191
205
|
reload_cells unless @cells
|
192
206
|
# Memoizes it because this can be bottle-neck.
|
193
207
|
# https://github.com/gimite/google-drive-ruby/pull/49
|
194
|
-
@num_cols ||=
|
208
|
+
@num_cols ||=
|
209
|
+
@input_values
|
210
|
+
.reject { |(_r, _c), v| v.empty? }
|
211
|
+
.map { |(_r, c), _v| c }
|
212
|
+
.max ||
|
213
|
+
0
|
195
214
|
end
|
196
215
|
|
197
216
|
# Number of rows including empty rows.
|
@@ -257,7 +276,7 @@ module GoogleDrive
|
|
257
276
|
self.max_rows += rows.size
|
258
277
|
num_rows.downto(row_num) do |r|
|
259
278
|
(1..num_cols).each do |c|
|
260
|
-
self[r + rows.size, c] =
|
279
|
+
self[r + rows.size, c] = input_value(r, c)
|
261
280
|
end
|
262
281
|
end
|
263
282
|
|
@@ -281,18 +300,19 @@ module GoogleDrive
|
|
281
300
|
# worksheet contains inter-cell reference.
|
282
301
|
def delete_rows(row_num, rows)
|
283
302
|
if row_num + rows - 1 > self.max_rows
|
284
|
-
|
303
|
+
raise(ArgumentError, 'The row number is out of range')
|
285
304
|
end
|
286
305
|
for r in row_num..(self.max_rows - rows)
|
287
306
|
for c in 1..num_cols
|
288
|
-
self[r, c] =
|
307
|
+
self[r, c] = input_value(r + rows, c)
|
289
308
|
end
|
290
309
|
end
|
291
310
|
self.max_rows -= rows
|
292
311
|
end
|
293
312
|
|
294
313
|
# Reloads content of the worksheets from the server.
|
295
|
-
# Note that changes you made by []= etc. is discarded if you haven't called
|
314
|
+
# Note that changes you made by []= etc. is discarded if you haven't called
|
315
|
+
# save().
|
296
316
|
def reload
|
297
317
|
set_worksheet_feed_entry(@session.request(:get, worksheet_feed_url).root)
|
298
318
|
reload_cells
|
@@ -316,24 +336,30 @@ module GoogleDrive
|
|
316
336
|
EOS
|
317
337
|
|
318
338
|
result = @session.request(
|
319
|
-
:put,
|
320
|
-
|
339
|
+
:put,
|
340
|
+
edit_url,
|
341
|
+
data: xml,
|
342
|
+
header: {
|
343
|
+
'Content-Type' => 'application/atom+xml;charset=utf-8',
|
344
|
+
'If-Match' => '*'
|
345
|
+
}
|
346
|
+
)
|
321
347
|
set_worksheet_feed_entry(result.root)
|
322
348
|
|
323
349
|
sent = true
|
324
|
-
|
325
350
|
end
|
326
351
|
|
327
352
|
unless @modified.empty?
|
328
|
-
|
329
353
|
# Gets id and edit URL for each cell.
|
330
354
|
# Note that return-empty=true is required to get those info for empty cells.
|
331
355
|
cell_entries = {}
|
332
356
|
rows = @modified.map { |r, _c| r }
|
333
357
|
cols = @modified.map { |_r, c| c }
|
334
|
-
url = concat_url(
|
335
|
-
|
336
|
-
|
358
|
+
url = concat_url(
|
359
|
+
cells_feed_url,
|
360
|
+
"?return-empty=true&min-row=#{rows.min}&max-row=#{rows.max}" \
|
361
|
+
"&min-col=#{cols.min}&max-col=#{cols.max}"
|
362
|
+
)
|
337
363
|
doc = @session.request(:get, url)
|
338
364
|
|
339
365
|
doc.css('entry').each do |entry|
|
@@ -354,8 +380,13 @@ module GoogleDrive
|
|
354
380
|
id = entry.css('id').text
|
355
381
|
edit_link = entry.css("link[rel='edit']")[0]
|
356
382
|
unless edit_link
|
357
|
-
|
358
|
-
|
383
|
+
raise(
|
384
|
+
GoogleDrive::Error,
|
385
|
+
format(
|
386
|
+
"The user doesn't have write permission to the spreadsheet: %p",
|
387
|
+
spreadsheet
|
388
|
+
)
|
389
|
+
)
|
359
390
|
end
|
360
391
|
edit_url = edit_link['href']
|
361
392
|
xml << <<-EOS
|
@@ -363,9 +394,14 @@ module GoogleDrive
|
|
363
394
|
<batch:id>#{h(row)},#{h(col)}</batch:id>
|
364
395
|
<batch:operation type="update"/>
|
365
396
|
<id>#{h(id)}</id>
|
366
|
-
<link
|
397
|
+
<link
|
398
|
+
rel="edit"
|
399
|
+
type="application/atom+xml"
|
367
400
|
href="#{h(edit_url)}"/>
|
368
|
-
<gs:cell
|
401
|
+
<gs:cell
|
402
|
+
row="#{h(row)}"
|
403
|
+
col="#{h(col)}"
|
404
|
+
inputValue="#{h(value)}"/>
|
369
405
|
</entry>
|
370
406
|
EOS
|
371
407
|
end
|
@@ -378,17 +414,27 @@ module GoogleDrive
|
|
378
414
|
:post,
|
379
415
|
batch_url,
|
380
416
|
data: xml,
|
381
|
-
header: {
|
417
|
+
header: {
|
418
|
+
'Content-Type' => 'application/atom+xml;charset=utf-8',
|
419
|
+
'If-Match' => '*'
|
420
|
+
}
|
421
|
+
)
|
382
422
|
result.css('entry').each do |entry|
|
383
423
|
interrupted = entry.css('batch|interrupted')[0]
|
384
424
|
if interrupted
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
fail(GoogleDrive::Error, 'Updating cell %s has failed: %s' %
|
390
|
-
[entry.css('id').text, entry.css('batch|status')[0]['reason']])
|
425
|
+
raise(
|
426
|
+
GoogleDrive::Error,
|
427
|
+
format('Update has failed: %s', interrupted['reason'])
|
428
|
+
)
|
391
429
|
end
|
430
|
+
next if entry.css('batch|status').first['code'] =~ /^2/
|
431
|
+
raise(
|
432
|
+
GoogleDrive::Error,
|
433
|
+
format(
|
434
|
+
'Updating cell %s has failed: %s',
|
435
|
+
entry.css('id').text, entry.css('batch|status')[0]['reason']
|
436
|
+
)
|
437
|
+
)
|
392
438
|
end
|
393
439
|
|
394
440
|
@modified.clear
|
@@ -405,7 +451,8 @@ module GoogleDrive
|
|
405
451
|
reload
|
406
452
|
end
|
407
453
|
|
408
|
-
# Deletes this worksheet. Deletion takes effect right away without calling
|
454
|
+
# Deletes this worksheet. Deletion takes effect right away without calling
|
455
|
+
# save().
|
409
456
|
def delete
|
410
457
|
ws_doc = @session.request(:get, worksheet_feed_url)
|
411
458
|
edit_url = ws_doc.css("link[rel='edit']")[0]['href']
|
@@ -420,10 +467,12 @@ module GoogleDrive
|
|
420
467
|
# List feed URL of the worksheet.
|
421
468
|
def list_feed_url
|
422
469
|
@worksheet_feed_entry.css(
|
423
|
-
"link[rel='http://schemas.google.com/spreadsheets/2006#listfeed']"
|
470
|
+
"link[rel='http://schemas.google.com/spreadsheets/2006#listfeed']"
|
471
|
+
)[0]['href']
|
424
472
|
end
|
425
473
|
|
426
|
-
# Provides access to cells using column names, assuming the first row
|
474
|
+
# Provides access to cells using column names, assuming the first row
|
475
|
+
# contains column
|
427
476
|
# names. Returned object is GoogleDrive::List which you can use mostly as
|
428
477
|
# Array of Hash.
|
429
478
|
#
|
@@ -444,12 +493,19 @@ module GoogleDrive
|
|
444
493
|
# worksheet.cell_name_to_row_col("C2") #=> [2, 3]
|
445
494
|
def cell_name_to_row_col(cell_name)
|
446
495
|
unless cell_name.is_a?(String)
|
447
|
-
|
496
|
+
raise(
|
497
|
+
ArgumentError, format('Cell name must be a string: %p', cell_name)
|
498
|
+
)
|
448
499
|
end
|
449
500
|
unless cell_name.upcase =~ /^([A-Z]+)(\d+)$/
|
450
|
-
|
451
|
-
|
452
|
-
|
501
|
+
raise(
|
502
|
+
ArgumentError,
|
503
|
+
format(
|
504
|
+
'Cell name must be only letters followed by digits with no ' \
|
505
|
+
'spaces in between: %p',
|
506
|
+
cell_name
|
507
|
+
)
|
508
|
+
)
|
453
509
|
end
|
454
510
|
col = 0
|
455
511
|
Regexp.last_match(1).each_byte do |b|
|
@@ -463,7 +519,11 @@ module GoogleDrive
|
|
463
519
|
def inspect
|
464
520
|
fields = { worksheet_feed_url: worksheet_feed_url }
|
465
521
|
fields[:title] = @title if @title
|
466
|
-
|
522
|
+
format(
|
523
|
+
"\#<%p %s>",
|
524
|
+
self.class,
|
525
|
+
fields.map { |k, v| format('%s=%p', k, v) }.join(', ')
|
526
|
+
)
|
467
527
|
end
|
468
528
|
|
469
529
|
private
|
@@ -475,7 +535,7 @@ module GoogleDrive
|
|
475
535
|
@updated = Time.parse(entry.css('updated').text)
|
476
536
|
@meta_modified = false
|
477
537
|
end
|
478
|
-
|
538
|
+
|
479
539
|
def set_max_values(entry)
|
480
540
|
@max_rows = entry.css('gs|rowCount').text.to_i
|
481
541
|
@max_cols = entry.css('gs|colCount').text.to_i
|
@@ -504,23 +564,36 @@ module GoogleDrive
|
|
504
564
|
|
505
565
|
def parse_cell_args(args)
|
506
566
|
if args.size == 1 && args[0].is_a?(String)
|
507
|
-
|
567
|
+
cell_name_to_row_col(args[0])
|
508
568
|
elsif args.size == 2 && args[0].is_a?(Integer) && args[1].is_a?(Integer)
|
509
569
|
if args[0] >= 1 && args[1] >= 1
|
510
|
-
|
570
|
+
args
|
511
571
|
else
|
512
|
-
|
513
|
-
|
572
|
+
raise(
|
573
|
+
ArgumentError,
|
574
|
+
format(
|
575
|
+
'Row/col must be >= 1 (1-origin), but are %d/%d',
|
576
|
+
args[0], args[1]
|
577
|
+
)
|
578
|
+
)
|
514
579
|
end
|
515
580
|
else
|
516
|
-
|
517
|
-
|
581
|
+
raise(
|
582
|
+
ArgumentError,
|
583
|
+
format(
|
584
|
+
"Arguments must be either one String or two Integer's, but are %p",
|
585
|
+
args
|
586
|
+
)
|
587
|
+
)
|
518
588
|
end
|
519
589
|
end
|
520
590
|
|
521
591
|
def validate_cell_value(value)
|
522
592
|
if value =~ XML_INVAILD_CHAR_REGEXP
|
523
|
-
|
593
|
+
raise(
|
594
|
+
ArgumentError,
|
595
|
+
format('Contains invalid character %p for XML 1.0: %p', $&, value)
|
596
|
+
)
|
524
597
|
end
|
525
598
|
end
|
526
599
|
end
|