google_drive 0.3.11 → 1.0.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +7 -7
  2. data/README.rdoc +27 -10
  3. data/lib/google_drive/acl.rb +40 -58
  4. data/lib/google_drive/acl_entry.rb +76 -56
  5. data/lib/google_drive/api_client_fetcher.rb +49 -0
  6. data/lib/google_drive/collection.rb +69 -71
  7. data/lib/google_drive/file.rb +171 -128
  8. data/lib/google_drive/session.rb +234 -268
  9. data/lib/google_drive/spreadsheet.rb +19 -163
  10. data/lib/google_drive/util.rb +126 -17
  11. data/lib/google_drive/worksheet.rb +108 -80
  12. data/lib/google_drive.rb +63 -57
  13. data/lib/google_drive_v1/acl.rb +115 -0
  14. data/lib/google_drive_v1/acl_entry.rb +100 -0
  15. data/lib/google_drive_v1/api_client_fetcher.rb +47 -0
  16. data/lib/google_drive_v1/authentication_error.rb +14 -0
  17. data/lib/{google_drive → google_drive_v1}/basic_fetcher.rb +1 -1
  18. data/lib/{google_drive → google_drive_v1}/client_login_fetcher.rb +2 -2
  19. data/lib/google_drive_v1/collection.rb +167 -0
  20. data/lib/google_drive_v1/error.rb +12 -0
  21. data/lib/google_drive_v1/file.rb +258 -0
  22. data/lib/google_drive_v1/list.rb +119 -0
  23. data/lib/google_drive_v1/list_row.rb +88 -0
  24. data/lib/{google_drive → google_drive_v1}/oauth1_fetcher.rb +1 -1
  25. data/lib/{google_drive → google_drive_v1}/oauth2_fetcher.rb +2 -2
  26. data/lib/google_drive_v1/record.rb +31 -0
  27. data/lib/google_drive_v1/session.rb +522 -0
  28. data/lib/google_drive_v1/spreadsheet.rb +248 -0
  29. data/lib/google_drive_v1/table.rb +60 -0
  30. data/lib/google_drive_v1/util.rb +73 -0
  31. data/lib/google_drive_v1/worksheet.rb +498 -0
  32. data/lib/google_drive_v1.rb +148 -0
  33. metadata +112 -77
  34. data/doc_src/google_drive/acl_entry.rb +0 -33
@@ -2,6 +2,7 @@
2
2
  # The license of this source is "New BSD Licence"
3
3
 
4
4
  require "cgi"
5
+ require "forwardable"
5
6
  require "stringio"
6
7
 
7
8
  require "google_drive/util"
@@ -14,109 +15,90 @@ module GoogleDrive
14
15
  #
15
16
  # Use GoogleDrive::Session#files or GoogleDrive::Session#file_by_title to
16
17
  # get this object.
18
+ #
19
+ # In addition to the methods below, properties defined here are also available as attributes:
20
+ # https://developers.google.com/drive/v2/reference/files#resource
21
+ #
22
+ # e.g.,
23
+ # file.mime_type # ==> "text/plain"
17
24
  class File
18
25
 
19
26
  include(Util)
27
+ extend(Forwardable)
20
28
 
21
- def initialize(session, entry_or_url) #:nodoc:
29
+ def initialize(session, api_file) #:nodoc:
22
30
  @session = session
23
- if !entry_or_url
24
- # TODO Delete this after editing spreadsheet.rb.
25
- @document_feed_entry = nil
26
- @document_feed_url = entry_or_url
27
- elsif entry_or_url.is_a?(String)
28
- @document_feed_entry = nil
29
- @document_feed_url = entry_or_url
30
- else
31
- @document_feed_entry = entry_or_url
32
- # This is usually equal to the URL in <link rel="self">. But the URL in
33
- # <link rel="self"> in collection feed is e.g.
34
- # https://docs.google.com/feeds/default/private/full/folder%3Aroot/contents/folder%3Axxx
35
- # and deletion of the URL doesn't delete the file itself.
36
- # So we construct the URL here using resource ID instead.
37
- @document_feed_url = "%s/%s?v=3" % [DOCS_BASE_URL, CGI.escape(self.resource_id)]
38
- end
31
+ @api_file = api_file
39
32
  @acl = nil
33
+ delegate_api_methods(self, @api_file, ["title"])
40
34
  end
41
-
42
- # URL of feed used in document list feed API.
43
- attr_reader(:document_feed_url)
44
-
45
- # <entry> element of document list feed as Nokogiri::XML::Element.
46
- #
47
- # Set <tt>params[:reload]</tt> to true to force reloading the feed.
48
- def document_feed_entry(params = {})
49
- warn(
50
- "WARNING: GoogleDrive::file\#document_feed_entry is deprecated and will be removed " +
51
- "in the next version.")
52
- return self.document_feed_entry_internal(params)
53
- end
54
35
 
55
- def document_feed_entry_internal(params = {}) #:nodoc:
56
- if !@document_feed_entry || params[:reload]
57
- @document_feed_entry =
58
- @session.request(:get, self.document_feed_url, :auth => :writely).css("entry")[0]
36
+ # Wrapped Google::APIClient::Schema::Drive::V2::File object.
37
+ attr_reader(:api_file)
38
+
39
+ # Reloads file metadata such as title and acl.
40
+ def reload_metadata()
41
+ api_result = @session.execute!(
42
+ :api_method => @session.drive.files.get,
43
+ :parameters => { "fileId" => self.id })
44
+ @api_file = api_result.data
45
+ if @acl
46
+ @acl = Acl.new(@session, self)
59
47
  end
60
- return @document_feed_entry
61
48
  end
62
49
 
63
- # Resource ID.
50
+ # Returns resource_type + ":" + id.
64
51
  def resource_id
65
- return self.document_feed_entry_internal.css("gd|resourceId").text
52
+ return "%s:%s" % [self.resource_type, self.id]
53
+ end
54
+
55
+ # URL of feed used in the deprecated document list feed API.
56
+ def document_feed_url
57
+ return "https://docs.google.com/feeds/default/private/full/" + CGI.escape(self.resource_id)
58
+ end
59
+
60
+ # Deprecated ACL feed URL of the file.
61
+ def acl_feed_url
62
+ return self.document_feed_url + "/acl"
66
63
  end
67
64
 
68
65
  # The type of resourse. e.g. "document", "spreadsheet", "folder"
69
66
  def resource_type
70
- return self.resource_id.split(/:/)[0]
67
+ return self.mime_type.slice(/^application\/vnd.google-apps.(.+)$/, 1) || "file"
71
68
  end
72
69
 
73
70
  # Title of the file.
74
- #
75
- # Set <tt>params[:reload]</tt> to true to force reloading the title.
76
71
  def title(params = {})
77
- return document_feed_entry_internal(params).css("title").text
72
+ reload_metadata() if params[:reload]
73
+ return self.api_file.title
78
74
  end
79
75
 
80
76
  # URL to view/edit the file in a Web browser.
81
77
  #
82
78
  # e.g. "https://docs.google.com/file/d/xxxx/edit"
83
79
  def human_url
84
- return self.document_feed_entry_internal.css("link[rel='alternate']")[0]["href"]
80
+ return self.alternate_link
85
81
  end
86
82
 
87
- # ACL feed URL of the file.
88
- def acl_feed_url
89
- orig_acl_feed_url = self.document_feed_entry_internal.css(
90
- "gd|feedLink[rel='http://schemas.google.com/acl/2007#accessControlList']")[0]["href"]
91
- case orig_acl_feed_url
92
- when %r{^https?://docs.google.com/feeds/default/private/full/.*/acl(\?.*)?$}
93
- return orig_acl_feed_url
94
- when %r{^https?://docs.google.com/feeds/acl/private/full/([^\?]*)(\?.*)?$}
95
- # URL of old API version. Converts to v3 URL.
96
- return "#{DOCS_BASE_URL}/#{$1}/acl"
97
- else
98
- raise(GoogleDrive::Error,
99
- "ACL feed URL is in unknown format: #{orig_acl_feed_url}")
100
- end
101
- end
102
-
103
83
  # Content types you can specify in methods download_to_file, download_to_string,
104
84
  # download_to_io .
85
+ #
86
+ # This returns zero or one file type. You may be able to download the file in other formats using
87
+ # export_as_file, export_as_string, or export_to_io. Use export_links method to get available formats
88
+ # in these methods.
105
89
  def available_content_types
106
- return self.document_feed_entry_internal.css("content").map(){ |c| c["type"] }
90
+ if self.api_file.download_url
91
+ return [self.api_file.mime_type]
92
+ else
93
+ return []
94
+ end
107
95
  end
108
96
 
109
- # Downloads the file to a local file.
110
- #
111
- # e.g.
97
+ # Downloads the file to a local file. e.g.
112
98
  # file.download_to_file("/path/to/hoge.txt")
113
- # file.download_to_file("/path/to/hoge", :content_type => "text/plain")
99
+ #
100
+ # To export the file in other formats, use export_as_file.
114
101
  def download_to_file(path, params = {})
115
- params = params.dup()
116
- if !params[:content_type]
117
- params[:content_type] = EXT_TO_CONTENT_TYPE[::File.extname(path).downcase]
118
- params[:content_type_is_hint] = true
119
- end
120
102
  open(path, "wb") do |f|
121
103
  download_to_io(f, params)
122
104
  end
@@ -124,9 +106,7 @@ module GoogleDrive
124
106
 
125
107
  # Downloads the file and returns as a String.
126
108
  #
127
- # e.g.
128
- # file.download_to_string() #=> "Hello world."
129
- # file.download_to_string(:content_type => "text/plain") #=> "Hello world."
109
+ # To export the file in other formats, use export_as_string.
130
110
  def download_to_string(params = {})
131
111
  sio = StringIO.new()
132
112
  download_to_io(sio, params)
@@ -134,42 +114,73 @@ module GoogleDrive
134
114
  end
135
115
 
136
116
  # Downloads the file and writes it to +io+.
117
+ #
118
+ # To export the file in other formats, use export_to_io.
137
119
  def download_to_io(io, params = {})
138
- all_contents = self.document_feed_entry_internal.css("content")
139
- if params[:content_type] && (!params[:content_type_is_hint] || all_contents.size > 1)
140
- contents = all_contents.select(){ |c| c["type"] == params[:content_type] }
141
- else
142
- contents = all_contents
120
+ if !self.api_file.download_url
121
+ raise(GoogleDrive::Error, "Downloading is not supported for this file.")
143
122
  end
144
- if contents.size == 1
145
- url = contents[0]["src"]
146
- else
147
- if contents.empty?
148
- raise(GoogleDrive::Error,
149
- ("Downloading with content type %p not supported for this file. " +
150
- "Specify one of these to content_type: %p") %
151
- [params[:content_type], self.available_content_types])
152
- else
153
- raise(GoogleDrive::Error,
154
- ("Multiple content types are available for this file. " +
155
- "Specify one of these to content_type: %p") %
156
- [self.available_content_types])
123
+ # TODO Use streaming if possible.
124
+ api_result = @session.execute!(:uri => self.api_file.download_url)
125
+ io.write(api_result.body)
126
+ end
127
+
128
+ # Export the file to +path+ in content type +format+.
129
+ # If +format+ is nil, it is guessed from the file name.
130
+ #
131
+ # e.g.,
132
+ # spreadsheet.export_as_file("/path/to/hoge.csv")
133
+ # spreadsheet.export_as_file("/path/to/hoge", "text/csv")
134
+ #
135
+ # If you want to download the file in the original format, use download_to_file instead.
136
+ def export_as_file(path, format = nil)
137
+ if !format
138
+ format = EXT_TO_CONTENT_TYPE[::File.extname(path).downcase]
139
+ if !format
140
+ raise(ArgumentError,
141
+ ("Cannot guess format from the file name: %s\n" +
142
+ "Specify format argument explicitly.") %
143
+ path)
157
144
  end
158
145
  end
159
- # TODO Use streaming if possible.
160
- body = @session.request(:get, url, :response_type => :raw, :auth => :writely)
161
- io.write(body)
146
+ open(path, "wb") do |f|
147
+ export_to_io(f, format)
148
+ end
162
149
  end
163
150
 
164
- # Updates the file with the content of the local file.
151
+ # Export the file as String in content type +format+.
165
152
  #
166
- # e.g.
167
- # file.update_from_file("/path/to/hoge.txt")
168
- def update_from_file(path, params = {})
169
- params = {:file_name => ::File.basename(path)}.merge(params)
170
- open(path, "rb") do |f|
171
- update_from_io(f, params)
153
+ # e.g.,
154
+ # spreadsheet.export_as_string("text/csv")
155
+ #
156
+ # If you want to download the file in the original format, use download_to_string instead.
157
+ def export_as_string(format)
158
+ sio = StringIO.new()
159
+ export_to_io(sio, format)
160
+ return sio.string
161
+ end
162
+
163
+ # Export the file to +io+ in content type +format+.
164
+ #
165
+ # If you want to download the file in the original format, use download_to_io instead.
166
+ def export_to_io(io, format)
167
+ mime_type = EXT_TO_CONTENT_TYPE["." + format] || format
168
+ if !self.export_links
169
+ raise(
170
+ GoogleDrive::Error,
171
+ "This file doesn't support exporting. You may still download the file in the " +
172
+ "original format using download_to_file, download_to_string or download_to_io.")
173
+ end
174
+ export_url = self.export_links[mime_type]
175
+ if !export_url
176
+ raise(
177
+ GoogleDrive::Error,
178
+ "This file doesn't support export with mime type %p. Supported mime types: %p" %
179
+ [mime_type, self.export_links.to_hash().keys])
172
180
  end
181
+ # TODO Use streaming if possible.
182
+ api_result = @session.execute!(:uri => export_url)
183
+ io.write(api_result.body)
173
184
  end
174
185
 
175
186
  # Updates the file with +content+.
@@ -177,50 +188,84 @@ module GoogleDrive
177
188
  # e.g.
178
189
  # file.update_from_string("Good bye, world.")
179
190
  def update_from_string(content, params = {})
180
- update_from_io(StringIO.new(content), params)
191
+ media = new_upload_io(StringIO.new(content), params)
192
+ return update_from_media(media, params)
181
193
  end
182
194
 
195
+ # Updates the file with the content of the local file.
196
+ #
197
+ # e.g.
198
+ # file.update_from_file("/path/to/hoge.txt")
199
+ def update_from_file(path, params = {})
200
+ file_name = ::File.basename(path)
201
+ params = {:file_name => file_name}.merge(params)
202
+ media = new_upload_io(path, params)
203
+ return update_from_media(media, params)
204
+ end
205
+
183
206
  # Reads content from +io+ and updates the file with the content.
184
207
  def update_from_io(io, params = {})
185
- params = {:header => {"If-Match" => "*"}}.merge(params)
186
- initial_url = self.document_feed_entry_internal.css(
187
- "link[rel='http://schemas.google.com/g/2005#resumable-edit-media']")[0]["href"]
188
- @document_feed_entry = @session.upload_raw(
189
- :put, initial_url, io, self.title, params)
208
+ media = new_upload_io(io, params)
209
+ return update_from_media(media, params)
190
210
  end
191
-
211
+
212
+ # Reads content from +media+ and updates the file with the content.
213
+ def update_from_media(media, params = {})
214
+ api_result = @session.execute!(
215
+ :api_method => @session.drive.files.update,
216
+ :media => media,
217
+ :parameters => {
218
+ "fileId" => self.id,
219
+ "uploadType" => "media",
220
+ })
221
+ return @session.wrap_api_file(api_result.data)
222
+ end
223
+
192
224
  # If +permanent+ is +false+, moves the file to the trash.
193
225
  # If +permanent+ is +true+, deletes the file permanently.
194
226
  def delete(permanent = false)
195
- url = to_v3_url(self.document_feed_url)
196
- url = concat_url(url, "?delete=true") if permanent
197
- @session.request(:delete, url,
198
- :auth => :writely, :header => {"If-Match" => "*"})
227
+ if permanent
228
+ @session.execute!(
229
+ :api_method => @session.drive.files.delete,
230
+ :parameters => {"fileId" => self.id})
231
+ else
232
+ @session.execute!(
233
+ :api_method => @session.drive.files.trash,
234
+ :parameters => {"fileId" => self.id})
235
+ end
236
+ return nil
199
237
  end
200
238
 
201
239
  # Renames title of the file.
202
240
  def rename(title)
203
- edit_url = self.document_feed_entry_internal.css("link[rel='edit']").first["href"]
204
- xml = <<-"EOS"
205
- <atom:entry
206
- xmlns:atom="http://www.w3.org/2005/Atom"
207
- xmlns:docs="http://schemas.google.com/docs/2007">
208
- <atom:title>#{h(title)}</atom:title>
209
- </atom:entry>
210
- EOS
211
- header = {"Content-Type" => "application/atom+xml;charset=utf-8", "If-Match" => "*"}
212
- @session.request(:put, edit_url, :data => xml, :auth => :writely, :header => header)
241
+ api_result = @session.execute!(
242
+ :api_method => @session.drive.files.patch,
243
+ :body_object => {"title" => title},
244
+ :parameters => {"fileId" => self.id})
245
+ @api_file = api_result.data
213
246
  end
214
247
 
215
248
  alias title= rename
249
+
250
+ # Creates copy of this file with the given title.
251
+ def copy(title)
252
+ copied_file = @session.drive.files.copy.request_schema.new({
253
+ "title" => title,
254
+ })
255
+ api_result = @session.execute!(
256
+ :api_method => @session.drive.files.copy,
257
+ :body_object => copied_file,
258
+ :parameters => {"fileId" => self.id})
259
+ return @session.wrap_api_file(api_result.data)
260
+ end
261
+
262
+ alias duplicate copy
216
263
 
217
264
  # Returns GoogleDrive::Acl object for the file.
218
265
  #
219
266
  # With the object, you can see and modify people who can access the file.
220
267
  # Modifications take effect immediately.
221
268
  #
222
- # Set <tt>params[:reload]</tt> to true to force reloading the data.
223
- #
224
269
  # e.g.
225
270
  # # Dumps people who have access:
226
271
  # for entry in file.acl
@@ -231,9 +276,9 @@ module GoogleDrive
231
276
  # # Shares the file with new people:
232
277
  # # NOTE: This sends email to the new people.
233
278
  # file.acl.push(
234
- # {:scope_type => "user", :scope => "example2@gmail.com", :role => "reader"})
279
+ # {:type => "user", :value => "example2@gmail.com", :role => "reader"})
235
280
  # file.acl.push(
236
- # {:scope_type => "user", :scope => "example3@gmail.com", :role => "writer"})
281
+ # {:type => "user", :value => "example3@gmail.com", :role => "writer"})
237
282
  #
238
283
  # # Changes the role of a person:
239
284
  # file.acl[1].role = "writer"
@@ -242,15 +287,13 @@ module GoogleDrive
242
287
  # file.acl.delete(file.acl[1])
243
288
  def acl(params = {})
244
289
  if !@acl || params[:reload]
245
- @acl = Acl.new(@session, self.acl_feed_url)
290
+ @acl = Acl.new(@session, self)
246
291
  end
247
292
  return @acl
248
293
  end
249
294
 
250
295
  def inspect
251
- fields = {:document_feed_url => self.document_feed_url}
252
- fields[:title] = self.title if @document_feed_entry
253
- return "\#<%p %s>" % [self.class, fields.map(){ |k, v| "%s=%p" % [k, v] }.join(", ")]
296
+ return "\#<%p id=%p title=%p>" % [self.class, self.id, self.title]
254
297
  end
255
298
 
256
299
  end