Module: Dropbox::API

Includes:
Memoization
Included in:
Session
Defined in:
lib/dropbox/api.rb

Overview

Extensions to the Dropbox::Session class that add core Dropbox API functionality to this class. You must have authenticated your Dropbox::Session instance before you can call any of these methods. (See the Dropbox::Session class documentation for instructions.)

API methods generally return Struct objects containing their results, unless otherwise noted. See the Dropbox API documentation at developers.dropbox.com for specific information on the schema of each result.

You can opt-in to memoization of API method results. See the Dropbox::Memoization class documentation to learn more.

Modes

The Dropbox API works in three modes: sandbox, Dropbox (root), and metadata-only.

  • In sandbox mode (the default), all operations are rooted from your application's sandbox folder; other files elsewhere on the user's Dropbox are inaccessible.

  • In Dropbox mode, the root is the user's Dropbox folder, and all files are accessible. This mode is typically only available to certain API users.

  • In metadata-only mode, the root is the Dropbox folder, but write access is not available. Operations that modify the user's files will fail.

You should configure the Dropbox::Session instance to use whichever mode you chose when you set up your application:

session.mode = :metadata_only

Valid values are listed in Dropbox::API::MODES, and this step is not necessary for sandboxed applications, as the sandbox mode is the default.

You can also temporarily change the mode for many method calls using their options hash:

session.move 'my_file', 'new/path', :mode => :dropbox

Constant Summary collapse

MODES =

Valid API modes for the #mode= method.

[ :sandbox, :dropbox, :metadata_only ]

Instance Method Summary collapse

Methods included from Memoization

#cache_clear_proc=, #cache_proc=, #disable_memoization, #enable_memoization, included

Instance Method Details

#accountObject

Returns a Struct with information about the user's account. See www.dropbox.com/developers/docs#account-info for more information on the data returned.



68
69
70
# File 'lib/dropbox/api.rb', line 68

def 
  get('account', 'info', :ssl => @ssl).to_struct_recursively
end

#copy(source, target, options = {}) ⇒ Object Also known as: cp

Copies the source file to the path at target. If target ends with a slash, the new file will share the same name as the old file. Returns a Struct with metadata for the new file. (See the metadata method.)

Both paths are assumed to be relative to the configured mode's root.

Raises FileNotFoundError if source does not exist. Raises FileExistsError if target already exists.

Options:

mode

Temporarily changes the API mode. See the MODES array.

TODO The API documentation says this method returns 404/403 if the source or target is invalid, but it actually returns 5xx.



239
240
241
242
243
244
245
246
247
248
249
250
# File 'lib/dropbox/api.rb', line 239

def copy(source, target, options={})
  source = source.sub(/^\//, '')
  target = target.sub(/^\//, '')
  target << File.basename(source) if target.ends_with?('/')
  begin
    (post('fileops', 'copy', :from_path => Dropbox.check_path(source), :to_path => Dropbox.check_path(target), :root => root(options), :ssl => @ssl)).to_struct_recursively
  rescue UnsuccessfulResponseError => error
    raise FileNotFoundError.new(source) if error.response.kind_of?(Net::HTTPNotFound)
    raise FileExistsError.new(target) if error.response.kind_of?(Net::HTTPForbidden)
    raise error
  end
end

#create_folder(path, options = {}) ⇒ Object Also known as: mkdir

Creates a folder at the given path. The path is assumed to be relative to the configured mode's root. Returns a Struct with metadata about the new folder. (See the metadata method.)

Raises FileExistsError if there is already a file or folder at path.

Options:

mode

Temporarily changes the API mode. See the MODES array.

TODO The API documentation says this method returns 403 if the path already exists, but it actually appends “ (1)” to the end of the name and returns 200.



265
266
267
268
269
270
271
272
273
274
# File 'lib/dropbox/api.rb', line 265

def create_folder(path, options={})
  path = path.sub(/^\//, '')
  path.sub! /\/$/, ''
  begin
    (post('fileops', 'create_folder', :path => Dropbox.check_path(path), :root => root(options), :ssl => @ssl)).to_struct_recursively
  rescue UnsuccessfulResponseError => error
    raise FileExistsError.new(path) if error.response.kind_of?(Net::HTTPForbidden)
    raise error
  end
end

#delete(path, options = {}) ⇒ Object Also known as: rm

Deletes a file or folder at the given path. The path is assumed to be relative to the configured mode's root.

Raises FileNotFoundError if the file or folder does not exist at path.

Options:

mode

Temporarily changes the API mode. See the MODES array.

TODO The API documentation says this method returns 404 if the path does not exist, but it actually returns 5xx.



288
289
290
291
292
293
294
295
296
297
298
# File 'lib/dropbox/api.rb', line 288

def delete(path, options={})
  path = path.sub(/^\//, '')
  path.sub! /\/$/, ''
  begin
    api_response(:post, 'fileops', 'delete', :path => Dropbox.check_path(path), :root => root(options), :ssl => @ssl)
  rescue UnsuccessfulResponseError => error
    raise FileNotFoundError.new(path) if error.response.kind_of?(Net::HTTPNotFound)
    raise error
  end
  return true
end

#download(path, options = {}) ⇒ Object

Downloads the file at the given path relative to the configured mode's root.

Returns the contents of the downloaded file as a String. Support for streaming downloads and range queries is available server-side, but not available in this API client due to limitations of the OAuth gem.

Options:

mode

Temporarily changes the API mode. See the MODES array.



84
85
86
87
88
89
90
# File 'lib/dropbox/api.rb', line 84

def download(path, options={})
  path = path.sub(/^\//, '')
  rest = Dropbox.check_path(path).split('/')
  rest << { :ssl => @ssl }
  api_body :get, 'files', root(options), *rest
  #TODO streaming, range queries
end

#entry(path) ⇒ Object Also known as: file, directory, dir

Returns a Dropbox::Entry instance that can be used to work with files or directories in an object-oriented manner.



57
58
59
# File 'lib/dropbox/api.rb', line 57

def entry(path)
  Dropbox::Entry.new(self, path)
end

#event_content(entry, options = {}) ⇒ Object

:nodoc:



472
473
474
475
476
477
478
479
480
# File 'lib/dropbox/api.rb', line 472

def event_content(entry, options={}) # :nodoc:
  request = Dropbox.api_url('event_content', :target_event => entry, :ssl => @ssl, :root => root(options))
  response = api_internal(:get, request)
  begin
    return response.body, JSON.parse(response.header['X-Dropbox-Metadata'])
  rescue JSON::ParserError
    raise ParseError.new(request, response)
  end
end

#event_metadata(target_events, options = {}) ⇒ Object

:nodoc:



468
469
470
# File 'lib/dropbox/api.rb', line 468

def (target_events, options={}) # :nodoc:
  get 'event_metadata', :ssl => @ssl, :root => root(options), :target_events => target_events
end

Returns a cookie-protected URL that the authorized user can use to view the file at the given path. This URL requires an authorized user.

The path is assumed to be relative to the configured mode's root.

Options:

mode

Temporarily changes the API mode. See the MODES array.



361
362
363
364
365
366
367
368
369
370
371
372
# File 'lib/dropbox/api.rb', line 361

def link(path, options={})
  path = path.sub(/^\//, '')
  begin
    rest = Dropbox.check_path(path).split('/')
    rest << { :ssl => @ssl }
    api_response(:get, 'links', root(options), *rest)
  rescue UnsuccessfulResponseError => error
    return error.response['Location'] if error.response.kind_of?(Net::HTTPFound)
    #TODO shouldn't be using rescue blocks for normal program flow
    raise error
  end
end

#list(path, options = {}) ⇒ Object Also known as: ls



463
464
465
# File 'lib/dropbox/api.rb', line 463

def list(path, options={})
  (path, options.merge(:suppress_list => false)).contents
end

#metadata(path, options = {}) ⇒ Object Also known as: info

Returns a Struct containing metadata on a given file or folder. The path is assumed to be relative to the configured mode's root.

If you pass a directory for path, the metadata will also contain a listing of the directory contents (unless the suppress_list option is true).

For information on the schema of the return struct, see the Dropbox API at www.dropbox.com/developers/docs#metadata

The modified key will be converted into a Time instance. The is_dir key will also be available as directory?.

Options:

suppress_list

Set this to true to remove the directory list from the result (only applicable if path is a directory).

limit

Set this value to limit the number of entries returned when listing a directory. If the result has more than this number of entries, a TooManyEntriesError will be raised.

prior_response

The response from a prior call to metadata for the same path. If the metadata has not changed since the prior call, the entire metadata will not be re-downloaded. Operation is undefined if the given value was for a call to metadata with a different path.

mode

Temporarily changes the API mode. See the MODES array.



424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
# File 'lib/dropbox/api.rb', line 424

def (path, options={})
  path = path.sub(/^\//, '')
  args = [
          'metadata',
          root(options)
          ]
  args += Dropbox.check_path(path).split('/')
  args << Hash.new
  args.last[:file_limit] = options[:limit] if options[:limit]
  args.last[:hash] = options[:prior_response].hash if options[:prior_response] and options[:prior_response].hash
  args.last[:list] = !(options[:suppress_list].to_bool)
  args.last[:ssl] = @ssl

  begin
    (get(*args)).to_struct_recursively
  rescue UnsuccessfulResponseError => error
    raise TooManyEntriesError.new(path) if error.response.kind_of?(Net::HTTPNotAcceptable)
    raise FileNotFoundError.new(path) if error.response.kind_of?(Net::HTTPNotFound)
    return options[:prior_response] if error.response.kind_of?(Net::HTTPNotModified)
    raise error
  end
end

#modeObject

Returns the configured API mode.



484
485
486
# File 'lib/dropbox/api.rb', line 484

def mode
  @api_mode ||= :sandbox
end

#mode=(newmode) ⇒ Object

Sets the API mode. See the MODES array.

Raises:

  • (ArgumentError)


490
491
492
493
# File 'lib/dropbox/api.rb', line 490

def mode=(newmode)
  raise ArgumentError, "Unknown API mode #{newmode.inspect}" unless MODES.include?(newmode)
  @api_mode = newmode
end

#move(source, target, options = {}) ⇒ Object Also known as: mv

Moves the source file to the path at target. If target ends with a slash, the file name will remain unchanged. If source and target share the same path but have differing file names, the file will be renamed (see also the rename method). Returns a Struct with metadata for the new file. (See the metadata method.)

Both paths are assumed to be relative to the configured mode's root.

Raises FileNotFoundError if source does not exist. Raises FileExistsError if target already exists.

Options:

mode

Temporarily changes the API mode. See the MODES array.

TODO The API documentation says this method returns 404/403 if the source or target is invalid, but it actually returns 5xx.



318
319
320
321
322
323
324
325
326
327
328
329
# File 'lib/dropbox/api.rb', line 318

def move(source, target, options={})
  source = source.sub(/^\//, '')
  target = target.sub(/^\//, '')
  target << File.basename(source) if target.ends_with?('/')
  begin
    (post('fileops', 'move', :from_path => Dropbox.check_path(source), :to_path => Dropbox.check_path(target), :root => root(options), :ssl => @ssl)).to_struct_recursively
  rescue UnsuccessfulResponseError => error
    raise FileNotFoundError.new(source) if error.response.kind_of?(Net::HTTPNotFound)
    raise FileExistsError.new(target) if error.response.kind_of?(Net::HTTPForbidden)
    raise error
  end
end

#rename(path, new_name, options = {}) ⇒ Object

Renames a file. Takes the same options and raises the same exceptions as the move method.

Calling

session.rename 'path/to/file', 'new_name'

is equivalent to calling

session.move 'path/to/file', 'path/to/new_name'

Raises:

  • (ArgumentError)


343
344
345
346
347
348
349
350
# File 'lib/dropbox/api.rb', line 343

def rename(path, new_name, options={})
  raise ArgumentError, "Names cannot have slashes in them" if new_name.include?('/')
  path = path.sub(/\/$/, '')
  destination = path.split('/')
  destination[destination.size - 1] = new_name
  destination = destination.join('/')
  move path, destination, options
end

#shares(path, options = {}) ⇒ Object

Creates and returns a shareable link to files or folders.

The path is assumed to be relative to the configured mode's root.

Options:

mode

Temporarily changes the API mode. See the MODES array.



383
384
385
386
387
388
389
390
391
392
393
394
# File 'lib/dropbox/api.rb', line 383

def shares(path, options={})
  path = path.sub(/^\//, '')
  rest = Dropbox.check_path(path).split('/')
  
  begin
    return JSON.parse( api_response(:post, 'shares', root(options), *rest).body ).symbolize_keys_recursively
  rescue UnsuccessfulResponseError => error
    return error.response['Location'] if error.response.kind_of?(Net::HTTPFound)
    #TODO shouldn't be using rescue blocks for normal program flow
    raise error
  end
end

#thumbnail(path, options = {}) ⇒ Object

Downloads a minimized thumbnail for a file. Pass the path to the file, optionally the size of the thumbnail you want, and any additional options. See www.dropbox.com/developers/docs#thumbnails for a list of valid size specifiers.

Returns the content of the thumbnail image as a String. The thumbnail data is in JPEG format. Returns nil if the file does not have a thumbnail. You can check if a file has a thumbnail using the metadata method.

Because of the way this API method works, if you pass in the name of a file that does not exist, you will not receive a 404, but instead just get nil.

Options:

format

The image format (see the API documentation for supported formats).

mode

Temporarily changes the API mode. See the MODES array.

Examples:

Get the thumbnail for an image (default thunmbnail size):

session.thumbnail('my/image.jpg')

Get the thumbnail for an image in the medium size:

session.thumbnail('my/image.jpg', 'medium')


122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/dropbox/api.rb', line 122

def thumbnail(path, options={})\
  path = path.sub(/^\//, '')
  rest = Dropbox.check_path(path).split('/')
  rest << { :ssl => @ssl }
  rest.last.merge! options

  begin
    api_body :get, 'thumbnails', root(options), *rest
  rescue Dropbox::UnsuccessfulResponseError => e
    raise unless e.response.code.to_i == 404
    return nil
  end
end

#upload(local_file, remote_path, options = {}) ⇒ Object

Uploads a file to a path relative to the configured mode's root. The remote_path parameter is taken to be the path portion only; the name of the remote file will be identical to that of the local file. You can provide any of the following for the first parameter:

  • a File object (in which case the name of the local file is used),

  • a path to a file (in which case that file's name is used), or

  • a StringIO (in which case the :as option must be specified.

Options:

mode

Temporarily changes the API mode. See the MODES array.

as

Specify a custom name for the uploaded file (required when uploading from a StringIO stream).

timeout

The amount of time to wait for a response from the Dropbox server (in seconds). By default it's 60; set this higher when uploading large files.

Examples:

session.upload 'music.pdf', '/' # upload a file by path to the root directory
session.upload 'music.pdf, 'music/' # upload a file by path to the music folder
session.upload File.new('music.pdf'), '/' # same as the first example
session.upload open('http://www.example.com/index.html'), :as => 'example.html' # upload from a StringIO stream (requires open-uri)


161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/dropbox/api.rb', line 161

def upload(local_file, remote_path, options={})
  if local_file.kind_of?(File) or local_file.kind_of?(Tempfile) then
    file = local_file
    name = local_file.respond_to?(:original_filename) ? local_file.original_filename : File.basename(local_file.path)
    local_path = local_file.path
  elsif local_file.kind_of?(String) then
    file = File.new(local_file)
    name = File.basename(local_file)
    local_path = local_file
  elsif local_file.kind_of?(StringIO) then
    raise(ArgumentError, "Must specify the :as option when uploading from StringIO") unless options[:as]
    file = local_file
    local_path = options[:as]
    
    # hack for bug in UploadIO
    class << file
      attr_accessor :path
    end
    file.path = local_path
  else
    raise ArgumentError, "local_file must be a File, StringIO, or file path"
  end
  
  name = File.basename(options.delete(:as)) if options[:as]

  remote_path = remote_path.sub(/^\//, '')
  remote_path = Dropbox.check_path(remote_path).split('/')

  remote_path << { :ssl => @ssl }
  url = Dropbox.api_url('files', root(options), *remote_path)
  uri = URI.parse(url)

  oauth_request = Net::HTTP::Post.new(uri.path)
  oauth_request.set_form_data 'file' => name

  alternate_host_session = clone_with_host(@ssl ? Dropbox::ALTERNATE_SSL_HOSTS['files'] : Dropbox::ALTERNATE_HOSTS['files'])
  alternate_host_session.instance_variable_get(:@consumer).sign!(oauth_request, @access_token)
  oauth_signature = oauth_request.to_hash['authorization']

  request = Net::HTTP::Post::Multipart.new(uri.path,
                                           'file' => UploadIO.new(
                                                   file,
                                                   'application/octet-stream',
                                                   name))
  request['authorization'] = oauth_signature.join(', ')

  proxy = URI.parse(@proxy || "")
  http = Net::HTTP::Proxy(proxy.host, proxy.port).new(uri.host, uri.port)
  http.use_ssl = @ssl
  http.read_timeout = options[:timeout] if options[:timeout]
  response = http.request(request)

  if response.kind_of?(Net::HTTPSuccess) then
    begin
      return JSON.parse(response.body).symbolize_keys_recursively.to_struct_recursively
    rescue JSON::ParserError
      raise ParseError.new(uri.to_s, response)
    end
  else
    raise UnsuccessfulResponseError.new(uri.to_s, response)
  end
end