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.



227
228
229
230
231
232
233
234
235
236
237
238
# File 'lib/dropbox/api.rb', line 227

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.



253
254
255
256
257
258
259
260
261
262
# File 'lib/dropbox/api.rb', line 253

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.



276
277
278
279
280
281
282
283
284
285
286
# File 'lib/dropbox/api.rb', line 276

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:



435
436
437
438
439
440
441
442
443
# File 'lib/dropbox/api.rb', line 435

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:



431
432
433
# File 'lib/dropbox/api.rb', line 431

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.



349
350
351
352
353
354
355
356
357
358
359
360
# File 'lib/dropbox/api.rb', line 349

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



426
427
428
# File 'lib/dropbox/api.rb', line 426

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



387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
# File 'lib/dropbox/api.rb', line 387

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[: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.



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

def mode
  @api_mode ||= :sandbox
end

#mode=(newmode) ⇒ Object

Sets the API mode. See the MODES array.

Raises:

  • (ArgumentError)


453
454
455
456
# File 'lib/dropbox/api.rb', line 453

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.



306
307
308
309
310
311
312
313
314
315
316
317
# File 'lib/dropbox/api.rb', line 306

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)


331
332
333
334
335
336
337
338
# File 'lib/dropbox/api.rb', line 331

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

#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).

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)


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
203
204
205
206
207
208
209
210
# File 'lib/dropbox/api.rb', line 158

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]
  else
    raise ArgumentError, "local_file must be a File, StringIO, or file path"
  end
  
  name = options.delete(:as).to_s 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.convert!(
                                                   file,
                                                   'application/octet-stream',
                                                   name,
                                                   local_path))
  request['authorization'] = oauth_signature.join(', ')

  proxy = URI.parse(@proxy || "")
  response = Net::HTTP::Proxy(proxy.host, proxy.port).new(uri.host, uri.port).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