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.
@@ -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/, '&#x0a;')
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