dropbox 0.0.10 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +23 -3
- data/LICENSE +20 -0
- data/README.rdoc +84 -17
- data/Rakefile +39 -20
- data/VERSION +1 -1
- data/lib/dropbox.rb +41 -12
- data/lib/dropbox/api.rb +530 -0
- data/lib/dropbox/entry.rb +96 -0
- data/lib/dropbox/event.rb +109 -0
- data/lib/dropbox/memoization.rb +98 -0
- data/lib/dropbox/revision.rb +197 -0
- data/lib/dropbox/session.rb +160 -0
- data/lib/extensions/array.rb +9 -0
- data/lib/extensions/hash.rb +61 -0
- data/lib/extensions/module.rb +22 -0
- data/lib/extensions/object.rb +5 -0
- data/lib/extensions/string.rb +9 -0
- data/lib/extensions/to_bool.rb +17 -0
- data/spec/dropbox/api_spec.rb +778 -0
- data/spec/dropbox/entry_spec.rb +144 -0
- data/spec/dropbox/event_spec.rb +122 -0
- data/spec/dropbox/revision_spec.rb +367 -0
- data/spec/dropbox/session_spec.rb +148 -0
- data/spec/dropbox_spec.rb +57 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +10 -0
- metadata +64 -27
- data/ChangeLog.rdoc +0 -17
- data/examples/dropbox_spec.rb +0 -99
- data/lib/dropbox/dropbox.rb +0 -213
data/lib/dropbox/dropbox.rb
DELETED
@@ -1,213 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'nokogiri'
|
3
|
-
require 'mechanize'
|
4
|
-
|
5
|
-
Mechanize.html_parser = Nokogiri::HTML
|
6
|
-
|
7
|
-
# TODO: Tests (mock out expected DropBox output)
|
8
|
-
# TODO: Directory object, File object
|
9
|
-
|
10
|
-
class DropBox
|
11
|
-
#before_filter :login_filter, :except => [:login]
|
12
|
-
|
13
|
-
def initialize(email, password, folder_namespace = "")
|
14
|
-
@email = email
|
15
|
-
@password = password
|
16
|
-
@agent = Mechanize.new
|
17
|
-
@folder_namespace = folder_namespace.gsub(/^\//,"")
|
18
|
-
@logged_in = false
|
19
|
-
end
|
20
|
-
|
21
|
-
def agent
|
22
|
-
@agent
|
23
|
-
end
|
24
|
-
|
25
|
-
# Lists all the files and folders in a given directory
|
26
|
-
def index(path = "/")
|
27
|
-
login_filter
|
28
|
-
path = namespace_path(path)
|
29
|
-
|
30
|
-
list = @agent.post("/browse2#{path}?ajax=yes", {"d"=> 1, "t" => @token })
|
31
|
-
|
32
|
-
listing = list.search('div.browse-file-box-details').collect do |file|
|
33
|
-
details = {}
|
34
|
-
details['name'] = file.at('div.details-filename a').content.strip
|
35
|
-
details['url'] = file.at('div.details-filename a')["href"]
|
36
|
-
#details['size'] = file.at('div.details-size a').try(:content).try(:strip)
|
37
|
-
#details['modified'] = file.at('div.details-modified a').try(:content).try(:strip)
|
38
|
-
|
39
|
-
if match_data = details['url'].match(/^\/browse_plain(.*)$/)
|
40
|
-
details['directory'] = true
|
41
|
-
details['path'] = normalize_namespace(match_data[1])
|
42
|
-
elsif match_data = details['url'].match(%r{^https?://[^/]*/get(.*)$})
|
43
|
-
details['directory'] = false
|
44
|
-
details['path'] = normalize_namespace(match_data[1])
|
45
|
-
elsif match_data = details['url'].match(%r{^https?://[^/]*/u/\d*/(.*)$})
|
46
|
-
details['directory'] = false
|
47
|
-
details['path'] = "Public/#{match_data[1]}"
|
48
|
-
else
|
49
|
-
raise "could not parse path from Dropbox URL: #{details['url'] }"
|
50
|
-
end
|
51
|
-
|
52
|
-
details
|
53
|
-
end
|
54
|
-
|
55
|
-
return listing
|
56
|
-
end
|
57
|
-
|
58
|
-
alias :list :index
|
59
|
-
|
60
|
-
# Lists the full history for a file on DropBox
|
61
|
-
def list_history(path)
|
62
|
-
login_filter
|
63
|
-
|
64
|
-
path = namespace_path(path)
|
65
|
-
|
66
|
-
history = @agent.get("/revisions#{path}")
|
67
|
-
listing = history.search("table.filebrowser > tr").select{|r| r.search("td").count > 1 }.collect do |r|
|
68
|
-
|
69
|
-
# warning, this is very brittle!
|
70
|
-
details = {}
|
71
|
-
details["version"] = r.search("td a").first.content.strip
|
72
|
-
details["url"] = r.search("td a").first["href"]
|
73
|
-
details["size"] = r.search("td").last.content.strip
|
74
|
-
details["modified"] = r.search("td")[2].content.strip
|
75
|
-
details["version_id"] = details["url"].match(/^.*sjid=([\d]*)$/)[1]
|
76
|
-
details['path'] = normalize_namespace(details['url'][33..-1])
|
77
|
-
|
78
|
-
details
|
79
|
-
end
|
80
|
-
|
81
|
-
return listing
|
82
|
-
end
|
83
|
-
|
84
|
-
# Downloads the specified file from DropBox
|
85
|
-
def show(path)
|
86
|
-
require 'pathname'
|
87
|
-
# change to before filter
|
88
|
-
login_filter
|
89
|
-
|
90
|
-
# round about way of getting the secure url we need
|
91
|
-
# path = namespace_path(path)
|
92
|
-
pathname = Pathname.new(path)
|
93
|
-
url = self.list(pathname.dirname.to_s).detect{ |f| f["name"] == pathname.basename.to_s }["url"]
|
94
|
-
|
95
|
-
#https://dl-web.dropbox.com/get/testing.txt?w=0ff80d5d&sjid=125987568
|
96
|
-
@agent.get(url).content
|
97
|
-
end
|
98
|
-
|
99
|
-
alias :get :show
|
100
|
-
|
101
|
-
# Creates a directory
|
102
|
-
def create_directory(new_path, destination = "/" )
|
103
|
-
# change to before filter
|
104
|
-
login unless @logged_in
|
105
|
-
destination = namespace_path(destination)
|
106
|
-
@agent.post("/cmd/new#{destination}",{"to_path"=>new_path, "folder"=>"yes", "t" => @token }).code == "200"
|
107
|
-
end
|
108
|
-
|
109
|
-
# Uploads a file to DropBox under the given filename
|
110
|
-
def create(file, destination = "/")
|
111
|
-
# change to before filter
|
112
|
-
if @logged_in
|
113
|
-
home_page = @agent.get('https://www.dropbox.com/home')
|
114
|
-
else
|
115
|
-
home_page = login
|
116
|
-
end
|
117
|
-
|
118
|
-
upload_form = home_page.forms.detect{ |f| f.action == "https://dl-web.dropbox.com/upload" }
|
119
|
-
upload_form.dest = namespace_path(destination)
|
120
|
-
upload_form.file_uploads.first.file_name = file if file
|
121
|
-
|
122
|
-
@agent.submit(upload_form).code == "200"
|
123
|
-
end
|
124
|
-
|
125
|
-
alias :update :create
|
126
|
-
|
127
|
-
# Renames a file or folder in the DropBox
|
128
|
-
def rename(file, destination)
|
129
|
-
login_filter
|
130
|
-
file = namespace_path(file)
|
131
|
-
destination = namespace_path(destination)
|
132
|
-
@agent.post("/cmd/rename#{file}", {"to_path"=> destination, "t" => @token }).code == "200"
|
133
|
-
end
|
134
|
-
|
135
|
-
# Deletes a file/folder from the DropBox (accepts string path or an array of string paths)
|
136
|
-
def destroy(paths)
|
137
|
-
login_filter
|
138
|
-
paths = [paths].flatten
|
139
|
-
paths = paths.collect { |path| namespace_path(path) }
|
140
|
-
@agent.post("/cmd/delete", {"files"=> paths, "t" => @token }).code == "200"
|
141
|
-
end
|
142
|
-
|
143
|
-
# Permanently deletes a file from the DropBox (no history!) accepts arrays, as #destroy does
|
144
|
-
def purge(paths)
|
145
|
-
login_filter
|
146
|
-
paths = [paths].flatten
|
147
|
-
paths = paths.collect { |path| namespace_path(path) }
|
148
|
-
@agent.post("/cmd/purge", {"files"=> paths, "t" => @token }).code == "200"
|
149
|
-
end
|
150
|
-
|
151
|
-
# 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)
|
152
|
-
def usage_stats
|
153
|
-
login_filter
|
154
|
-
|
155
|
-
stats_page = @agent.get("/account")
|
156
|
-
|
157
|
-
stats = stats_page.at('#usage-percent').content.scan(/(\d+(?:\.\d+)?)%\ used\ \((\d+(?:\.\d+)?)([MG])B of (\d+(?:\.\d+)?)GB\)/).collect{ |d|
|
158
|
-
{ :used => d[1].to_f * ((d[2] == "G") ? 1024 : 1),
|
159
|
-
:total => d[3].to_f * 1024,
|
160
|
-
:free => (d[3].to_f * 1024 - d[1].to_f * ((d[2] == "G") ? 1024 : 1)),
|
161
|
-
:percent => Percentage.new(d[0].to_f/100)
|
162
|
-
}
|
163
|
-
}[0]
|
164
|
-
|
165
|
-
regular_data = stats_page.at('span.bar-graph-legend.bar-graph-normal').next.content.scan(/\((\d+(?:\.\d+)?)([MG])B/)[0]
|
166
|
-
stats[:regular_used] = regular_data[0].to_f * ((regular_data[1] == "G") ? 1024 : 1) unless regular_data.nil?
|
167
|
-
|
168
|
-
shared_data = stats_page.at('span.bar-graph-legend.bar-graph-shared').next.content.scan(/\((\d+(?:\.\d+)?)([MG])B/)[0]
|
169
|
-
stats[:shared_used] = shared_data[0].to_f * ((shared_data[1] == "G") ? 1024 : 1) unless shared_data.nil?
|
170
|
-
|
171
|
-
return stats
|
172
|
-
end
|
173
|
-
|
174
|
-
private
|
175
|
-
def namespace_path(path)
|
176
|
-
# remove the start slash if we have one
|
177
|
-
path.gsub(/^\//,"")
|
178
|
-
new_path = if @folder_namespace.empty?
|
179
|
-
"/#{path}"
|
180
|
-
else
|
181
|
-
"/#{@folder_namespace}/#{path}"
|
182
|
-
end
|
183
|
-
new_path.gsub("//","/")
|
184
|
-
end
|
185
|
-
|
186
|
-
def normalize_namespace(file)
|
187
|
-
file.gsub(/^\/#{@folder_namespace}/,"")
|
188
|
-
end
|
189
|
-
|
190
|
-
def login
|
191
|
-
page = @agent.get('https://www.dropbox.com/login')
|
192
|
-
login_form = page.forms.detect { |f| f.action == "/login" }
|
193
|
-
login_form.login_email = @email
|
194
|
-
login_form.login_password = @password
|
195
|
-
|
196
|
-
home_page = @agent.submit(login_form)
|
197
|
-
# todo check if we are logged in! (ie search for email and "Log out"
|
198
|
-
@logged_in = true
|
199
|
-
@token = home_page.at('//script[contains(text(), "TOKEN")]').content.match("TOKEN: '(.*)',$")[1]
|
200
|
-
|
201
|
-
# check if we have our namespace
|
202
|
-
|
203
|
-
home_page
|
204
|
-
end
|
205
|
-
|
206
|
-
def login_filter
|
207
|
-
login unless @logged_in
|
208
|
-
end
|
209
|
-
end
|
210
|
-
|
211
|
-
# this file doesn't use the standard ruby indent settings, so the following
|
212
|
-
# modeline will make sure that the whitespace stays consistent.
|
213
|
-
# vim:noexpandtab tabstop=4 shiftwidth=4
|