google_drive2 3.0.8
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 +7 -0
- data/README.md +136 -0
- data/lib/google_drive/access_token_credentials.rb +19 -0
- data/lib/google_drive/acl.rb +111 -0
- data/lib/google_drive/acl_entry.rb +181 -0
- data/lib/google_drive/api_client_fetcher.rb +59 -0
- data/lib/google_drive/authentication_error.rb +10 -0
- data/lib/google_drive/collection.rb +207 -0
- data/lib/google_drive/config.rb +36 -0
- data/lib/google_drive/error.rb +8 -0
- data/lib/google_drive/file.rb +266 -0
- data/lib/google_drive/list.rb +125 -0
- data/lib/google_drive/list_row.rb +89 -0
- data/lib/google_drive/response_code_error.rb +23 -0
- data/lib/google_drive/session.rb +733 -0
- data/lib/google_drive/spreadsheet.rb +135 -0
- data/lib/google_drive/util.rb +243 -0
- data/lib/google_drive/worksheet.rb +770 -0
- data/lib/google_drive.rb +35 -0
- metadata +196 -0
@@ -0,0 +1,135 @@
|
|
1
|
+
# Author: Hiroshi Ichikawa <http://gimite.net/>
|
2
|
+
# The license of this source is "New BSD Licence"
|
3
|
+
|
4
|
+
require 'time'
|
5
|
+
|
6
|
+
require 'google_drive/util'
|
7
|
+
require 'google_drive/error'
|
8
|
+
require 'google_drive/worksheet'
|
9
|
+
require 'google_drive/acl'
|
10
|
+
require 'google_drive/file'
|
11
|
+
|
12
|
+
module GoogleDrive
|
13
|
+
# A spreadsheet.
|
14
|
+
#
|
15
|
+
# e.g., Use methods spreadsheet_by_title, spreadsheet_by_url,
|
16
|
+
# create_spreadsheet in GoogleDrive::Session to get GoogleDrive::Spreadsheet
|
17
|
+
# object.
|
18
|
+
class Spreadsheet < GoogleDrive::File
|
19
|
+
|
20
|
+
# TODO: Bump up the major version before switching the existing methods to
|
21
|
+
# v4 API because it requires to turn on a new API in the API console.
|
22
|
+
|
23
|
+
include(Util)
|
24
|
+
|
25
|
+
SUPPORTED_EXPORT_FORMAT = Set.new(%w[xlsx csv pdf])
|
26
|
+
|
27
|
+
# Key of the spreadsheet.
|
28
|
+
def key
|
29
|
+
id
|
30
|
+
end
|
31
|
+
|
32
|
+
# URL of worksheet-based feed of the spreadsheet.
|
33
|
+
def worksheets_feed_url
|
34
|
+
format(
|
35
|
+
'https://spreadsheets.google.com/feeds/worksheets/%s/private/full', id
|
36
|
+
)
|
37
|
+
end
|
38
|
+
|
39
|
+
# URL of feed used in the deprecated document list feed API.
|
40
|
+
def document_feed_url
|
41
|
+
'https://docs.google.com/feeds/documents/private/full/' +
|
42
|
+
CGI.escape(resource_id)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Spreadsheet feed URL of the spreadsheet.
|
46
|
+
def spreadsheet_feed_url
|
47
|
+
'https://spreadsheets.google.com/feeds/spreadsheets/private/full/' + id
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns worksheets of the spreadsheet as array of GoogleDrive::Worksheet.
|
51
|
+
def worksheets
|
52
|
+
api_spreadsheet = @session.sheets_service.get_spreadsheet(id, fields: 'sheets.properties')
|
53
|
+
api_spreadsheet.sheets.map{ |s| Worksheet.new(@session, self, s.properties) }
|
54
|
+
end
|
55
|
+
|
56
|
+
# Returns a GoogleDrive::Worksheet with the given title in the spreadsheet.
|
57
|
+
#
|
58
|
+
# Returns nil if not found. Returns the first one when multiple worksheets
|
59
|
+
# with the title are found.
|
60
|
+
def worksheet_by_title(title)
|
61
|
+
worksheets.find { |ws| ws.title == title }
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns a GoogleDrive::Worksheet with the given gid.
|
65
|
+
#
|
66
|
+
# Returns nil if not found.
|
67
|
+
def worksheet_by_sheet_id(sheet_id)
|
68
|
+
sheet_id = sheet_id.to_i
|
69
|
+
worksheets.find { |ws| ws.sheet_id == sheet_id }
|
70
|
+
end
|
71
|
+
|
72
|
+
alias worksheet_by_gid worksheet_by_sheet_id
|
73
|
+
|
74
|
+
# Adds a new worksheet to the spreadsheet. Returns added
|
75
|
+
# GoogleDrive::Worksheet.
|
76
|
+
#
|
77
|
+
# When +index+ is specified, the worksheet is inserted at the given
|
78
|
+
# +index+.
|
79
|
+
def add_worksheet(title, max_rows = 100, max_cols = 20, index: nil)
|
80
|
+
(response,) = batch_update([{
|
81
|
+
add_sheet: {
|
82
|
+
properties: {
|
83
|
+
title: title,
|
84
|
+
index: index,
|
85
|
+
grid_properties: {
|
86
|
+
row_count: max_rows,
|
87
|
+
column_count: max_cols,
|
88
|
+
},
|
89
|
+
},
|
90
|
+
},
|
91
|
+
}])
|
92
|
+
Worksheet.new(@session, self, response.add_sheet.properties)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Not available for GoogleDrive::Spreadsheet. Use export_as_file instead.
|
96
|
+
def download_to_file(_path, _params = {})
|
97
|
+
raise(
|
98
|
+
NotImplementedError,
|
99
|
+
'download_to_file is not available for GoogleDrive::Spreadsheet. ' \
|
100
|
+
'Use export_as_file instead.'
|
101
|
+
)
|
102
|
+
end
|
103
|
+
|
104
|
+
# Not available for GoogleDrive::Spreadsheet. Use export_as_string instead.
|
105
|
+
def download_to_string(_params = {})
|
106
|
+
raise(
|
107
|
+
NotImplementedError,
|
108
|
+
'download_to_string is not available for GoogleDrive::Spreadsheet. ' \
|
109
|
+
'Use export_as_string instead.'
|
110
|
+
)
|
111
|
+
end
|
112
|
+
|
113
|
+
# Not available for GoogleDrive::Spreadsheet. Use export_to_io instead.
|
114
|
+
def download_to_io(_io, _params = {})
|
115
|
+
raise(
|
116
|
+
NotImplementedError,
|
117
|
+
'download_to_io is not available for GoogleDrive::Spreadsheet. ' \
|
118
|
+
'Use export_to_io instead.'
|
119
|
+
)
|
120
|
+
end
|
121
|
+
|
122
|
+
# Performs batch update of the spreadsheet.
|
123
|
+
#
|
124
|
+
# +requests+ is an Array of Google::Apis::SheetsV4::Request or its Hash
|
125
|
+
# equivalent. Returns an Array of Google::Apis::SheetsV4::Response.
|
126
|
+
def batch_update(requests)
|
127
|
+
batch_request =
|
128
|
+
Google::Apis::SheetsV4::BatchUpdateSpreadsheetRequest.new(
|
129
|
+
requests: requests)
|
130
|
+
batch_response =
|
131
|
+
@session.sheets_service.batch_update_spreadsheet(id, batch_request)
|
132
|
+
batch_response.replies
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,243 @@
|
|
1
|
+
# Author: Hiroshi Ichikawa <http://gimite.net/>
|
2
|
+
# The license of this source is "New BSD Licence"
|
3
|
+
|
4
|
+
require 'cgi'
|
5
|
+
|
6
|
+
module GoogleDrive
|
7
|
+
# @api private
|
8
|
+
module Util
|
9
|
+
EXT_TO_CONTENT_TYPE = {
|
10
|
+
'.csv' => 'text/csv',
|
11
|
+
'.tsv' => 'text/tab-separated-values',
|
12
|
+
'.tab' => 'text/tab-separated-values',
|
13
|
+
'.doc' => 'application/msword',
|
14
|
+
'.docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
15
|
+
'.ods' => 'application/x-vnd.oasis.opendocument.spreadsheet',
|
16
|
+
'.odt' => 'application/vnd.oasis.opendocument.text',
|
17
|
+
'.rtf' => 'application/rtf',
|
18
|
+
'.sxw' => 'application/vnd.sun.xml.writer',
|
19
|
+
'.txt' => 'text/plain',
|
20
|
+
'.xls' => 'application/vnd.ms-excel',
|
21
|
+
'.xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
22
|
+
'.pdf' => 'application/pdf',
|
23
|
+
'.png' => 'image/png',
|
24
|
+
'.ppt' => 'application/vnd.ms-powerpoint',
|
25
|
+
'.pps' => 'application/vnd.ms-powerpoint',
|
26
|
+
'.pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
27
|
+
'.htm' => 'text/html',
|
28
|
+
'.html' => 'text/html',
|
29
|
+
'.zip' => 'application/zip',
|
30
|
+
'.swf' => 'application/x-shockwave-flash'
|
31
|
+
}.freeze
|
32
|
+
|
33
|
+
IMPORTABLE_CONTENT_TYPE_MAP = {
|
34
|
+
'application/x-vnd.oasis.opendocument.presentation' => 'application/vnd.google-apps.presentation',
|
35
|
+
'text/tab-separated-values' => 'application/vnd.google-apps.spreadsheet',
|
36
|
+
'image/jpeg' => 'application/vnd.google-apps.document',
|
37
|
+
'image/bmp' => 'application/vnd.google-apps.document',
|
38
|
+
'image/gif' => 'application/vnd.google-apps.document',
|
39
|
+
'application/vnd.ms-excel.sheet.macroenabled.12' => 'application/vnd.google-apps.spreadsheet',
|
40
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.template' => 'application/vnd.google-apps.document',
|
41
|
+
'application/vnd.ms-powerpoint.presentation.macroenabled.12' => 'application/vnd.google-apps.presentation',
|
42
|
+
'application/vnd.ms-word.template.macroenabled.12' => 'application/vnd.google-apps.document',
|
43
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'application/vnd.google-apps.document',
|
44
|
+
'image/pjpeg' => 'application/vnd.google-apps.document',
|
45
|
+
'application/vnd.google-apps.script+text/plain' => 'application/vnd.google-apps.script',
|
46
|
+
'application/vnd.ms-excel' => 'application/vnd.google-apps.spreadsheet',
|
47
|
+
'application/vnd.sun.xml.writer' => 'application/vnd.google-apps.document',
|
48
|
+
'application/vnd.ms-word.document.macroenabled.12' => 'application/vnd.google-apps.document',
|
49
|
+
'application/vnd.ms-powerpoint.slideshow.macroenabled.12' => 'application/vnd.google-apps.presentation',
|
50
|
+
'text/rtf' => 'application/vnd.google-apps.document',
|
51
|
+
'text/plain' => 'application/vnd.google-apps.document',
|
52
|
+
'application/vnd.oasis.opendocument.spreadsheet' => 'application/vnd.google-apps.spreadsheet',
|
53
|
+
'application/x-vnd.oasis.opendocument.spreadsheet' => 'application/vnd.google-apps.spreadsheet',
|
54
|
+
'image/png' => 'application/vnd.google-apps.document',
|
55
|
+
'application/x-vnd.oasis.opendocument.text' => 'application/vnd.google-apps.document',
|
56
|
+
'application/msword' => 'application/vnd.google-apps.document',
|
57
|
+
'application/pdf' => 'application/vnd.google-apps.document',
|
58
|
+
'application/json' => 'application/vnd.google-apps.script',
|
59
|
+
'application/x-msmetafile' => 'application/vnd.google-apps.drawing',
|
60
|
+
'application/vnd.openxmlformats-officedocument.spreadsheetml.template' => 'application/vnd.google-apps.spreadsheet',
|
61
|
+
'application/vnd.ms-powerpoint' => 'application/vnd.google-apps.presentation',
|
62
|
+
'application/vnd.ms-excel.template.macroenabled.12' => 'application/vnd.google-apps.spreadsheet',
|
63
|
+
'image/x-bmp' => 'application/vnd.google-apps.document',
|
64
|
+
'application/rtf' => 'application/vnd.google-apps.document',
|
65
|
+
'application/vnd.openxmlformats-officedocument.presentationml.template' => 'application/vnd.google-apps.presentation',
|
66
|
+
'image/x-png' => 'application/vnd.google-apps.document',
|
67
|
+
'text/html' => 'application/vnd.google-apps.document',
|
68
|
+
'application/vnd.oasis.opendocument.text' => 'application/vnd.google-apps.document',
|
69
|
+
'application/vnd.openxmlformats-officedocument.presentationml.presentation' => 'application/vnd.google-apps.presentation',
|
70
|
+
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'application/vnd.google-apps.spreadsheet',
|
71
|
+
'application/vnd.google-apps.script+json' => 'application/vnd.google-apps.script',
|
72
|
+
'application/vnd.openxmlformats-officedocument.presentationml.slideshow' => 'application/vnd.google-apps.presentation',
|
73
|
+
'application/vnd.ms-powerpoint.template.macroenabled.12' => 'application/vnd.google-apps.presentation',
|
74
|
+
'text/csv' => 'application/vnd.google-apps.spreadsheet',
|
75
|
+
'application/vnd.oasis.opendocument.presentation' => 'application/vnd.google-apps.presentation',
|
76
|
+
'image/jpg' => 'application/vnd.google-apps.document',
|
77
|
+
'text/richtext' => 'application/vnd.google-apps.document'
|
78
|
+
}.freeze
|
79
|
+
|
80
|
+
module_function
|
81
|
+
|
82
|
+
def encode_query(params)
|
83
|
+
params
|
84
|
+
.map { |k, v| CGI.escape(k.to_s) + '=' + CGI.escape(v.to_s) }
|
85
|
+
.join('&')
|
86
|
+
end
|
87
|
+
|
88
|
+
def concat_url(url, piece)
|
89
|
+
(url_base, url_query) = url.split(/\?/, 2)
|
90
|
+
(piece_base, piece_query) = piece.split(/\?/, 2)
|
91
|
+
result_query =
|
92
|
+
[url_query, piece_query].select { |s| s && !s.empty? }.join('&')
|
93
|
+
(url_base || '') +
|
94
|
+
(piece_base || '') +
|
95
|
+
(result_query.empty? ? '' : "?#{result_query}")
|
96
|
+
end
|
97
|
+
|
98
|
+
def h(str)
|
99
|
+
# Should also escape "\n" to keep it in cell contents.
|
100
|
+
CGI.escapeHTML(str.to_s).gsub(/\n/, '
')
|
101
|
+
end
|
102
|
+
|
103
|
+
def construct_query(arg)
|
104
|
+
case arg
|
105
|
+
|
106
|
+
when String
|
107
|
+
arg
|
108
|
+
|
109
|
+
when Array
|
110
|
+
if arg[0].scan(/\?/).size != arg.size - 1
|
111
|
+
raise(
|
112
|
+
ArgumentError,
|
113
|
+
format(
|
114
|
+
"The number of placeholders doesn't match the number of " \
|
115
|
+
'arguments: %p',
|
116
|
+
arg
|
117
|
+
)
|
118
|
+
)
|
119
|
+
end
|
120
|
+
i = 1
|
121
|
+
arg[0].gsub(/\?/) do
|
122
|
+
v = arg[i]
|
123
|
+
i += 1
|
124
|
+
case v
|
125
|
+
when String
|
126
|
+
format("'%s'", v.gsub(/['\\]/) { '\\' + $& })
|
127
|
+
when Time
|
128
|
+
format("'%s'", v.iso8601)
|
129
|
+
when TrueClass
|
130
|
+
'true'
|
131
|
+
when FalseClass
|
132
|
+
'false'
|
133
|
+
else
|
134
|
+
raise(
|
135
|
+
ArgumentError,
|
136
|
+
format('Expected String, Time, true or false, but got %p', v)
|
137
|
+
)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
else
|
142
|
+
raise(
|
143
|
+
ArgumentError, format('Expected String or Array, but got %p', arg)
|
144
|
+
)
|
145
|
+
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def construct_and_query(args)
|
150
|
+
args
|
151
|
+
.select { |a| a }.map { |a| format('(%s)', construct_query(a)) }
|
152
|
+
.join(' and ')
|
153
|
+
end
|
154
|
+
|
155
|
+
def convert_params(params)
|
156
|
+
str_params = {}
|
157
|
+
params.each do |k, v|
|
158
|
+
str_params[k.to_s] = v
|
159
|
+
end
|
160
|
+
|
161
|
+
old_terms = []
|
162
|
+
new_params = {}
|
163
|
+
str_params.each do |k, v|
|
164
|
+
case k
|
165
|
+
when 'q'
|
166
|
+
new_params[:q] = construct_query(v)
|
167
|
+
|
168
|
+
# Parameters in the old API.
|
169
|
+
when 'title'
|
170
|
+
if str_params['title-exact'].to_s == 'true'
|
171
|
+
old_terms.push(['name = ?', v])
|
172
|
+
else
|
173
|
+
old_terms.push(['name contains ?', v])
|
174
|
+
end
|
175
|
+
when 'title-exact'
|
176
|
+
# Skips it. It is handled above.
|
177
|
+
when 'opened-min'
|
178
|
+
old_terms.push(['lastViewedByMeDate >= ?', v])
|
179
|
+
when 'opened-max'
|
180
|
+
old_terms.push(['lastViewedByMeDate <= ?', v])
|
181
|
+
when 'edited-min'
|
182
|
+
old_terms.push(['modifiedDate >= ?', v])
|
183
|
+
when 'edited-max'
|
184
|
+
old_terms.push(['modifiedDate <= ?', v])
|
185
|
+
when 'owner'
|
186
|
+
old_terms.push(['? in owners', v])
|
187
|
+
when 'writer'
|
188
|
+
old_terms.push(['? in writers', v])
|
189
|
+
when 'reader'
|
190
|
+
old_terms.push(['? in readers', v])
|
191
|
+
when 'showfolders'
|
192
|
+
if v.to_s == 'false'
|
193
|
+
old_terms.push("mimeType != 'application/vnd.google-apps.folder'")
|
194
|
+
end
|
195
|
+
when 'showdeleted'
|
196
|
+
old_terms.push('trashed = false') if v.to_s == 'false'
|
197
|
+
when 'ocr', 'targetLanguage', 'sourceLanguage'
|
198
|
+
raise(
|
199
|
+
ArgumentError, format("'%s' parameter is no longer supported.", k)
|
200
|
+
)
|
201
|
+
else
|
202
|
+
# e.g., 'pageToken' -> :page_token
|
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
|
208
|
+
new_params[new_key] = v
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
unless old_terms.empty?
|
213
|
+
if new_params.key?(:q)
|
214
|
+
raise(
|
215
|
+
ArgumentError,
|
216
|
+
"Cannot specify both 'q' parameter and old query parameters."
|
217
|
+
)
|
218
|
+
else
|
219
|
+
new_params[:q] = construct_and_query(old_terms)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
new_params
|
224
|
+
end
|
225
|
+
|
226
|
+
def get_singleton_class(obj)
|
227
|
+
class << obj
|
228
|
+
return self
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
def delegate_api_methods(obj, api_obj, exceptions = [])
|
233
|
+
sc = get_singleton_class(obj)
|
234
|
+
names = api_obj.public_methods(false) - exceptions
|
235
|
+
names.each do |name|
|
236
|
+
next if name.to_s =~ /=$/
|
237
|
+
sc.__send__(:define_method, name) do
|
238
|
+
api_obj.__send__(name)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|