dropbox 0.0.2 → 0.0.5

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.
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ pkg/
2
+ .DS_Store
3
+ *.gem
data/README.markdown ADDED
@@ -0,0 +1,10 @@
1
+ DropBox
2
+ =======
3
+
4
+ A simple Ruby API for DropBox
5
+
6
+ Usage
7
+ -----
8
+
9
+ d = DropBox.new("you@email.com","password!")
10
+ p d.list("/")
data/Rakefile CHANGED
@@ -1,22 +1,35 @@
1
1
  require 'rubygems'
2
2
  require 'rake'
3
+ require 'spec/rake/spectask'
3
4
 
4
5
  begin
5
6
  require 'jeweler'
6
7
  Jeweler::Tasks.new do |gem|
7
8
  gem.name = "dropbox"
8
- gem.summary = %Q{A simple Ruby API for DropBox }
9
- gem.description = <<-DESC
10
- A simple Ruby API for DropBox
11
- DESC
12
- gem.email = ""
13
- gem.homepage = ""
14
- gem.authors = []
15
- gem.files = FileList["[A-Z]*", "{lib}/**/*"]
16
-
17
- gem.add_dependency("nokogiri")
18
- gem.add_dependency("mechanize")
9
+ open("VERSION") do |f|
10
+ gem.version = f.read
11
+ end
12
+ gem.summary = %Q{A Simple DropBox API in Ruby}
13
+ gem.description = %Q{A Simple DropBox API in Ruby}
14
+ gem.email = "tys@tvg.ca"
15
+ gem.homepage = "http://github.com/tvongaza/DropBox"
16
+ gem.authors = ["Tys von Gaza","JP Hastings-Spital","Chris Searle","Nicholas A. Evans"]
17
+ gem.add_development_dependency "Shoulda"
18
+ gem.add_dependency "mechanize"
19
+ gem.add_dependency "nokogiri"
20
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
19
21
  end
22
+ Jeweler::GemcutterTasks.new
20
23
  rescue LoadError
21
- puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
24
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
25
+ end
26
+
27
+ desc "Run all examples"
28
+ Spec::Rake::SpecTask.new('examples') do |t|
29
+ t.spec_files = FileList['examples/**/*.rb']
30
+ t.libs << 'lib'
31
+ t.spec_opts = %w[-c -fs]
32
+ t.ruby_opts = %w[-rubygems]
22
33
  end
34
+
35
+ task :default => :examples
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.2
1
+ 0.0.5
@@ -0,0 +1,98 @@
1
+ require 'spec'
2
+ require 'dropbox'
3
+
4
+ %w[DROPBOX_EMAIL DROPBOX_PASSWORD].each do |var|
5
+ raise "Need to set #{var} to run the specs." unless ENV[var]
6
+ end
7
+ ENV["DROPBOX_FOLDER_NAMESPACE"] ||= "dropboxrb/specroot"
8
+
9
+ describe DropBox do
10
+ before :all do
11
+ # TODO: check for existance of DROPBOX_FOLDER_NAMESPACE
12
+ # TODO: ensure the folder starts out empty
13
+ @connection = connection
14
+ end
15
+
16
+ def connection
17
+ DropBox.new(ENV["DROPBOX_EMAIL"],
18
+ ENV["DROPBOX_PASSWORD"],
19
+ ENV["DROPBOX_FOLDER_NAMESPACE"])
20
+ end
21
+
22
+ def dirent_for(name)
23
+ @connection.list.find {|f| f["name"] == name }
24
+ end
25
+
26
+ context "uploading a file" do
27
+ before :all do
28
+ @file = Tempfile.new("dbrb")
29
+ @contents = <<-EOF
30
+ This is a temp file used by the dropbox.rb specs.
31
+ foo
32
+ bar
33
+ baz
34
+ EOF
35
+ @file.print @contents
36
+ @file.flush
37
+ @basename = File.basename(@file.path)
38
+ @connection.create(@file.path)
39
+ end
40
+ after :all do
41
+ # TODO: fix the bugs that force us to reset the connection.before deleting the file
42
+ @connection.login
43
+ @connection.destroy @basename
44
+ @file.close!
45
+ end
46
+
47
+ it "should be listed at the toplevel" do
48
+ @connection.list.map {|f| f["name"] }.should include(@basename)
49
+ end
50
+
51
+ it "should be listed as not a directory" do
52
+ dirent_for(@basename)["directory"].should be_false
53
+ end
54
+
55
+ it "should have a path relative to the folder namespace (containing query params)" do
56
+ dirent_for(@basename)["path"].should match(%r{^/#{@basename}\?w=.*$})
57
+ end
58
+
59
+ it "should be retrievable" do
60
+ path = dirent_for(@basename)["path"]
61
+ @connection.get(path).should == @contents
62
+ end
63
+
64
+ end
65
+
66
+ context "creating a directory at the toplevel" do
67
+ before { @connection.create_directory("spec_create_dir") }
68
+ after { @connection.destroy("spec_create_dir") }
69
+
70
+ it "should be listed at the toplevel" do
71
+ @connection.list.map {|f| f["name"] }.should include("spec_create_dir")
72
+ end
73
+
74
+ it "should be listed as a directory" do
75
+ dirent_for("spec_create_dir")["directory"].should be_true
76
+ end
77
+
78
+ it "should have a path relative to the folder namespace" do
79
+ dirent_for("spec_create_dir")["path"].should == "/spec_create_dir"
80
+ end
81
+
82
+ it "should contain only a link to the parent directory" do
83
+ list = @connection.list("spec_create_dir")
84
+ list.should have(1).item
85
+ dir = list.first
86
+ dir["name"].should == "Parent folder"
87
+ dir["directory"].should be_true
88
+ dir["path"].should == ""
89
+ end
90
+
91
+ end
92
+
93
+ #context "renaming a file"
94
+ #context "deleting a file"
95
+
96
+ end
97
+
98
+ #describe DropBox, "folder namespace"
data/lib/dropbox.rb CHANGED
@@ -1 +1,14 @@
1
- require 'dropbox/dropbox'
1
+ require 'dropbox/dropbox'
2
+
3
+ ## Should I include this?
4
+ require 'delegate'
5
+
6
+ # Allows percentages to be inspected and stringified in human
7
+ # form "33.3%", but kept in a float format for mathmatics
8
+ class Percentage < DelegateClass(Float)
9
+ def to_s(decimalplaces = 0)
10
+ (((self * 10**(decimalplaces+2)).round)/10**decimalplaces).to_s+"%"
11
+ end
12
+ alias :inspect :to_s
13
+ end
14
+
@@ -1,8 +1,7 @@
1
- require 'rubygems'
2
1
  require 'nokogiri'
3
2
  require 'mechanize'
4
3
 
5
- WWW::Mechanize.html_parser = Nokogiri::HTML
4
+ Mechanize.html_parser = Nokogiri::HTML
6
5
 
7
6
  # TODO: Tests (mock out expected DropBox output)
8
7
  # TODO: Directory object, File object
@@ -13,35 +12,12 @@ class DropBox
13
12
  def initialize(email, password, folder_namespace = "")
14
13
  @email = email
15
14
  @password = password
16
- @agent = WWW::Mechanize.new
15
+ @agent = Mechanize.new
17
16
  @folder_namespace = folder_namespace.gsub(/^\//,"")
18
17
  @logged_in = false
19
18
  end
20
19
 
21
- def login
22
- page = @agent.get('https://www.dropbox.com/login')
23
- login_form = page.forms.detect { |f| f.action == "/login" }
24
- login_form.login_email = @email
25
- login_form.login_password = @password
26
-
27
- home_page = @agent.submit(login_form)
28
- # todo check if we are logged in! (ie search for email and "Log out"
29
- @logged_in = true
30
- @token = home_page.at('//script[contains(text(), "TOKEN")]').content.match("TOKEN: '(.*)',$")[1]
31
-
32
- # check if we have our namespace
33
-
34
- home_page
35
- end
36
-
37
- def login_filter
38
- login unless @logged_in
39
- end
40
-
41
- def list(path = "/")
42
- index(path)
43
- end
44
-
20
+ # Lists all the files and folders in a given directory
45
21
  def index(path = "/")
46
22
  login_filter
47
23
  path = namespace_path(path)
@@ -58,9 +34,14 @@ class DropBox
58
34
  if match_data = details['url'].match(/^\/browse_plain(.*)$/)
59
35
  details['directory'] = true
60
36
  details['path'] = normalize_namespace(match_data[1])
61
- else
37
+ elsif match_data = details['url'].match(%r{^https?://[^/]*/get(.*)$})
62
38
  details['directory'] = false
63
- details['path'] = normalize_namespace(details['url'][33..-1])
39
+ details['path'] = normalize_namespace(match_data[1])
40
+ elsif match_data = details['url'].match(%r{^https?://[^/]*/u/\d*/(.*)$})
41
+ details['directory'] = false
42
+ details['path'] = "Public/#{match_data[1]}"
43
+ else
44
+ raise "could not parse path from Dropbox URL: #{details['url'] }"
64
45
  end
65
46
 
66
47
  details
@@ -69,6 +50,9 @@ class DropBox
69
50
  return listing
70
51
  end
71
52
 
53
+ alias :list :index
54
+
55
+ # Lists the full history for a file on DropBox
72
56
  def list_history(path)
73
57
  login_filter
74
58
 
@@ -91,11 +75,8 @@ class DropBox
91
75
 
92
76
  return listing
93
77
  end
94
-
95
- def get(path)
96
- show(path)
97
- end
98
78
 
79
+ # Downloads the specified file from DropBox
99
80
  def show(path)
100
81
  # change to before filter
101
82
  login_filter
@@ -105,15 +86,18 @@ class DropBox
105
86
  #https://dl-web.dropbox.com/get/testing.txt?w=0ff80d5d&sjid=125987568
106
87
  @agent.get("https://dl-web.dropbox.com/get/#{path}").content
107
88
  end
108
-
89
+
90
+ alias :get :show
91
+
92
+ # Creates a directory
109
93
  def create_directory(new_path, destination = "/" )
110
94
  # change to before filter
111
95
  login unless @logged_in
112
96
  destination = namespace_path(destination)
113
- @agent.post("/cmd/new#{destination}",{"to_path"=>new_path, "folder"=>"yes", "t" => @token })
114
- # check status code
97
+ @agent.post("/cmd/new#{destination}",{"to_path"=>new_path, "folder"=>"yes", "t" => @token }).code == "200"
115
98
  end
116
99
 
100
+ # Uploads a file to DropBox under the given filename
117
101
  def create(file, destination = "/")
118
102
  # change to before filter
119
103
  if @logged_in
@@ -122,39 +106,44 @@ class DropBox
122
106
  home_page = login
123
107
  end
124
108
 
125
- destination = namespace_path(destination)
126
-
127
109
  upload_form = home_page.forms.detect{ |f| f.action == "https://dl-web.dropbox.com/upload" }
128
- upload_form.dest = "/#{@folder_namespace}/#{destination}"
110
+ upload_form.dest = namespace_path(destination)
129
111
  upload_form.file_uploads.first.file_name = file if file
130
112
 
131
- @agent.submit(upload_form)
132
- end
133
-
134
- def update(file, destination = "/")
135
- create(file, destination)
113
+ @agent.submit(upload_form).code == "200"
136
114
  end
137
115
 
116
+ alias :update :create
117
+
118
+ # Renames a file or folder in the DropBox
138
119
  def rename(file, destination)
139
120
  login_filter
140
121
  file = namespace_path(file)
141
122
  destination = namespace_path(destination)
142
- @agent.post("/cmd/rename#{file}", {"to_path"=> destination, "t" => @token })
123
+ @agent.post("/cmd/rename#{file}", {"to_path"=> destination, "t" => @token }).code == "200"
143
124
  end
144
125
 
126
+ # Deletes a file/folder from the DropBox (accepts string path or an array of string paths)
145
127
  def destroy(paths)
146
128
  login_filter
147
129
  paths = [paths].flatten
148
130
  paths = paths.collect { |path| namespace_path(path) }
149
- @agent.post("/cmd/delete", {"files"=> paths, "t" => @token })
131
+ @agent.post("/cmd/delete", {"files"=> paths, "t" => @token }).code == "200"
150
132
  end
151
133
 
134
+ # Permanently deletes a file from the DropBox (no history!) accepts arrays, as #destroy does
152
135
  def purge(paths)
153
136
  login_filter
154
137
  paths = [paths].flatten
155
138
  paths = paths.collect { |path| namespace_path(path) }
156
- @agent.post("/cmd/purge", {"files"=> paths, "t" => @token })
139
+ @agent.post("/cmd/purge", {"files"=> paths, "t" => @token }).code == "200"
157
140
  end
141
+
142
+ # Will give a hash of the amount of space left on the DropBox, the amound used, the calculated amount free (all as a 1 d.p. rounded GB value) and the percentage used (scraped)
143
+ def usage_stats
144
+ login_filter
145
+ @agent.get("/account").at('#usage-percent').content.scan(/(\d+(?:\.\d+)?)%\ used\ \((\d+(?:\.\d+)?)([MG])B of (\d+(?:\.\d+)?)GB\)/).collect{|d| {:used => d[1].to_f * ((d[2] == "M") ? 1024 : 1), :total => d[3].to_f, :free => (d[3].to_f - d[1].to_f * ((d[2] == "M") ? 1024 : 1)), :percent => Percentage.new(d[0].to_f/100)} }[0]
146
+ end
158
147
 
159
148
  private
160
149
  def namespace_path(path)
@@ -171,4 +160,27 @@ class DropBox
171
160
  file.gsub(/^\/#{@folder_namespace}/,"")
172
161
  end
173
162
 
174
- end
163
+ def login
164
+ page = @agent.get('https://www.dropbox.com/login')
165
+ login_form = page.forms.detect { |f| f.action == "/login" }
166
+ login_form.login_email = @email
167
+ login_form.login_password = @password
168
+
169
+ home_page = @agent.submit(login_form)
170
+ # todo check if we are logged in! (ie search for email and "Log out"
171
+ @logged_in = true
172
+ @token = home_page.at('//script[contains(text(), "TOKEN")]').content.match("TOKEN: '(.*)',$")[1]
173
+
174
+ # check if we have our namespace
175
+
176
+ home_page
177
+ end
178
+
179
+ def login_filter
180
+ login unless @logged_in
181
+ end
182
+ end
183
+
184
+ # this file doesn't use the standard ruby indent settings, so the following
185
+ # modeline will make sure that the whitespace stays consistent.
186
+ # vim:noexpandtab tabstop=4 shiftwidth=4
metadata CHANGED
@@ -1,55 +1,79 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dropbox
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 5
9
+ version: 0.0.5
5
10
  platform: ruby
6
- authors: []
7
-
11
+ authors:
12
+ - Tys von Gaza
13
+ - JP Hastings-Spital
14
+ - Chris Searle
15
+ - Nicholas A. Evans
8
16
  autorequire:
9
17
  bindir: bin
10
18
  cert_chain: []
11
19
 
12
- date: 2010-01-06 00:00:00 +01:00
20
+ date: 2010-03-04 00:00:00 -07:00
13
21
  default_executable:
14
22
  dependencies:
15
23
  - !ruby/object:Gem::Dependency
16
- name: nokogiri
17
- type: :runtime
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
24
+ name: Shoulda
25
+ prerelease: false
26
+ requirement: &id001 !ruby/object:Gem::Requirement
20
27
  requirements:
21
28
  - - ">="
22
29
  - !ruby/object:Gem::Version
30
+ segments:
31
+ - 0
23
32
  version: "0"
24
- version:
33
+ type: :development
34
+ version_requirements: *id001
25
35
  - !ruby/object:Gem::Dependency
26
36
  name: mechanize
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ segments:
43
+ - 0
44
+ version: "0"
27
45
  type: :runtime
28
- version_requirement:
29
- version_requirements: !ruby/object:Gem::Requirement
46
+ version_requirements: *id002
47
+ - !ruby/object:Gem::Dependency
48
+ name: nokogiri
49
+ prerelease: false
50
+ requirement: &id003 !ruby/object:Gem::Requirement
30
51
  requirements:
31
52
  - - ">="
32
53
  - !ruby/object:Gem::Version
54
+ segments:
55
+ - 0
33
56
  version: "0"
34
- version:
35
- description: |
36
- A simple Ruby API for DropBox
37
-
38
- email: ""
57
+ type: :runtime
58
+ version_requirements: *id003
59
+ description: A Simple DropBox API in Ruby
60
+ email: tys@tvg.ca
39
61
  executables: []
40
62
 
41
63
  extensions: []
42
64
 
43
65
  extra_rdoc_files:
44
- - README
66
+ - README.markdown
45
67
  files:
46
- - README
68
+ - .gitignore
69
+ - README.markdown
47
70
  - Rakefile
48
71
  - VERSION
72
+ - examples/dropbox_spec.rb
49
73
  - lib/dropbox.rb
50
74
  - lib/dropbox/dropbox.rb
51
75
  has_rdoc: true
52
- homepage: ""
76
+ homepage: http://github.com/tvongaza/DropBox
53
77
  licenses: []
54
78
 
55
79
  post_install_message:
@@ -61,20 +85,22 @@ required_ruby_version: !ruby/object:Gem::Requirement
61
85
  requirements:
62
86
  - - ">="
63
87
  - !ruby/object:Gem::Version
88
+ segments:
89
+ - 0
64
90
  version: "0"
65
- version:
66
91
  required_rubygems_version: !ruby/object:Gem::Requirement
67
92
  requirements:
68
93
  - - ">="
69
94
  - !ruby/object:Gem::Version
95
+ segments:
96
+ - 0
70
97
  version: "0"
71
- version:
72
98
  requirements: []
73
99
 
74
100
  rubyforge_project:
75
- rubygems_version: 1.3.5
101
+ rubygems_version: 1.3.6
76
102
  signing_key:
77
103
  specification_version: 3
78
- summary: A simple Ruby API for DropBox
79
- test_files: []
80
-
104
+ summary: A Simple DropBox API in Ruby
105
+ test_files:
106
+ - examples/dropbox_spec.rb
data/README DELETED
@@ -1,3 +0,0 @@
1
- A simple Ruby API for DropBox
2
-
3
- Install with: gem install dropbox (using gemcutter as source)