google_drive2 3.0.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -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