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.



219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/dropbox/api.rb', line 219

def copy(source, target, options={})
  source.sub! /^\//, ''
  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.



245
246
247
248
249
250
251
252
253
254
# File 'lib/dropbox/api.rb', line 245

def create_folder(path, options={})
  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.



268
269
270
271
272
273
274
275
276
277
278
# File 'lib/dropbox/api.rb', line 268

def delete(path, options={})
  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.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:



427
428
429
430
431
432
433
434
435
# File 'lib/dropbox/api.rb', line 427

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:



423
424
425
# File 'lib/dropbox/api.rb', line 423

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.



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

def link(path, options={})
  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



418
419
420
# File 'lib/dropbox/api.rb', line 418

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.

mode

Temporarily changes the API mode. See the MODES array.

TODO hash option seems to return HTTPBadRequest for now



379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
# File 'lib/dropbox/api.rb', line 379

def (path, options={})
  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[:hash] if options[: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 :not_modified if error.kind_of?(Net::HTTPNotModified)
    raise error
  end
end

#modeObject

Returns the configured API mode.



439
440
441
# File 'lib/dropbox/api.rb', line 439

def mode
  @api_mode ||= :sandbox
end

#mode=(newmode) ⇒ Object

Sets the API mode. See the MODES array.

Raises:

  • (ArgumentError)


445
446
447
448
# File 'lib/dropbox/api.rb', line 445

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.



298
299
300
301
302
303
304
305
306
307
308
309
# File 'lib/dropbox/api.rb', line 298

def move(source, target, options={})
  source.sub! /^\//, ''
  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)


323
324
325
326
327
328
329
330
# File 'lib/dropbox/api.rb', line 323

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

#thumbnail(*args) ⇒ 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:

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')

Raises:

  • (ArgumentError)


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

def thumbnail(*args)
  options = args.extract_options!
  path = args.shift
  size = args.shift
  raise ArgumentError, "thumbnail takes a path, an optional size, and optional options" unless path.kind_of?(String) and (size.kind_of?(String) or size.nil?) and args.empty?
  
  path.sub! /^\//, ''
  rest = Dropbox.check_path(path).split('/')
  rest << { :ssl => @ssl }
  rest.last[:size] = size if size
  
  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, or

  • a path to a file, in which case that file’s name is used.

Options:

mode

Temporarily changes the API mode. See the MODES array.

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


157
158
159
160
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
# File 'lib/dropbox/api.rb', line 157

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
  else
    raise ArgumentError, "local_file must be a File or file path"
  end

  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.convert!(
                                                   file,
                                                   'application/octet-stream',
                                                   name,
                                                   local_path))
  request['authorization'] = oauth_signature.join(', ')
  
  response = Net::HTTP.start(uri.host, uri.port) { |http| 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