dbox 0.5.3 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +83 -0
- data/README.md +31 -7
- data/TODO.txt +1 -1
- data/VERSION +1 -1
- data/bin/dbox +2 -1
- data/dbox.gemspec +13 -11
- data/lib/dbox.rb +11 -2
- data/lib/dbox/api.rb +130 -66
- data/lib/dbox/cacert.pem +3376 -0
- data/lib/dbox/database.rb +106 -8
- data/lib/dbox/syncer.rb +103 -86
- data/lib/dbox/utils.rb +82 -0
- data/spec/dbox_spec.rb +201 -0
- data/spec/spec_helper.rb +2 -2
- data/vendor/dropbox-ruby-sdk/CHANGELOG +29 -0
- data/vendor/{dropbox-client-ruby → dropbox-ruby-sdk}/LICENSE +1 -1
- data/vendor/dropbox-ruby-sdk/README +7 -0
- data/vendor/dropbox-ruby-sdk/cli_example.rb +197 -0
- data/vendor/dropbox-ruby-sdk/dropbox_controller.rb +57 -0
- data/vendor/dropbox-ruby-sdk/gemspec.rb +25 -0
- data/vendor/dropbox-ruby-sdk/lib/dropbox_sdk.rb +690 -0
- data/vendor/dropbox-ruby-sdk/web_file_browser.rb +184 -0
- metadata +16 -14
- data/vendor/dropbox-client-ruby/README +0 -17
- data/vendor/dropbox-client-ruby/Rakefile +0 -41
- data/vendor/dropbox-client-ruby/config/testing.json.example +0 -16
- data/vendor/dropbox-client-ruby/lib/dropbox.rb +0 -259
- data/vendor/dropbox-client-ruby/manifest +0 -9
- data/vendor/dropbox-client-ruby/test/authenticator_test.rb +0 -53
- data/vendor/dropbox-client-ruby/test/client_test.rb +0 -100
- data/vendor/dropbox-client-ruby/test/util.rb +0 -21
data/lib/dbox/utils.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
module Dbox
|
2
|
+
module Utils
|
3
|
+
def times_equal?(t1, t2)
|
4
|
+
time_to_s(t1) == time_to_s(t2)
|
5
|
+
end
|
6
|
+
|
7
|
+
def time_to_s(t)
|
8
|
+
case t
|
9
|
+
when Time
|
10
|
+
# matches dropbox time format
|
11
|
+
t.utc.strftime("%a, %d %b %Y %H:%M:%S +0000")
|
12
|
+
when String
|
13
|
+
t
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def parse_time(t)
|
18
|
+
case t
|
19
|
+
when Time
|
20
|
+
t
|
21
|
+
when String
|
22
|
+
Time.parse(t)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# assumes local_path is defined
|
27
|
+
def local_to_relative_path(path)
|
28
|
+
if path.include?(local_path)
|
29
|
+
path.sub(local_path, "").sub(/^\//, "")
|
30
|
+
else
|
31
|
+
raise BadPath, "Not a local path: #{path}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# assumes remote_path is defined
|
36
|
+
def remote_to_relative_path(path)
|
37
|
+
if path.include?(remote_path)
|
38
|
+
path.sub(remote_path, "").sub(/^\//, "")
|
39
|
+
else
|
40
|
+
raise BadPath, "Not a remote path: #{path}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# assumes local_path is defined
|
45
|
+
def relative_to_local_path(path)
|
46
|
+
if path && path.length > 0
|
47
|
+
File.join(local_path, path)
|
48
|
+
else
|
49
|
+
local_path
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# assumes remote_path is defined
|
54
|
+
def relative_to_remote_path(path)
|
55
|
+
if path && path.length > 0
|
56
|
+
File.join(remote_path, path)
|
57
|
+
else
|
58
|
+
remote_path
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def calculate_hash(filepath)
|
63
|
+
begin
|
64
|
+
Digest::MD5.file(filepath).to_s
|
65
|
+
rescue Errno::EISDIR
|
66
|
+
nil
|
67
|
+
rescue Errno::ENOENT
|
68
|
+
nil
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def find_nonconflicting_path(filepath)
|
73
|
+
proposed = filepath
|
74
|
+
while File.exists?(proposed)
|
75
|
+
dir, p = File.split(proposed)
|
76
|
+
p = p.sub(/^(.*?)( \((\d+)\))?(\..*?)?$/) { "#{$1} (#{$3 ? $3.to_i + 1 : 1})#{$4}" }
|
77
|
+
proposed = File.join(dir, p)
|
78
|
+
end
|
79
|
+
proposed
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/spec/dbox_spec.rb
CHANGED
@@ -122,6 +122,101 @@ describe Dbox do
|
|
122
122
|
Dbox.pull(@local).should eql(:created => ["subdir", "subdir/one.txt"], :deleted => ["foo.txt"], :updated => ["", "baz.txt"], :failed => [])
|
123
123
|
Dbox.pull(@local).should eql(:created => [], :deleted => [], :updated => [], :failed => [])
|
124
124
|
end
|
125
|
+
|
126
|
+
it "should be able to download a bunch of files at the same time" do
|
127
|
+
Dbox.create(@remote, @local)
|
128
|
+
@alternate = "#{ALTERNATE_LOCAL_TEST_PATH}/#{@name}"
|
129
|
+
Dbox.clone(@remote, @alternate)
|
130
|
+
|
131
|
+
# generate 20 x 100kB files
|
132
|
+
20.times do
|
133
|
+
make_file "#{@alternate}/#{randname}.txt", 100
|
134
|
+
end
|
135
|
+
|
136
|
+
Dbox.push(@alternate)
|
137
|
+
|
138
|
+
res = Dbox.pull(@local)
|
139
|
+
res[:deleted].should eql([])
|
140
|
+
res[:updated].should eql([""])
|
141
|
+
res[:failed].should eql([])
|
142
|
+
res[:created].size.should eql(20)
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should be able to pull a series of updates to the same file" do
|
146
|
+
Dbox.create(@remote, @local)
|
147
|
+
@alternate = "#{ALTERNATE_LOCAL_TEST_PATH}/#{@name}"
|
148
|
+
Dbox.clone(@remote, @alternate)
|
149
|
+
|
150
|
+
make_file "#{@local}/hello.txt"
|
151
|
+
Dbox.push(@local)
|
152
|
+
Dbox.pull(@alternate).should eql(:created => ["hello.txt"], :deleted => [], :updated => [""], :failed => [])
|
153
|
+
make_file "#{@local}/hello.txt"
|
154
|
+
Dbox.push(@local)
|
155
|
+
Dbox.pull(@alternate).should eql(:created => [], :deleted => [], :updated => ["", "hello.txt"], :failed => [])
|
156
|
+
make_file "#{@local}/hello.txt"
|
157
|
+
Dbox.push(@local)
|
158
|
+
Dbox.pull(@alternate).should eql(:created => [], :deleted => [], :updated => ["", "hello.txt"], :failed => [])
|
159
|
+
end
|
160
|
+
|
161
|
+
it "should handle conflicting pulls of new files gracefully" do
|
162
|
+
Dbox.create(@remote, @local)
|
163
|
+
@alternate = "#{ALTERNATE_LOCAL_TEST_PATH}/#{@name}"
|
164
|
+
Dbox.clone(@remote, @alternate)
|
165
|
+
|
166
|
+
make_file "#{@local}/hello.txt"
|
167
|
+
Dbox.push(@local)
|
168
|
+
|
169
|
+
make_file "#{@alternate}/hello.txt"
|
170
|
+
Dbox.pull(@alternate).should eql(:created => ["hello.txt"], :deleted => [], :updated => [""], :conflicts => [{:original => "hello.txt", :renamed => "hello (1).txt"}], :failed => [])
|
171
|
+
end
|
172
|
+
|
173
|
+
it "should handle conflicting pulls of updated files gracefully" do
|
174
|
+
Dbox.create(@remote, @local)
|
175
|
+
@alternate = "#{ALTERNATE_LOCAL_TEST_PATH}/#{@name}"
|
176
|
+
Dbox.clone(@remote, @alternate)
|
177
|
+
|
178
|
+
make_file "#{@local}/hello.txt"
|
179
|
+
Dbox.push(@local)
|
180
|
+
Dbox.pull(@alternate).should eql(:created => ["hello.txt"], :deleted => [], :updated => [""], :failed => [])
|
181
|
+
|
182
|
+
make_file "#{@local}/hello.txt"
|
183
|
+
Dbox.push(@local)
|
184
|
+
|
185
|
+
make_file "#{@alternate}/hello.txt"
|
186
|
+
Dbox.pull(@alternate).should eql(:created => [], :deleted => [], :updated => ["", "hello.txt"], :conflicts => [{:original => "hello.txt", :renamed => "hello (1).txt"}], :failed => [])
|
187
|
+
end
|
188
|
+
|
189
|
+
it "should deal with all sorts of weird filenames when renaming due to conflicts on pull" do
|
190
|
+
Dbox.create(@remote, @local)
|
191
|
+
@alternate = "#{ALTERNATE_LOCAL_TEST_PATH}/#{@name}"
|
192
|
+
Dbox.clone(@remote, @alternate)
|
193
|
+
|
194
|
+
make_file "#{@local}/hello.txt"
|
195
|
+
make_file "#{@local}/hello (1).txt"
|
196
|
+
make_file "#{@local}/goodbye.txt"
|
197
|
+
Dbox.push(@local)
|
198
|
+
|
199
|
+
make_file "#{@alternate}/hello.txt"
|
200
|
+
make_file "#{@alternate}/hello (1).txt"
|
201
|
+
make_file "#{@alternate}/hello (3).txt"
|
202
|
+
make_file "#{@alternate}/hello (4).txt"
|
203
|
+
make_file "#{@alternate}/hello (test).txt"
|
204
|
+
make_file "#{@alternate}/goodbye.txt"
|
205
|
+
make_file "#{@alternate}/goodbye (1).txt"
|
206
|
+
make_file "#{@alternate}/goodbye (2).txt"
|
207
|
+
make_file "#{@alternate}/goodbye (3).txt"
|
208
|
+
make_file "#{@alternate}/goodbye ().txt"
|
209
|
+
|
210
|
+
# there's a race condition, so the output could be one of two things
|
211
|
+
res = Dbox.pull(@alternate)
|
212
|
+
res[:created].should eql(["goodbye.txt", "hello (1).txt", "hello.txt"])
|
213
|
+
res[:updated].should eql([""])
|
214
|
+
res[:deleted].should eql([])
|
215
|
+
res[:failed].should eql([])
|
216
|
+
c = (res[:conflicts] == [{:original => "goodbye.txt", :renamed => "goodbye (4).txt"}, {:original => "hello (1).txt", :renamed => "hello (5).txt"}, {:original => "hello.txt", :renamed => "hello (2).txt"}]) ||
|
217
|
+
(res[:conflicts] == [{:original => "goodbye.txt", :renamed => "goodbye (4).txt"}, {:original => "hello (1).txt", :renamed => "hello (2).txt"}, {:original => "hello.txt", :renamed => "hello (5).txt"}])
|
218
|
+
c.should be_true
|
219
|
+
end
|
125
220
|
end
|
126
221
|
|
127
222
|
describe "#push" do
|
@@ -230,6 +325,112 @@ describe Dbox do
|
|
230
325
|
rm_rf @local
|
231
326
|
Dbox.clone(@remote, @local).should eql(:created => [crazy_name1, File.join(crazy_name1, "foo.txt")], :deleted => [], :updated => [""], :failed => [])
|
232
327
|
end
|
328
|
+
|
329
|
+
it "should be able to upload a bunch of files at the same time" do
|
330
|
+
Dbox.create(@remote, @local)
|
331
|
+
|
332
|
+
# generate 20 x 100kB files
|
333
|
+
20.times do
|
334
|
+
make_file "#{@local}/#{randname}.txt", 100
|
335
|
+
end
|
336
|
+
|
337
|
+
res = Dbox.push(@local)
|
338
|
+
res[:deleted].should eql([])
|
339
|
+
res[:updated].should eql([])
|
340
|
+
res[:failed].should eql([])
|
341
|
+
res[:created].size.should eql(20)
|
342
|
+
end
|
343
|
+
|
344
|
+
it "should be able to push a series of updates to the same file" do
|
345
|
+
Dbox.create(@remote, @local)
|
346
|
+
|
347
|
+
make_file "#{@local}/hello.txt"
|
348
|
+
Dbox.push(@local).should eql(:created => ["hello.txt"], :deleted => [], :updated => [], :failed => [])
|
349
|
+
make_file "#{@local}/hello.txt"
|
350
|
+
Dbox.push(@local).should eql(:created => [], :deleted => [], :updated => ["hello.txt"], :failed => [])
|
351
|
+
make_file "#{@local}/hello.txt"
|
352
|
+
Dbox.push(@local).should eql(:created => [], :deleted => [], :updated => ["hello.txt"], :failed => [])
|
353
|
+
end
|
354
|
+
|
355
|
+
it "should handle conflicting pushes of new files gracefully" do
|
356
|
+
Dbox.create(@remote, @local)
|
357
|
+
|
358
|
+
@alternate = "#{ALTERNATE_LOCAL_TEST_PATH}/#{@name}"
|
359
|
+
Dbox.clone(@remote, @alternate)
|
360
|
+
|
361
|
+
make_file "#{@local}/hello.txt"
|
362
|
+
Dbox.push(@local).should eql(:created => ["hello.txt"], :deleted => [], :updated => [], :failed => [])
|
363
|
+
|
364
|
+
make_file "#{@alternate}/hello.txt"
|
365
|
+
Dbox.push(@alternate).should eql(:created => [], :deleted => [], :updated => [], :conflicts => [{:original => "hello.txt", :renamed => "hello (1).txt"}], :failed => [])
|
366
|
+
end
|
367
|
+
|
368
|
+
it "should handle conflicting pushes of updated files gracefully" do
|
369
|
+
Dbox.create(@remote, @local)
|
370
|
+
make_file "#{@local}/hello.txt"
|
371
|
+
Dbox.push(@local)
|
372
|
+
|
373
|
+
@alternate = "#{ALTERNATE_LOCAL_TEST_PATH}/#{@name}"
|
374
|
+
Dbox.clone(@remote, @alternate)
|
375
|
+
|
376
|
+
make_file "#{@local}/hello.txt"
|
377
|
+
Dbox.push(@local).should eql(:created => [], :deleted => [], :updated => ["hello.txt"], :failed => [])
|
378
|
+
|
379
|
+
make_file "#{@alternate}/hello.txt"
|
380
|
+
Dbox.push(@alternate).should eql(:created => [], :deleted => [], :updated => [], :conflicts => [{:original => "hello.txt", :renamed => "hello (1).txt"}], :failed => [])
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
describe "#sync" do
|
385
|
+
it "should fail if the local dir is missing" do
|
386
|
+
expect { Dbox.sync(@local) }.to raise_error(Dbox::DatabaseError)
|
387
|
+
end
|
388
|
+
|
389
|
+
it "should be able to sync basic changes" do
|
390
|
+
Dbox.create(@remote, @local)
|
391
|
+
@alternate = "#{ALTERNATE_LOCAL_TEST_PATH}/#{@name}"
|
392
|
+
Dbox.clone(@remote, @alternate)
|
393
|
+
|
394
|
+
Dbox.sync(@local).should eql(:pull => { :created => [], :deleted => [], :updated => [], :failed => [] },
|
395
|
+
:push => { :created => [], :deleted => [], :updated => [], :failed => [] })
|
396
|
+
Dbox.sync(@alternate).should eql(:pull => { :created => [], :deleted => [], :updated => [], :failed => [] },
|
397
|
+
:push => { :created => [], :deleted => [], :updated => [], :failed => [] })
|
398
|
+
|
399
|
+
make_file "#{@local}/hello.txt"
|
400
|
+
Dbox.sync(@local).should eql(:pull => { :created => [], :deleted => [], :updated => [], :failed => [] },
|
401
|
+
:push => { :created => ["hello.txt"], :deleted => [], :updated => [], :failed => [] })
|
402
|
+
Dbox.sync(@alternate).should eql(:pull => { :created => ["hello.txt"], :deleted => [], :updated => [""], :failed => [] },
|
403
|
+
:push => { :created => [], :deleted => [], :updated => [], :failed => [] })
|
404
|
+
end
|
405
|
+
|
406
|
+
it "should be able to sync complex changes" do
|
407
|
+
Dbox.create(@remote, @local)
|
408
|
+
@alternate = "#{ALTERNATE_LOCAL_TEST_PATH}/#{@name}"
|
409
|
+
Dbox.clone(@remote, @alternate)
|
410
|
+
|
411
|
+
Dbox.sync(@local).should eql(:pull => { :created => [], :deleted => [], :updated => [], :failed => [] },
|
412
|
+
:push => { :created => [], :deleted => [], :updated => [], :failed => [] })
|
413
|
+
Dbox.sync(@alternate).should eql(:pull => { :created => [], :deleted => [], :updated => [], :failed => [] },
|
414
|
+
:push => { :created => [], :deleted => [], :updated => [], :failed => [] })
|
415
|
+
|
416
|
+
make_file "#{@local}/hello.txt"
|
417
|
+
make_file "#{@local}/goodbye.txt"
|
418
|
+
make_file "#{@local}/so_long.txt"
|
419
|
+
make_file "#{@alternate}/hello.txt"
|
420
|
+
make_file "#{@alternate}/farewell.txt"
|
421
|
+
Dbox.sync(@local).should eql(:pull => { :created => [], :deleted => [], :updated => [], :failed => [] },
|
422
|
+
:push => { :created => ["goodbye.txt", "hello.txt", "so_long.txt"], :deleted => [], :updated => [], :failed => [] })
|
423
|
+
Dbox.sync(@alternate).should eql(:pull => { :created => ["goodbye.txt", "hello.txt", "so_long.txt"], :deleted => [], :updated => [""], :failed => [], :conflicts => [{ :renamed => "hello (1).txt", :original => "hello.txt" }] },
|
424
|
+
:push => { :created => ["farewell.txt", "hello (1).txt"], :deleted => [], :updated => [], :failed => [] })
|
425
|
+
|
426
|
+
make_file "#{@alternate}/farewell.txt"
|
427
|
+
make_file "#{@alternate}/goodbye.txt"
|
428
|
+
make_file "#{@alternate}/au_revoir.txt"
|
429
|
+
Dbox.sync(@alternate).should eql(:pull => { :created => [], :deleted => [], :updated => [""], :failed => [] },
|
430
|
+
:push => { :created => ["au_revoir.txt"], :deleted => [], :updated => ["farewell.txt", "goodbye.txt"], :failed => [] })
|
431
|
+
Dbox.sync(@local).should eql(:pull => { :created => ["au_revoir.txt", "farewell.txt", "hello (1).txt"], :deleted => [], :updated => ["", "goodbye.txt"], :failed => [] },
|
432
|
+
:push => { :created => [], :deleted => [], :updated => [], :failed => [] })
|
433
|
+
end
|
233
434
|
end
|
234
435
|
|
235
436
|
describe "#move" do
|
data/spec/spec_helper.rb
CHANGED
@@ -36,8 +36,8 @@ def log
|
|
36
36
|
LOGGER
|
37
37
|
end
|
38
38
|
|
39
|
-
def make_file(filepath)
|
40
|
-
|
39
|
+
def make_file(filepath, size_in_kb=1)
|
40
|
+
`dd if=/dev/urandom of="#{filepath.gsub('"','\"')}" bs=1024 count=#{size_in_kb} 1>/dev/null 2>/dev/null`
|
41
41
|
end
|
42
42
|
|
43
43
|
RSpec::Matchers.define :exist do
|
@@ -0,0 +1,29 @@
|
|
1
|
+
1.1 (2011-10-17)
|
2
|
+
* Various bug fixes found during beta period
|
3
|
+
* Improved documentation
|
4
|
+
* Improved module structure
|
5
|
+
* Removed dependency on the oauth module, using plaintext authentication over https
|
6
|
+
|
7
|
+
1.0 (2011-8-16)
|
8
|
+
* Backwards compatibility broken
|
9
|
+
- Changed interface
|
10
|
+
- Change 'sandbox' references to 'app_folder'
|
11
|
+
* Updated SDK to Dropbox API Version 1, supporting all calls
|
12
|
+
- Added 'rev' parameter to metadata and get_file
|
13
|
+
- Added 'parent_rev' parameter to put_file
|
14
|
+
- Added search, share, media, revisions, and restore
|
15
|
+
- put_file uses /files_put instead of multipart POST
|
16
|
+
- Removed methods for calls that were removed from v1 of the REST API
|
17
|
+
* Changed return format for calls
|
18
|
+
- On error (non-200 response), an exception is raised
|
19
|
+
- On success, the JSON is parsed and a Hash is returned
|
20
|
+
* Updated examples
|
21
|
+
- Improved CLI example
|
22
|
+
- Added a Ruby on Rails 3 controller example
|
23
|
+
- Added a web based file browser/uploader that uses Sinatra
|
24
|
+
* put_file no longer takes a "name" arugment, only takes a full path
|
25
|
+
* Removed reliance on config files
|
26
|
+
* Assorted bugfixes and improvements
|
27
|
+
* All calls are now made over SSL
|
28
|
+
* Fully documented code for RDoc generation
|
29
|
+
* Added a CHANGELOG
|
@@ -0,0 +1,7 @@
|
|
1
|
+
Getting started with the Dropbox Ruby SDK:
|
2
|
+
1. Install json and oauth ruby gems with:
|
3
|
+
gem install json oauth
|
4
|
+
2. Edit cli_example.rb to to include your APP_KEY and APP_SECRET
|
5
|
+
3. Try running the example app: 'ruby clie_example.rb'.
|
6
|
+
4. See dropbox_controller.rb for an example Ruby on Rails controller. It needs an APP_KEY and APP_SECRET set too.
|
7
|
+
5. Check out the dropbox website for reference and help! http://dropbox.com/developers_beta
|
@@ -0,0 +1,197 @@
|
|
1
|
+
require './lib/dropbox_sdk'
|
2
|
+
require 'pp'
|
3
|
+
|
4
|
+
####
|
5
|
+
# An example app using the Dropbox API Ruby Client
|
6
|
+
# This ruby script sets up a basic command line interface (CLI)
|
7
|
+
# that prompts a user to authenticate on the web, then
|
8
|
+
# allows them to type commands to manipulate their dropbox.
|
9
|
+
####
|
10
|
+
|
11
|
+
# You must use your Dropbox App key and secret to use the API.
|
12
|
+
# Find this at https://www.dropbox.com/developers
|
13
|
+
APP_KEY = ''
|
14
|
+
APP_SECRET = ''
|
15
|
+
ACCESS_TYPE = :app_folder #The two valid values here are :app_folder and :dropbox
|
16
|
+
#The default is :app_folder, but your application might be
|
17
|
+
#set to have full :dropbox access. Check your app at
|
18
|
+
#https://www.dropbox.com/developers/apps
|
19
|
+
|
20
|
+
class DropboxCLI
|
21
|
+
LOGIN_REQUIRED = %w{put get cp mv rm ls mkdir info logout search}
|
22
|
+
|
23
|
+
def initialize
|
24
|
+
if APP_KEY == '' or APP_SECRET == ''
|
25
|
+
puts "You must set your APP_KEY and APP_SECRET in cli_example.rb!"
|
26
|
+
puts "Find this in your apps page at https://www.dropbox.com/developers/"
|
27
|
+
exit
|
28
|
+
end
|
29
|
+
|
30
|
+
@session = DropboxSession.new(APP_KEY, APP_SECRET)
|
31
|
+
@client = nil
|
32
|
+
end
|
33
|
+
|
34
|
+
def login
|
35
|
+
########
|
36
|
+
# Instead of going to a authorize URL, you can set a access token key and secret
|
37
|
+
# from a previous session
|
38
|
+
########
|
39
|
+
# @session.set_access_token('key', 'secret')
|
40
|
+
|
41
|
+
if @session.authorized?
|
42
|
+
puts "already logged in!"
|
43
|
+
else
|
44
|
+
|
45
|
+
# grab the request token for session
|
46
|
+
@session.get_request_token
|
47
|
+
|
48
|
+
authorize_url = @session.get_authorize_url
|
49
|
+
puts "Got a request token. Your request token key is #{@session.request_token.key} and your token secret is #{@session.request_token.secret}"
|
50
|
+
|
51
|
+
# make the user log in and authorize this token
|
52
|
+
puts "AUTHORIZING", authorize_url, "Please visit that web page and hit 'Allow', then hit Enter here."
|
53
|
+
gets
|
54
|
+
|
55
|
+
# get the access token from the server. Its then stored in the session.
|
56
|
+
@session.get_access_token
|
57
|
+
|
58
|
+
end
|
59
|
+
puts "You are logged in. Your access token key is #{@session.access_token.key} your secret is #{@session.access_token.secret}"
|
60
|
+
@client = DropboxClient.new(@session, ACCESS_TYPE)
|
61
|
+
end
|
62
|
+
|
63
|
+
def command_loop
|
64
|
+
puts "Enter a command or 'help' or 'exit'"
|
65
|
+
command_line = ''
|
66
|
+
while command_line.strip != 'exit'
|
67
|
+
begin
|
68
|
+
execute_dropbox_command(command_line)
|
69
|
+
rescue RuntimeError => e
|
70
|
+
puts "Command Line Error! #{e.class}: #{e}"
|
71
|
+
puts e.backtrace
|
72
|
+
end
|
73
|
+
print '> '
|
74
|
+
command_line = gets.strip
|
75
|
+
end
|
76
|
+
puts 'goodbye'
|
77
|
+
exit(0)
|
78
|
+
end
|
79
|
+
|
80
|
+
def execute_dropbox_command(cmd_line)
|
81
|
+
command = cmd_line.split
|
82
|
+
method = command.first
|
83
|
+
if LOGIN_REQUIRED.include? method
|
84
|
+
if @client
|
85
|
+
send(method.to_sym, command)
|
86
|
+
else
|
87
|
+
puts 'must be logged in; type \'login\' to get started.'
|
88
|
+
end
|
89
|
+
elsif ['login', 'help'].include? method
|
90
|
+
send(method.to_sym)
|
91
|
+
else
|
92
|
+
if command.first && !command.first.strip.empty?
|
93
|
+
puts 'invalid command. type \'help\' to see commands.'
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def logout(command)
|
99
|
+
@session.clear_access_token
|
100
|
+
puts "You are logged out."
|
101
|
+
@client = nil
|
102
|
+
end
|
103
|
+
|
104
|
+
def put(command)
|
105
|
+
fname = command[1]
|
106
|
+
|
107
|
+
#If the user didn't specifiy the file name, just use the name of the file on disk
|
108
|
+
if command[2]
|
109
|
+
new_name = command[2]
|
110
|
+
else
|
111
|
+
new_name = File.basename(fname)
|
112
|
+
end
|
113
|
+
|
114
|
+
if fname && !fname.empty? && File.exists?(fname) && (File.ftype(fname) == 'file') && File.stat(fname).readable?
|
115
|
+
#This is where we call the the Dropbox Client
|
116
|
+
pp @client.put_file(new_name, open(fname))
|
117
|
+
else
|
118
|
+
puts "couldn't find the file #{ fname }"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def get(command)
|
123
|
+
dest = command[2]
|
124
|
+
if !command[1] || command[1].empty?
|
125
|
+
puts "please specify item to get"
|
126
|
+
elsif !dest || dest.empty?
|
127
|
+
puts "please specify full local path to dest, i.e. the file to write to"
|
128
|
+
elsif File.exists?(dest)
|
129
|
+
puts "error: File #{dest} already exists."
|
130
|
+
else
|
131
|
+
src = clean_up(command[1])
|
132
|
+
out = @client.get_file('/' + src)
|
133
|
+
open(dest, 'w'){|f| f.puts out }
|
134
|
+
puts "wrote file #{dest}."
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def mkdir(command)
|
139
|
+
pp @client.file_create_folder(command[1])
|
140
|
+
end
|
141
|
+
|
142
|
+
def thumbnail(command)
|
143
|
+
command[2] ||= 'small'
|
144
|
+
pp @client.thumbnail(command[1], command[2])
|
145
|
+
end
|
146
|
+
|
147
|
+
def cp(command)
|
148
|
+
src = clean_up(command[1])
|
149
|
+
dest = clean_up(command[2])
|
150
|
+
pp @client.file_copy(src, dest)
|
151
|
+
end
|
152
|
+
|
153
|
+
def mv(command)
|
154
|
+
src = clean_up(command[1])
|
155
|
+
dest = clean_up(command[2])
|
156
|
+
pp @client.file_move(src, dest)
|
157
|
+
end
|
158
|
+
|
159
|
+
def rm(command)
|
160
|
+
pp @client.file_delete(clean_up(command[1]))
|
161
|
+
end
|
162
|
+
|
163
|
+
def search(command)
|
164
|
+
resp = @client.search('/',clean_up(command[1]))
|
165
|
+
|
166
|
+
for item in resp
|
167
|
+
puts item['path']
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def info(command)
|
172
|
+
pp @client.account_info
|
173
|
+
end
|
174
|
+
|
175
|
+
def ls(command)
|
176
|
+
command[1] = '/' + clean_up(command[1] || '')
|
177
|
+
resp = @client.metadata(command[1])
|
178
|
+
|
179
|
+
if resp['contents'].length > 0
|
180
|
+
for item in resp['contents']
|
181
|
+
puts item['path']
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def help
|
187
|
+
puts "commands are: login #{LOGIN_REQUIRED.join(' ')} help exit"
|
188
|
+
end
|
189
|
+
|
190
|
+
def clean_up(str)
|
191
|
+
return str.gsub(/^\/+/, '') if str
|
192
|
+
str
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
cli = DropboxCLI.new
|
197
|
+
cli.command_loop
|