dropbox-api 0.1.0

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,48 @@
1
+ module Dropbox
2
+ module API
3
+
4
+ class Raw
5
+
6
+ attr_accessor :connection
7
+
8
+ def initialize(options = {})
9
+ @connection = options[:connection]
10
+ end
11
+
12
+ def self.add_method(method, action, options = {})
13
+ # Add the default root bit, but allow it to be disabled by a config option
14
+ root = options[:root] == false ? '' : "options[:root] ||= '#{Dropbox::API::Config.mode}'"
15
+ self.class_eval <<-STR
16
+ def #{options[:as] || action}(options = {})
17
+ #{root}
18
+ request(:#{options[:endpoint] || 'main'}, :#{method}, "#{action}", options)
19
+ end
20
+ STR
21
+ end
22
+
23
+ def request(endpoint, method, action, data = {})
24
+ action.sub! ':root', data.delete(:root) if action.match ':root'
25
+ action.sub! ':path', data.delete(:path) if action.match ':path'
26
+ connection.send(method, endpoint, action, data)
27
+ end
28
+
29
+ add_method :get, "/account/info", :as => 'account', :root => false
30
+
31
+ add_method :get, "/metadata/:root/:path", :as => 'metadata'
32
+ add_method :get, "/revisions/:root/:path", :as => 'revisions'
33
+ add_method :post, "/restore/:root/:path", :as => 'restore'
34
+ add_method :get, "/search/:root/:path", :as => 'search'
35
+ add_method :post, "/shares/:root/:path", :as => 'shares'
36
+ add_method :post, "/media/:root/:path", :as => 'media'
37
+
38
+ add_method :get_raw, "/thumbnails/:root/:path", :as => 'thumbnails', :endpoint => :content
39
+
40
+ add_method :post, "/fileops/copy", :as => "copy"
41
+ add_method :post, "/fileops/create_folder", :as => "create_folder"
42
+ add_method :post, "/fileops/delete", :as => "delete"
43
+ add_method :post, "/fileops/move", :as => "move"
44
+
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,34 @@
1
+ require "dropbox-api/connection/requests"
2
+
3
+ module Dropbox
4
+ module API
5
+
6
+ class Connection
7
+
8
+ include Dropbox::API::Connection::Requests
9
+
10
+ attr_accessor :consumers
11
+ attr_accessor :tokens
12
+
13
+ def initialize(options = {})
14
+ @options = options
15
+ @consumers = {}
16
+ @tokens = {}
17
+ Dropbox::API::Config.endpoints.each do |endpoint, url|
18
+ @consumers[endpoint] = Dropbox::API::OAuth.consumer(endpoint)
19
+ @tokens[endpoint] = Dropbox::API::OAuth.access_token(@consumers[endpoint], options)
20
+ end
21
+ end
22
+
23
+ def consumer(endpoint = :main)
24
+ @consumers[endpoint]
25
+ end
26
+
27
+ def token(endpoint = :main)
28
+ @tokens[endpoint]
29
+ end
30
+
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,63 @@
1
+ module Dropbox
2
+ module API
3
+
4
+ class Connection
5
+
6
+ module Requests
7
+
8
+ def request(options = {})
9
+ response = yield
10
+ raise Dropbox::API::Error::ConnectionFailed if !response
11
+ status = response.code.to_i
12
+ case status
13
+ when 401
14
+ raise Dropbox::API::Error::Unauthorized
15
+ when 403
16
+ parsed = Yajl::Parser.parse(response.body)
17
+ raise Dropbox::API::Error::Forbidden.new(parsed["error"])
18
+ when 404
19
+ raise Dropbox::API::Error::NotFound
20
+ when 400, 406
21
+ parsed = Yajl::Parser.parse(response.body)
22
+ raise Dropbox::API::Error.new(parsed["error"])
23
+ when 300..399
24
+ raise Dropbox::API::Error::Redirect
25
+ when 500..599
26
+ raise Dropbox::API::Error
27
+ else
28
+ options[:raw] ? response.body : Yajl::Parser.parse(response.body)
29
+ end
30
+ end
31
+
32
+ def get_raw(endpoint, path, data = {}, headers = {})
33
+ query = Dropbox::API::Util.query(data)
34
+ request(:raw => true) do
35
+ token(endpoint).get "#{Dropbox::API::Config.prefix}#{URI.parse(URI.encode(path))}?#{URI.parse(URI.encode(query))}", headers
36
+ end
37
+ end
38
+
39
+ def get(endpoint, path, data = {}, headers = {})
40
+ query = Dropbox::API::Util.query(data)
41
+ request do
42
+ token(endpoint).get "#{Dropbox::API::Config.prefix}#{URI.parse(URI.encode(path))}?#{URI.parse(URI.encode(query))}", headers
43
+ end
44
+ end
45
+
46
+ def post(endpoint, path, data = {}, headers = {})
47
+ request do
48
+ token(endpoint).post "#{Dropbox::API::Config.prefix}#{URI.parse(URI.encode(path))}", data, headers
49
+ end
50
+ end
51
+
52
+ def put(endpoint, path, data = {}, headers = {})
53
+ request do
54
+ token(endpoint).put "#{Dropbox::API::Config.prefix}#{URI.parse(URI.encode(path))}", data, headers
55
+ end
56
+ end
57
+
58
+ end
59
+
60
+ end
61
+
62
+ end
63
+ end
@@ -0,0 +1,20 @@
1
+ module Dropbox
2
+ module API
3
+
4
+ class Dir < Dropbox::API::Object
5
+
6
+ include Dropbox::API::Fileops
7
+
8
+ def ls(path_to_list = '')
9
+ data = client.raw.metadata :path => path + path_to_list
10
+ if data['is_dir']
11
+ Dropbox::API::Object.convert(data.delete('contents') || [], client)
12
+ else
13
+ [Dropbox::API::Object.convert(data, client)]
14
+ end
15
+ end
16
+
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,39 @@
1
+ module Dropbox
2
+ module API
3
+
4
+ class File < Dropbox::API::Object
5
+
6
+ include Dropbox::API::Fileops
7
+
8
+ def revisions(options = {})
9
+ response = client.raw.revisions({ :path => self.path }.merge(options))
10
+ Dropbox::API::Object.convert(response, client)
11
+ end
12
+
13
+ def restore(rev, options = {})
14
+ response = client.raw.restore({ :rev => rev, :path => self.path }.merge(options))
15
+ self.update response
16
+ end
17
+
18
+ def share_url(options = {})
19
+ response = client.raw.shares({ :path => self.path }.merge(options))
20
+ Dropbox::API::Object.init(response, client)
21
+ end
22
+
23
+ def direct_url(options = {})
24
+ response = client.raw.media({ :path => self.path }.merge(options))
25
+ Dropbox::API::Object.init(response, client)
26
+ end
27
+
28
+ def thumbnail(options = {})
29
+ client.raw.thumbnails({ :path => self.path }.merge(options))
30
+ end
31
+
32
+ def download
33
+ client.download(self.path)
34
+ end
35
+
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,28 @@
1
+ module Dropbox
2
+ module API
3
+
4
+ module Fileops
5
+
6
+ def copy(to, options = {})
7
+ response = client.raw.copy({ :from_path => self.path, :to_path => to }.merge(options))
8
+ self.update response
9
+ end
10
+
11
+ def move(to, options = {})
12
+ response = client.raw.move({ :from_path => self.path, :to_path => to }.merge(options))
13
+ self.update response
14
+ end
15
+
16
+ def delete(options = {})
17
+ response = client.raw.delete({ :path => self.path }.merge(options))
18
+ self.update response
19
+ end
20
+
21
+ def path
22
+ self['path'].sub(/^\//, '')
23
+ end
24
+
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,39 @@
1
+ module Dropbox
2
+ module API
3
+
4
+ class Object < Hashie::Mash
5
+ attr_accessor :client
6
+
7
+ def self.init(response, client)
8
+ instance = self.new(response)
9
+ instance.client = client
10
+ instance
11
+ end
12
+
13
+ def self.resolve_class(hash)
14
+ hash['is_dir'] == true ? Dropbox::API::Dir : Dropbox::API::File
15
+ end
16
+
17
+ def self.convert(array_or_object, client)
18
+ if array_or_object.is_a?(Array)
19
+ array_or_object.collect do |item|
20
+ resolve_class(item).init(item, client)
21
+ end
22
+ else
23
+ resolve_class(array_or_object).init(array_or_object, client)
24
+ end
25
+ end
26
+
27
+ # Kill off the ability for recursive conversion
28
+ def deep_update(other_hash)
29
+ other_hash.each_pair do |k,v|
30
+ key = convert_key(k)
31
+ regular_writer(key, convert_value(v, true))
32
+ end
33
+ self
34
+ end
35
+
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,48 @@
1
+ module Dropbox
2
+ module API
3
+
4
+ class Tasks
5
+
6
+ extend Rake::DSL if defined? Rake::DSL
7
+
8
+ def self.install
9
+
10
+ namespace :dropbox do
11
+ desc "Authorize wizard for Dropbox API"
12
+ task :authorize do
13
+ require "dropbox-api"
14
+ require "cgi"
15
+ print "Enter consumer key: "
16
+ consumer_key = $stdin.gets.chomp
17
+ print "Enter consumer secret: "
18
+ consumer_secret = $stdin.gets.chomp
19
+
20
+ Dropbox::API::Config.app_key = consumer_key
21
+ Dropbox::API::Config.app_secret = consumer_secret
22
+
23
+ consumer = Dropbox::API::OAuth.consumer(:authorize)
24
+ request_token = consumer.get_request_token
25
+ puts "\nGo to this url and click 'Authorize' to get the token:"
26
+ puts request_token.authorize_url
27
+ query = request_token.authorize_url.split('?').last
28
+ params = CGI.parse(query)
29
+ token = params['oauth_token'].first
30
+ print "\nOnce you authorize the app on Dropbox, press enter... "
31
+ $stdin.gets.chomp
32
+
33
+ access_token = request_token.get_access_token(:oauth_verifier => token)
34
+
35
+ puts "\nAuthorization complete!:\n\n"
36
+ puts " Dropbox::Api::Config.app_key = '#{consumer.key}'"
37
+ puts " Dropbox::Api::Config.app_secret = '#{consumer.secret}'"
38
+ puts " client = Dropbox::Api::Client.new(:token => '#{access_token.token}', :secret => '#{access_token.secret}')"
39
+ puts "\n"
40
+ end
41
+ end
42
+
43
+ end
44
+
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,27 @@
1
+ module Dropbox
2
+ module API
3
+
4
+ module Config
5
+
6
+ class << self
7
+ attr_accessor :endpoints
8
+ attr_accessor :prefix
9
+ attr_accessor :app_key
10
+ attr_accessor :app_secret
11
+ attr_accessor :mode
12
+ end
13
+
14
+ self.endpoints = {
15
+ :main => "https://api.dropbox.com",
16
+ :content => "https://api-content.dropbox.com",
17
+ :authorize => "https://www.dropbox.com"
18
+ }
19
+ self.prefix = "/1"
20
+ self.app_key = nil
21
+ self.app_secret = nil
22
+ self.mode = 'sandbox'
23
+
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,16 @@
1
+ module Dropbox
2
+ module API
3
+
4
+ class Error < Exception
5
+
6
+ class ConnectionFailed < Exception; end
7
+ class Config < Exception; end
8
+ class Unauthorized < Exception; end
9
+ class Forbidden < Exception; end
10
+ class NotFound < Exception; end
11
+ class Redirect < Exception; end
12
+
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,29 @@
1
+ module Dropbox
2
+ module API
3
+
4
+ module OAuth
5
+
6
+ class << self
7
+
8
+ def consumer(endpoint)
9
+ if !Dropbox::API::Config.app_key or !Dropbox::API::Config.app_secret
10
+ raise Dropbox::API::Error::Config.new("app_key or app_secret not provided")
11
+ end
12
+ ::OAuth::Consumer.new(Dropbox::API::Config.app_key, Dropbox::API::Config.app_secret,
13
+ :site => Dropbox::API::Config.endpoints[endpoint],
14
+ :request_token_path => Dropbox::API::Config.prefix + "/oauth/request_token",
15
+ :authorize_path => Dropbox::API::Config.prefix + "/oauth/authorize",
16
+ :access_token_path => Dropbox::API::Config.prefix + "/oauth/access_token")
17
+ end
18
+
19
+ def access_token(consumer, options = {})
20
+ ::OAuth::AccessToken.new(consumer, options[:token], options[:secret])
21
+ end
22
+
23
+ end
24
+
25
+ end
26
+
27
+ end
28
+ end
29
+
@@ -0,0 +1,17 @@
1
+ module Dropbox
2
+ module API
3
+
4
+ module Util
5
+
6
+ class << self
7
+
8
+ def query(data)
9
+ data.inject([]) { |memo, entry| memo.push(entry.join('=')); memo }.join('&')
10
+ end
11
+
12
+ end
13
+
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,5 @@
1
+ module Dropbox
2
+ module API
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,4 @@
1
+ app_key: # CONSUMER KEY
2
+ app_secret: # CONSUMER SECRET
3
+ token: # ACCESS TOKEN
4
+ secret: # ACCESS SECRET
Binary file
@@ -0,0 +1,92 @@
1
+ require "spec_helper"
2
+
3
+ describe Dropbox::API::Client do
4
+
5
+ before do
6
+ # pending
7
+ @client = Dropbox::Spec.instance
8
+ end
9
+
10
+ describe "#initialize" do
11
+
12
+ it "has a handle to the connection" do
13
+ @client.connection.should be_an_instance_of(Dropbox::API::Connection)
14
+ end
15
+
16
+ end
17
+
18
+ describe "#account" do
19
+
20
+ it "retrieves the account object" do
21
+ response = @client.account
22
+ response.should be_an_instance_of(Dropbox::API::Object)
23
+ end
24
+
25
+ end
26
+
27
+ describe "#ls" do
28
+
29
+ it "returns an array of files and dirs" do
30
+ result = @client.ls
31
+ result.should be_an_instance_of(Array)
32
+ end
33
+
34
+ it "returns a single item array of if we ls a file" do
35
+ result = @client.ls
36
+ first_file = result.detect { |f| f.class == Dropbox::API::File }
37
+ result = @client.ls first_file.path
38
+ result.should be_an_instance_of(Array)
39
+ end
40
+
41
+ end
42
+
43
+ describe "#mkdir" do
44
+
45
+ it "returns an array of files and dirs" do
46
+ dirname = "test/test-dir-#{Dropbox::Spec.namespace}"
47
+ response = @client.mkdir dirname
48
+ response.path.should == dirname
49
+ response.should be_an_instance_of(Dropbox::API::Dir)
50
+ end
51
+
52
+ end
53
+
54
+ describe "#upload" do
55
+
56
+ it "puts the file in dropbox" do
57
+ filename = "test/test-#{Dropbox::Spec.namespace}.txt"
58
+ response = @client.upload filename, "Some file"
59
+ response.path.should == filename
60
+ response.bytes.should == 9
61
+ end
62
+
63
+ end
64
+
65
+ describe "#search" do
66
+
67
+ it "finds a file" do
68
+ filename = "test/searchable-test-#{Dropbox::Spec.namespace}.txt"
69
+ @client.upload filename, "Some file"
70
+ response = @client.search "searchable-test-#{Dropbox::Spec.namespace}", :path => 'test'
71
+ response.size.should == 1
72
+ response.first.class.should == Dropbox::API::File
73
+ end
74
+
75
+ end
76
+
77
+ describe "#download" do
78
+
79
+ it "downloads a file from Dropbox" do
80
+ file = @client.download 'test/test.txt'
81
+ file.should == "Some file"
82
+ end
83
+
84
+ it "raises a 404 when a file is not found in Dropbox" do
85
+ lambda {
86
+ @client.download 'test/no.txt'
87
+ }.should raise_error(Dropbox::API::Error::NotFound)
88
+ end
89
+
90
+ end
91
+
92
+ end