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,207 @@
1
+ # Author: Hiroshi Ichikawa <http://gimite.net/>
2
+ # The license of this source is "New BSD Licence"
3
+
4
+ require 'google_drive/util'
5
+ require 'google_drive/error'
6
+ require 'google_drive/spreadsheet'
7
+
8
+ module GoogleDrive
9
+ # Represents a folder in Google Drive.
10
+ #
11
+ # Use GoogleDrive::Session#root_collection,
12
+ # GoogleDrive::Collection#subcollections,
13
+ # or GoogleDrive::Session#collection_by_url to get GoogleDrive::Collection
14
+ # object.
15
+ class Collection < GoogleDrive::File
16
+ include(Util)
17
+
18
+ alias collection_feed_url document_feed_url
19
+
20
+ # Adds the given GoogleDrive::File to the folder.
21
+ def add(file)
22
+ @session.drive_service.update_file(
23
+ file.id, add_parents: id, fields: '', supports_all_drives: true
24
+ )
25
+ nil
26
+ end
27
+
28
+ # Removes the given GoogleDrive::File from the folder.
29
+ def remove(file)
30
+ @session.drive_service.update_file(
31
+ file.id, remove_parents: id, fields: '', supports_all_drives: true
32
+ )
33
+ end
34
+
35
+ # Creates a sub-folder with given title in this folder.
36
+ # Returns GoogleDrive::Collection object.
37
+ def create_subcollection(title, file_properties = {})
38
+ create_file(title, file_properties.merge(mime_type: 'application/vnd.google-apps.folder'))
39
+ end
40
+
41
+ alias create_subfolder create_subcollection
42
+
43
+ # Creates a spreadsheet with given title in this folder.
44
+ # Returns GoogleDrive::Spreadsheet object.
45
+ def create_spreadsheet(title, file_properties = {})
46
+ create_file(title, file_properties.merge(mime_type: 'application/vnd.google-apps.spreadsheet'))
47
+ end
48
+
49
+ # Creates a file with given title and properties in this folder.
50
+ # Returns objects with the following types:
51
+ # GoogleDrive::Spreadsheet, GoogleDrive::File, GoogleDrive::Collection
52
+ #
53
+ # You can pass a MIME Type using the file_properties-function parameter,
54
+ # for example: create_file('Document Title', mime_type: 'application/vnd.google-apps.document')
55
+ #
56
+ # A list of available Drive MIME Types can be found here:
57
+ # https://developers.google.com/drive/v3/web/mime-types
58
+ def create_file(title, file_properties = {})
59
+ file_metadata = {
60
+ name: title,
61
+ parents: [id]
62
+ }.merge(file_properties)
63
+
64
+ file = @session.drive_service.create_file(
65
+ file_metadata, fields: '*', supports_all_drives: true
66
+ )
67
+
68
+ @session.wrap_api_file(file)
69
+ end
70
+
71
+ # Returns true if this is a root folder.
72
+ def root?
73
+ !api_file.parents || api_file.parents.empty?
74
+ end
75
+
76
+ # Returns all the files (including spreadsheets, documents, subfolders) in
77
+ # the folder. You can specify parameters documented at
78
+ # https://developers.google.com/drive/v3/web/search-parameters
79
+ #
80
+ # e.g.
81
+ #
82
+ # # Gets all the files in the folder, including subfolders.
83
+ # collection.files
84
+ #
85
+ # # Gets only files with title "hoge".
86
+ # collection.files(q: "name = 'hoge'")
87
+ #
88
+ # # Same as above with a placeholder.
89
+ # collection.files(q: ["name = ?", "hoge"])
90
+ #
91
+ # By default, it returns the first 100 files. See document of
92
+ # GoogleDrive::Session#files method for how to get all files.
93
+ def files(params = {}, &block)
94
+ files_with_type(nil, params, &block)
95
+ end
96
+
97
+ # Uploads a file to this folder. See Session#upload_from_file for details.
98
+ def upload_from_file(path, title = nil, params = {})
99
+ params = { parents: [id] }.merge(params)
100
+ @session.upload_from_file(path, title, params)
101
+ end
102
+
103
+ # Uploads a file to this folder. See Session#upload_from_io for details.
104
+ def upload_from_io(io, title = 'Untitled', params = {})
105
+ params = { parents: [id] }.merge(params)
106
+ @session.upload_from_io(io, title, params)
107
+ end
108
+
109
+ # Uploads a file to this folder. See Session#upload_from_string for details.
110
+ def upload_from_string(content, title = 'Untitled', params = {})
111
+ params = { parents: [id] }.merge(params)
112
+ @session.upload_from_string(content, title, params)
113
+ end
114
+
115
+ alias contents files
116
+
117
+ # Returns all the spreadsheets in the folder.
118
+ #
119
+ # By default, it returns the first 100 spreadsheets. See document of
120
+ # GoogleDrive::Session#files method for how to get all spreadsheets.
121
+ def spreadsheets(params = {}, &block)
122
+ files_with_type('application/vnd.google-apps.spreadsheet', params, &block)
123
+ end
124
+
125
+ # Returns all the Google Docs documents in the folder.
126
+ #
127
+ # By default, it returns the first 100 documents. See document of
128
+ # GoogleDrive::Session#files method for how to get all documents.
129
+ def documents(params = {}, &block)
130
+ files_with_type('application/vnd.google-apps.document', params, &block)
131
+ end
132
+
133
+ # Returns all its subfolders.
134
+ #
135
+ # By default, it returns the first 100 subfolders. See document of
136
+ # GoogleDrive::Session#files method for how to get all subfolders.
137
+ def subcollections(params = {}, &block)
138
+ files_with_type('application/vnd.google-apps.folder', params, &block)
139
+ end
140
+
141
+ alias subfolders subcollections
142
+
143
+ # Returns a file (can be a spreadsheet, document, subfolder or other files)
144
+ # in the folder which exactly matches +title+ as GoogleDrive::File.
145
+ # Returns nil if not found. If multiple folders with the +title+ are found,
146
+ # returns one of them.
147
+ #
148
+ # If given an Array, does a recursive subfolder traversal.
149
+ def file_by_title(title)
150
+ file_by_title_with_type(title, nil)
151
+ end
152
+
153
+ alias file_by_name file_by_title
154
+
155
+ # Returns its subfolder whose title exactly matches +title+ as
156
+ # GoogleDrive::Collection.
157
+ # Returns nil if not found. If multiple folders with the +title+ are found,
158
+ # returns one of them.
159
+ #
160
+ # If given an Array, does a recursive subfolder traversal.
161
+ def subcollection_by_title(title)
162
+ file_by_title_with_type(title, 'application/vnd.google-apps.folder')
163
+ end
164
+
165
+ alias subfolder_by_name subcollection_by_title
166
+
167
+ # Returns URL of the deprecated contents feed.
168
+ def contents_url
169
+ document_feed_url + '/contents'
170
+ end
171
+
172
+ protected
173
+
174
+ def file_by_title_with_type(title, type)
175
+ if title.is_a?(Array)
176
+ rel_path = title
177
+ if rel_path.empty?
178
+ return self
179
+ else
180
+ parent = subcollection_by_title(rel_path[0...-1])
181
+ return parent && parent.file_by_title_with_type(rel_path[-1], type)
182
+ end
183
+ else
184
+ files_with_type(type, q: ['name = ?', title], page_size: 1)[0]
185
+ end
186
+ end
187
+
188
+ alias file_by_name_with_type file_by_title_with_type
189
+
190
+ private
191
+
192
+ def files_with_type(type, params = {}, &block)
193
+ params = convert_params(params)
194
+ query = construct_and_query([
195
+ ['? in parents', id],
196
+ type ? ['mimeType = ?', type] : nil,
197
+ params[:q]
198
+ ])
199
+ params = params.merge(q: query)
200
+ # This is faster than calling children.list and then files.get for each
201
+ # file.
202
+ @session.files(params, &block)
203
+ end
204
+ end
205
+
206
+ Folder = Collection
207
+ end
@@ -0,0 +1,36 @@
1
+ # Author: Mateusz Czerwinski <mtczerwinski@gmail.com>
2
+ # The license of this source is "New BSD Licence"
3
+
4
+ require 'json'
5
+
6
+ module GoogleDrive
7
+ # @api private
8
+ class Config
9
+ FIELDS = %w[client_id client_secret scope refresh_token type].freeze
10
+ attr_accessor(*FIELDS)
11
+
12
+ def initialize(config_path)
13
+ @config_path = config_path
14
+ if ::File.exist?(config_path)
15
+ JSON.parse(::File.read(config_path)).each do |key, value|
16
+ instance_variable_set("@#{key}", value) if FIELDS.include?(key)
17
+ end
18
+ end
19
+ end
20
+
21
+ def save
22
+ ::File.open(@config_path, 'w', 0o600) { |f| f.write(to_json) }
23
+ end
24
+
25
+ private
26
+
27
+ def to_json
28
+ hash = {}
29
+ FIELDS.each do |field|
30
+ value = __send__(field)
31
+ hash[field] = value if value
32
+ end
33
+ JSON.pretty_generate(hash)
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,8 @@
1
+ # Author: Hiroshi Ichikawa <http://gimite.net/>
2
+ # The license of this source is "New BSD Licence"
3
+
4
+ module GoogleDrive
5
+ # Raised on errors in this library.
6
+ class Error < RuntimeError
7
+ end
8
+ end
@@ -0,0 +1,266 @@
1
+ # Author: Hiroshi Ichikawa <http://gimite.net/>
2
+ # The license of this source is "New BSD Licence"
3
+
4
+ require 'cgi'
5
+ require 'forwardable'
6
+ require 'stringio'
7
+
8
+ require 'google_drive/util'
9
+ require 'google_drive/acl'
10
+
11
+ module GoogleDrive
12
+ # A file in Google Drive, including a Google Docs
13
+ # document/spreadsheet/presentation and a folder.
14
+ #
15
+ # Use GoogleDrive::Session#files or GoogleDrive::Session#file_by_title to
16
+ # get this object.
17
+ #
18
+ # In addition to the methods below, properties defined here are also available
19
+ # as attributes:
20
+ # https://developers.google.com/drive/v3/reference/files#resource
21
+ #
22
+ # e.g.,
23
+ # file.mime_type # ==> "text/plain"
24
+ class File
25
+ include(Util)
26
+ extend(Forwardable)
27
+
28
+ # @api private
29
+ def initialize(session, api_file)
30
+ @session = session
31
+ @api_file = api_file
32
+ @acl = nil
33
+ delegate_api_methods(self, @api_file, [:title])
34
+ end
35
+
36
+ # Wrapped Google::APIClient::Schema::Drive::V3::File object.
37
+ attr_reader(:api_file)
38
+
39
+ # Reloads file metadata such as title and acl.
40
+ def reload_metadata
41
+ @api_file = @session.drive_service.get_file(
42
+ id, fields: '*', supports_all_drives: true
43
+ )
44
+ @acl = Acl.new(@session, self) if @acl
45
+ end
46
+
47
+ # Returns resource_type + ":" + id.
48
+ def resource_id
49
+ format('%s:%s', resource_type, id)
50
+ end
51
+
52
+ # URL of feed used in the deprecated document list feed API.
53
+ def document_feed_url
54
+ 'https://docs.google.com/feeds/default/private/full/' +
55
+ CGI.escape(resource_id)
56
+ end
57
+
58
+ # Deprecated ACL feed URL of the file.
59
+ def acl_feed_url
60
+ document_feed_url + '/acl'
61
+ end
62
+
63
+ # The type of resourse. e.g. "document", "spreadsheet", "folder"
64
+ def resource_type
65
+ mime_type.slice(/^application\/vnd.google-apps.(.+)$/, 1) || 'file'
66
+ end
67
+
68
+ # Title of the file.
69
+ def title(params = {})
70
+ reload_metadata if params[:reload]
71
+ api_file.name
72
+ end
73
+
74
+ alias name title
75
+
76
+ # URL to view/edit the file in a Web browser.
77
+ #
78
+ # e.g. "https://docs.google.com/file/d/xxxx/edit"
79
+ def human_url
80
+ api_file.web_view_link
81
+ end
82
+
83
+ # Content types you can specify in methods download_to_file,
84
+ # download_to_string, download_to_io.
85
+ #
86
+ # This returns zero or one file type. You may be able to download the file
87
+ # in other formats using export_as_file, export_as_string, or export_to_io.
88
+ def available_content_types
89
+ api_file.web_content_link ? [api_file.mime_type] : []
90
+ end
91
+
92
+ # Downloads the file to a local file. e.g.
93
+ # file.download_to_file("/path/to/hoge.txt")
94
+ #
95
+ # To export the file in other formats, use export_as_file.
96
+ def download_to_file(path, params = {})
97
+ @session.drive_service.get_file(
98
+ id,
99
+ { download_dest: path, supports_all_drives: true }.merge(params)
100
+ )
101
+ end
102
+
103
+ # Downloads the file and returns as a String.
104
+ #
105
+ # To export the file in other formats, use export_as_string.
106
+ def download_to_string(params = {})
107
+ sio = StringIO.new
108
+ download_to_io(sio, params)
109
+ sio.string
110
+ end
111
+
112
+ # Downloads the file and writes it to +io+.
113
+ #
114
+ # To export the file in other formats, use export_to_io.
115
+ def download_to_io(io, params = {})
116
+ @session.drive_service.get_file(
117
+ id,
118
+ **{ download_dest: io, supports_all_drives: true }.merge(params)
119
+ )
120
+ end
121
+
122
+ # Export the file to +path+ in content type +format+.
123
+ # If +format+ is nil, it is guessed from the file name.
124
+ #
125
+ # e.g.,
126
+ # spreadsheet.export_as_file("/path/to/hoge.csv")
127
+ # spreadsheet.export_as_file("/path/to/hoge", "text/csv")
128
+ #
129
+ # If you want to download the file in the original format,
130
+ # use download_to_file instead.
131
+ def export_as_file(path, format = nil)
132
+ unless format
133
+ format = EXT_TO_CONTENT_TYPE[::File.extname(path).downcase]
134
+ unless format
135
+ raise(ArgumentError,
136
+ format("Cannot guess format from the file name: %s\n" \
137
+ 'Specify format argument explicitly.', path))
138
+ end
139
+ end
140
+ export_to_dest(path, format)
141
+ end
142
+
143
+ # Export the file as String in content type +format+.
144
+ #
145
+ # e.g.,
146
+ # spreadsheet.export_as_string("text/csv")
147
+ #
148
+ # If you want to download the file in the original format, use
149
+ # download_to_string instead.
150
+ def export_as_string(format)
151
+ sio = StringIO.new
152
+ export_to_dest(sio, format)
153
+ sio.string
154
+ end
155
+
156
+ # Export the file to +io+ in content type +format+.
157
+ #
158
+ # If you want to download the file in the original format, use
159
+ # download_to_io instead.
160
+ def export_to_io(io, format)
161
+ export_to_dest(io, format)
162
+ end
163
+
164
+ # Updates the file with +content+.
165
+ #
166
+ # e.g.
167
+ # file.update_from_string("Good bye, world.")
168
+ def update_from_string(content, params = {})
169
+ update_from_io(StringIO.new(content), params)
170
+ end
171
+
172
+ # Updates the file with the content of the local file.
173
+ #
174
+ # e.g.
175
+ # file.update_from_file("/path/to/hoge.txt")
176
+ def update_from_file(path, params = {})
177
+ # Somehow it doesn't work if I specify the file name directly as
178
+ # upload_source.
179
+ open(path, 'rb') do |f|
180
+ update_from_io(f, params)
181
+ end
182
+ nil
183
+ end
184
+
185
+ # Reads content from +io+ and updates the file with the content.
186
+ def update_from_io(io, params = {})
187
+ params = { upload_source: io, supports_all_drives: true }.merge(params)
188
+ @session.drive_service.update_file(id, nil, **params)
189
+ nil
190
+ end
191
+
192
+ # If +permanent+ is +false+, moves the file to the trash.
193
+ # If +permanent+ is +true+, deletes the file permanently.
194
+ def delete(permanent = false)
195
+ if permanent
196
+ @session.drive_service.delete_file(id, supports_all_drives: true)
197
+ else
198
+ @session.drive_service.update_file(
199
+ id, { trashed: true }, supports_all_drives: true
200
+ )
201
+ end
202
+ nil
203
+ end
204
+
205
+ # Renames title of the file.
206
+ def rename(title)
207
+ @session.drive_service.update_file(
208
+ id, { name: title }, supports_all_drives: true
209
+ )
210
+ nil
211
+ end
212
+
213
+ alias title= rename
214
+
215
+ # Creates copy of this file with the given title.
216
+ def copy(title, file_properties = {})
217
+ api_file = @session.drive_service.copy_file(
218
+ id, { name: title }.merge(file_properties), fields: '*', supports_all_drives: true
219
+ )
220
+ @session.wrap_api_file(api_file)
221
+ end
222
+
223
+ alias duplicate copy
224
+
225
+ # Returns GoogleDrive::Acl object for the file.
226
+ #
227
+ # With the object, you can see and modify people who can access the file.
228
+ # Modifications take effect immediately.
229
+ #
230
+ # e.g.
231
+ # # Dumps people who have access:
232
+ # for entry in file.acl
233
+ # p [entry.type, entry.email_address, entry.role]
234
+ # # => e.g. ["user", "example1@gmail.com", "owner"]
235
+ # end
236
+ #
237
+ # # Shares the file with new people:
238
+ # # NOTE: This sends email to the new people.
239
+ # file.acl.push(
240
+ # {type: "user", email_address: "example2@gmail.com", role: "reader"})
241
+ # file.acl.push(
242
+ # {type: "user", email_address: "example3@gmail.com", role: "writer"})
243
+ #
244
+ # # Changes the role of a person:
245
+ # file.acl[1].role = "writer"
246
+ #
247
+ # # Deletes an ACL entry:
248
+ # file.acl.delete(file.acl[1])
249
+ def acl(params = {})
250
+ @acl = Acl.new(@session, self) if !@acl || params[:reload]
251
+ @acl
252
+ end
253
+
254
+ def inspect
255
+ format("\#<%p id=%p title=%p>", self.class, id, title)
256
+ end
257
+
258
+ private
259
+
260
+ def export_to_dest(dest, format)
261
+ mime_type = EXT_TO_CONTENT_TYPE['.' + format] || format
262
+ @session.drive_service.export_file(id, mime_type, download_dest: dest)
263
+ nil
264
+ end
265
+ end
266
+ end
@@ -0,0 +1,125 @@
1
+ # Author: Hiroshi Ichikawa <http://gimite.net/>
2
+ # The license of this source is "New BSD Licence"
3
+
4
+ require 'google_drive/util'
5
+ require 'google_drive/error'
6
+ require 'google_drive/list_row'
7
+
8
+ module GoogleDrive
9
+ # Provides access to cells using column names.
10
+ # Use GoogleDrive::Worksheet#list to get GoogleDrive::List object.
11
+ #--
12
+ # This is implemented as wrapper of GoogleDrive::Worksheet i.e. using cells
13
+ # feed, not list feed. In this way, we can easily provide consistent API as
14
+ # GoogleDrive::Worksheet using save()/reload().
15
+ class List
16
+ include(Enumerable)
17
+
18
+ # @api private
19
+ def initialize(worksheet)
20
+ @worksheet = worksheet
21
+ end
22
+
23
+ # Number of non-empty rows in the worksheet excluding the first row.
24
+ def size
25
+ @worksheet.num_rows - 1
26
+ end
27
+
28
+ # Returns Hash-like object (GoogleDrive::ListRow) for the row with the
29
+ # index. Keys of the object are colum names (the first row).
30
+ # The second row has index 0.
31
+ #
32
+ # Note that updates to the returned object are not sent to the server until
33
+ # you call GoogleDrive::Worksheet#save().
34
+ def [](index)
35
+ ListRow.new(self, index)
36
+ end
37
+
38
+ # Updates the row with the index with the given Hash object.
39
+ # Keys of +hash+ are colum names (the first row).
40
+ # The second row has index 0.
41
+ #
42
+ # Note that update is not sent to the server until
43
+ # you call GoogleDrive::Worksheet#save().
44
+ def []=(index, hash)
45
+ self[index].replace(hash)
46
+ end
47
+
48
+ # Iterates over Hash-like object (GoogleDrive::ListRow) for each row
49
+ # (except for the first row).
50
+ # Keys of the object are colum names (the first row).
51
+ def each(&_block)
52
+ for i in 0...size
53
+ yield(self[i])
54
+ end
55
+ end
56
+
57
+ # Column names i.e. the contents of the first row.
58
+ # Duplicates are removed.
59
+ def keys
60
+ (1..@worksheet.num_cols).map { |i| @worksheet[1, i] }.uniq
61
+ end
62
+
63
+ # Updates column names i.e. the contents of the first row.
64
+ #
65
+ # Note that update is not sent to the server until
66
+ # you call GoogleDrive::Worksheet#save().
67
+ def keys=(ary)
68
+ for i in 1..ary.size
69
+ @worksheet[1, i] = ary[i - 1]
70
+ end
71
+ for i in (ary.size + 1)..@worksheet.num_cols
72
+ @worksheet[1, i] = ''
73
+ end
74
+ end
75
+
76
+ # Adds a new row to the bottom.
77
+ # Keys of +hash+ are colum names (the first row).
78
+ # Returns GoogleDrive::ListRow for the new row.
79
+ #
80
+ # Note that update is not sent to the server until
81
+ # you call GoogleDrive::Worksheet#save().
82
+ def push(hash)
83
+ row = self[size]
84
+ row.update(hash)
85
+ row
86
+ end
87
+
88
+ # Returns all rows (except for the first row) as Array of Hash.
89
+ # Keys of Hash objects are colum names (the first row).
90
+ def to_hash_array
91
+ map(&:to_hash)
92
+ end
93
+
94
+ # @api private
95
+ def get(index, key)
96
+ @worksheet[index + 2, key_to_col(key)]
97
+ end
98
+
99
+ # @api private
100
+ def numeric_value(index, key)
101
+ @worksheet.numeric_value(index + 2, key_to_col(key))
102
+ end
103
+
104
+ # @api private
105
+ def input_value(index, key)
106
+ @worksheet.input_value(index + 2, key_to_col(key))
107
+ end
108
+
109
+ # @api private
110
+ def set(index, key, value)
111
+ @worksheet[index + 2, key_to_col(key)] = value
112
+ end
113
+
114
+ private
115
+
116
+ def key_to_col(key)
117
+ key = key.to_s
118
+ col = (1..@worksheet.num_cols).find { |c| @worksheet[1, c] == key }
119
+ unless col
120
+ raise(GoogleDrive::Error, format("Column doesn't exist: %p", key))
121
+ end
122
+ col
123
+ end
124
+ end
125
+ end