dbox 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -9,9 +9,15 @@ An easy way to push and pull your Dropbox folders, with fine-grained control ove
9
9
  Installation
10
10
  ------------
11
11
 
12
+ ### Install dbox
13
+
14
+ ```sh
15
+ $ gem install dbox
16
+ ```
17
+
12
18
  ### Get developer keys
13
19
 
14
- * Follow the instructions at https://www.dropbox.com/developers/quickstart to create a Dropbox development application, and copy the application keys.
20
+ * Follow the instructions at https://www.dropbox.com/developers/quickstart to create a Dropbox development application, and copy the application keys. Unless you get your app approved for production status, these keys will only work with the account you create them under, so make sure you are logged in with the account you want to access from dbox.
15
21
 
16
22
  * Now either set the keys as environment variables:
17
23
 
@@ -65,16 +71,16 @@ Usage
65
71
  $ dbox authorize
66
72
  ```
67
73
 
68
- ### Clone an existing Dropbox folder
74
+ ### Create a new Dropbox folder
69
75
 
70
76
  ```sh
71
- $ dbox clone <remote_path> [<local_path>]
77
+ $ dbox create <remote_path> [<local_path>]
72
78
  ```
73
79
 
74
- ### Create a new Dropbox folder
80
+ ### Clone an existing Dropbox folder
75
81
 
76
82
  ```sh
77
- $ dbox create <remote_path> [<local_path>]
83
+ $ dbox clone <remote_path> [<local_path>]
78
84
  ```
79
85
 
80
86
  ### Pull (download changes from Dropbox)
data/Rakefile CHANGED
@@ -17,19 +17,15 @@ Jeweler::Tasks.new do |gem|
17
17
  end
18
18
  Jeweler::RubygemsDotOrgTasks.new
19
19
 
20
- require 'rake/testtask'
21
- Rake::TestTask.new(:test) do |test|
22
- test.libs << 'lib' << 'test'
23
- test.pattern = 'test/**/test_*.rb'
24
- test.verbose = true
20
+ require 'rspec/core'
21
+ require 'rspec/core/rake_task'
22
+ RSpec::Core::RakeTask.new(:spec) do |spec|
23
+ spec.pattern = FileList['spec/**/*_spec.rb']
25
24
  end
26
25
 
27
- require 'rcov/rcovtask'
28
- Rcov::RcovTask.new do |test|
29
- test.libs << 'test'
30
- test.pattern = 'test/**/test_*.rb'
31
- test.verbose = true
32
- test.rcov_opts << '--exclude "gems/*"'
26
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
27
+ spec.pattern = 'spec/**/*_spec.rb'
28
+ spec.rcov = true
33
29
  end
34
30
 
35
- task :default => :test
31
+ task :default => :spec
data/TODO.txt CHANGED
@@ -1,6 +1,5 @@
1
- * Add tests
2
- * Add proper logger
3
- * See if pull re-creates locally-deleted files (it shouldn't)
1
+ * More helpers in specs (and string style paths instead of join all over the place)
2
+ * Have pull, push, etc return a list of changed files
4
3
  * Look down directory tree until you hit a .dropbox.db file
5
4
  * Solve upload -> re-download issue
6
5
  * Add rename_remote command
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.2.0
data/bin/dbox CHANGED
@@ -5,14 +5,26 @@ require "dbox"
5
5
 
6
6
  # usage line
7
7
  def usage
8
- "Usage:
9
- dbox authorize
10
- export DROPBOX_AUTH_KEY=abcdef012345678
11
- export DROPBOX_AUTH_SECRET=876543210fedcba
12
- dbox create <remote_path> [<local_path>]
13
- dbox clone <remote_path> [<local_path>]
14
- dbox pull
15
- dbox push"
8
+ <<_EOF
9
+ Usage: dbox <commond> [<args>]
10
+
11
+ Commands:
12
+ authorize Generate auth keys
13
+ create <remote_path> [<local_path>] Create a new Dropbox folder
14
+ clone <remote_path> [<local_path>] Clone an existing Dropbox folder
15
+ pull [<local_path>] Pull chonges from Dropbox
16
+ push [<local_path>] Push changes to Dropbox
17
+
18
+ Environment varables needed for everything:
19
+ export DROPBOX_APP_KEY=cmlrrjd3j0gbend
20
+ export DROPBOX_APP_SECRET=uvuulp75xf9jffl
21
+
22
+ Environment varables needed for everything other than authorize:
23
+ export DROPBOX_AUTH_KEY=v4d7l1rez1czksn
24
+ export DROPBOX_AUTH_SECRET=pqej9rmnj0i1gcxr4
25
+
26
+ See http://github.com/kenpratt/dbox for examples and more information
27
+ _EOF
16
28
  end
17
29
  def print_usage_and_quit; puts usage; exit 1; end
18
30
 
data/dbox.gemspec ADDED
@@ -0,0 +1,60 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{dbox}
8
+ s.version = "0.2.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = [%q{Ken Pratt}]
12
+ s.date = %q{2011-05-14}
13
+ s.description = %q{An easy-to-use Dropbox client with fine-grained control over syncs.}
14
+ s.email = %q{ken@kenpratt.net}
15
+ s.executables = [%q{dbox}]
16
+ s.extra_rdoc_files = [
17
+ "LICENSE.txt",
18
+ "README.md"
19
+ ]
20
+ s.files = [
21
+ ".document",
22
+ "LICENSE.txt",
23
+ "README.md",
24
+ "Rakefile",
25
+ "TODO.txt",
26
+ "VERSION",
27
+ "bin/dbox",
28
+ "dbox.gemspec",
29
+ "lib/dbox.rb",
30
+ "lib/dbox/api.rb",
31
+ "lib/dbox/db.rb",
32
+ "lib/dbox/loggable.rb",
33
+ "spec/dbox_spec.rb",
34
+ "spec/spec_helper.rb",
35
+ "vendor/dropbox-client-ruby/LICENSE",
36
+ "vendor/dropbox-client-ruby/README",
37
+ "vendor/dropbox-client-ruby/Rakefile",
38
+ "vendor/dropbox-client-ruby/config/testing.json.example",
39
+ "vendor/dropbox-client-ruby/lib/dropbox.rb",
40
+ "vendor/dropbox-client-ruby/manifest",
41
+ "vendor/dropbox-client-ruby/test/authenticator_test.rb",
42
+ "vendor/dropbox-client-ruby/test/client_test.rb",
43
+ "vendor/dropbox-client-ruby/test/util.rb"
44
+ ]
45
+ s.homepage = %q{http://github.com/kenpratt/dbox}
46
+ s.licenses = [%q{MIT}]
47
+ s.require_paths = [%q{lib}]
48
+ s.rubygems_version = %q{1.8.1}
49
+ s.summary = %q{Dropbox made easy.}
50
+
51
+ if s.respond_to? :specification_version then
52
+ s.specification_version = 3
53
+
54
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
55
+ else
56
+ end
57
+ else
58
+ end
59
+ end
60
+
data/lib/dbox.rb CHANGED
@@ -6,7 +6,9 @@ require "dropbox"
6
6
  require "fileutils"
7
7
  require "time"
8
8
  require "yaml"
9
+ require "logger"
9
10
 
11
+ require "dbox/loggable"
10
12
  require "dbox/api"
11
13
  require "dbox/db"
12
14
 
@@ -42,7 +44,7 @@ module Dbox
42
44
  def self.clean_remote_path(path)
43
45
  if path
44
46
  path.sub(/\/$/,'')
45
- path[0] == "/" ? path : "/#{path}"
47
+ path[0].chr == "/" ? path : "/#{path}"
46
48
  else
47
49
  raise "Missing remote path"
48
50
  end
data/lib/dbox/api.rb CHANGED
@@ -1,9 +1,9 @@
1
1
  module Dbox
2
2
  class API
3
+ include Loggable
4
+
3
5
  def self.authorize
4
- puts conf.inspect
5
6
  auth = Authenticator.new(conf)
6
- puts auth.inspect
7
7
  authorize_url = auth.get_request_token
8
8
  puts "Please visit the following URL in your browser, log into Dropbox, and authorize the app you created.\n\n#{authorize_url}\n\nWhen you have done so, press [ENTER] to continue."
9
9
  STDIN.readline
@@ -43,31 +43,42 @@ module Dbox
43
43
 
44
44
  def metadata(path = "/")
45
45
  path = escape_path(path)
46
- puts "[api] fetching metadata for #{path}"
47
- @client.metadata(@conf["root"], path)
46
+ log.debug "Fetching metadata for #{path}"
47
+ begin
48
+ case res = @client.metadata(@conf["root"], path)
49
+ when Hash
50
+ res
51
+ when Net::HTTPNotFound
52
+ raise "Remote path does not exist"
53
+ else
54
+ raise "Unexpected result from GET /metadata: #{res.inspect}"
55
+ end
56
+ rescue DropboxError => e
57
+ raise "Server error -- might be a hiccup, please try your request again (#{e.message})"
58
+ end
48
59
  end
49
60
 
50
61
  def create_dir(path)
51
62
  path = escape_path(path)
52
- puts "[api] creating #{path}"
63
+ log.info "Creating #{path}"
53
64
  @client.file_create_folder(@conf["root"], path)
54
65
  end
55
66
 
56
67
  def delete_dir(path)
57
68
  path = escape_path(path)
58
- puts "[api] deleting #{path}"
69
+ log.info "Deleting #{path}"
59
70
  @client.file_delete(@conf["root"], path)
60
71
  end
61
72
 
62
73
  def get_file(path)
63
74
  path = escape_path(path)
64
- puts "[api] downloading #{path}"
75
+ log.info "Downloading #{path}"
65
76
  @client.get_file(@conf["root"], path)
66
77
  end
67
78
 
68
79
  def put_file(path, file_obj)
69
80
  path = escape_path(path)
70
- puts "[api] uploading #{path}"
81
+ log.info "Uploading #{path}"
71
82
  dir = File.dirname(path)
72
83
  name = File.basename(path)
73
84
  @client.put_file(@conf["root"], dir, name, file_obj)
@@ -75,7 +86,7 @@ module Dbox
75
86
 
76
87
  def delete_file(path)
77
88
  path = escape_path(path)
78
- puts "[api] deleting #{path}"
89
+ log.info "Deleting #{path}"
79
90
  @client.file_delete(@conf["root"], path)
80
91
  end
81
92
 
data/lib/dbox/db.rb CHANGED
@@ -1,27 +1,23 @@
1
1
  module Dbox
2
2
  class DB
3
+ include Loggable
4
+
3
5
  DB_FILE = ".dropbox.db"
4
6
 
5
7
  attr_accessor :local_path
6
8
 
7
9
  def self.create(remote_path, local_path)
8
- puts "[db] Creating remote folder: #{remote_path}"
10
+ log.info "Creating remote folder: #{remote_path}"
9
11
  api.create_dir(remote_path)
10
12
  clone(remote_path, local_path)
11
13
  end
12
14
 
13
15
  def self.clone(remote_path, local_path)
14
- puts "[db] Cloning #{remote_path} into #{local_path}"
15
- case res = api.metadata(remote_path)
16
- when Hash
17
- raise "Remote path error" unless remote_path == res["path"]
18
- db = new(local_path, res)
19
- db.pull
20
- when Net::HTTPNotFound
21
- raise "Remote path does not exist"
22
- else
23
- raise "Clone failed: #{res.inspect}"
24
- end
16
+ log.info "Cloning #{remote_path} into #{local_path}"
17
+ res = api.metadata(remote_path)
18
+ raise "Remote path error" unless remote_path == res["path"]
19
+ db = new(local_path, res)
20
+ db.pull
25
21
  end
26
22
 
27
23
  def self.load(local_path)
@@ -124,6 +120,8 @@ module Dbox
124
120
  end
125
121
 
126
122
  class DropboxBlob
123
+ include Loggable
124
+
127
125
  attr_reader :path, :revision, :modified_at
128
126
 
129
127
  def initialize(db, res)
@@ -235,7 +233,7 @@ module Dbox
235
233
  def pull
236
234
  prev = self.clone
237
235
  prev.freeze
238
- puts "[db] pulling"
236
+ log.info "Pulling changes"
239
237
  res = api.metadata(remote_path)
240
238
  update(res)
241
239
  if contents_hash != prev.contents_hash
@@ -247,7 +245,7 @@ module Dbox
247
245
  def push
248
246
  prev = self.clone
249
247
  prev.freeze
250
- puts "[db] pushing"
248
+ log.info "Pushing changes"
251
249
  res = gather_info(@path)
252
250
  update(res)
253
251
  reconcile(prev, :up)
@@ -305,7 +303,7 @@ module Dbox
305
303
  end
306
304
 
307
305
  def create_local
308
- puts "[fs] creating dir #{local_path}"
306
+ log.info "Creating dir: #{local_path}"
309
307
  saving_parent_timestamp do
310
308
  FileUtils.mkdir_p(local_path)
311
309
  update_file_timestamp
@@ -313,14 +311,14 @@ module Dbox
313
311
  end
314
312
 
315
313
  def delete_local
316
- puts "[fs] deleting dir #{local_path}"
314
+ log.info "Deleting dir: #{local_path}"
317
315
  saving_parent_timestamp do
318
316
  FileUtils.rm_r(local_path)
319
317
  end
320
318
  end
321
319
 
322
320
  def update_local
323
- puts "[fs] updating dir #{local_path}"
321
+ log.info "Updating dir: #{local_path}"
324
322
  update_file_timestamp
325
323
  end
326
324
 
@@ -356,7 +354,7 @@ module Dbox
356
354
  end
357
355
 
358
356
  def create_local
359
- puts "[fs] creating file #{local_path}"
357
+ log.info "Creating file: #{local_path}"
360
358
  saving_parent_timestamp do
361
359
  download
362
360
  update_file_timestamp
@@ -364,14 +362,14 @@ module Dbox
364
362
  end
365
363
 
366
364
  def delete_local
367
- puts "[fs] deleting file #{local_path}"
365
+ log.info "Deleting file: #{local_path}"
368
366
  saving_parent_timestamp do
369
367
  FileUtils.rm_rf(local_path)
370
368
  end
371
369
  end
372
370
 
373
371
  def update_local
374
- puts "[fs] updating file #{local_path}"
372
+ log.info "Updating file: #{local_path}"
375
373
  download
376
374
  update_file_timestamp
377
375
  end
@@ -0,0 +1,34 @@
1
+ module Dbox
2
+ module Loggable
3
+ def self.included receiver
4
+ receiver.extend ClassMethods
5
+ end
6
+
7
+ module ClassMethods
8
+ def log
9
+ Dbox.log
10
+ end
11
+ end
12
+
13
+ def log
14
+ Dbox.log
15
+ end
16
+ end
17
+
18
+ def self.log
19
+ @logger ||= setup_logger
20
+ end
21
+
22
+ def self.setup_logger
23
+ if defined?(LOGGER)
24
+ LOGGER
25
+ elsif defined?(Rails.logger)
26
+ Rails.logger
27
+ else
28
+ l = Logger.new(STDOUT)
29
+ l.level = Logger::INFO
30
+ l.formatter = proc {|severity, datetime, progname, msg| "[#{severity}] #{msg}\n" }
31
+ l
32
+ end
33
+ end
34
+ end
data/spec/dbox_spec.rb ADDED
@@ -0,0 +1,121 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/spec_helper")
2
+
3
+ include FileUtils
4
+
5
+ describe Dbox do
6
+ before(:each) do
7
+ cd LOCAL_TEST_PATH
8
+ @name = randname()
9
+ @local = File.join(LOCAL_TEST_PATH, @name)
10
+ @remote = File.join(REMOTE_TEST_PATH, @name)
11
+ end
12
+
13
+ describe "#create" do
14
+ it "creates the local directory" do
15
+ Dbox.create(@remote)
16
+ File.exists?(@local).should be_true
17
+ end
18
+
19
+ xit "should fail if the remote already exists" do
20
+ Dbox.create(@remote)
21
+ rm_rf @local
22
+ expect { Dbox.create(@remote) }.to raise_error("Remote path already exists")
23
+ File.exists?(@local).should be_false
24
+ end
25
+ end
26
+
27
+ describe "#clone" do
28
+ it "creates the local directory" do
29
+ Dbox.create(@remote)
30
+ rm_rf @local
31
+ File.exists?(@local).should be_false
32
+ Dbox.clone(@remote)
33
+ File.exists?(@local).should be_true
34
+ end
35
+
36
+ it "should fail if the remote does not exist" do
37
+ expect { Dbox.clone(@remote) }.to raise_error("Remote path does not exist")
38
+ File.exists?(@local).should be_false
39
+ end
40
+ end
41
+
42
+ describe "#pull" do
43
+ it "should fail if the local dir is missing" do
44
+ expect { Dbox.pull(@local) }.to raise_error(/No DB file found/)
45
+ end
46
+
47
+ it "should fail if the remote dir is missing" do
48
+ Dbox.create(@remote)
49
+ modify_dbfile {|s| s.sub(/^remote_path: \/.*$/, "remote_path: /#{randname()}") }
50
+ expect { Dbox.pull(@local) }.to raise_error("Remote path does not exist")
51
+ end
52
+
53
+ it "should be able to pull" do
54
+ Dbox.create(@remote)
55
+ expect { Dbox.pull(@local) }.to_not raise_error
56
+ end
57
+
58
+ it "should be able to pull from inside the dir" do
59
+ Dbox.create(@remote)
60
+ cd @local
61
+ expect { Dbox.pull }.to_not raise_error
62
+ end
63
+
64
+ it "should be able to pull changes" do
65
+ Dbox.create(@remote)
66
+ File.exists?("#{@local}/hello.txt").should be_false
67
+
68
+ cd ALTERNATE_LOCAL_TEST_PATH
69
+ Dbox.clone(@remote)
70
+ cd @name
71
+ touch "hello.txt"
72
+ Dbox.push
73
+
74
+ expect { Dbox.pull(@local) }.to_not raise_error
75
+ File.exists?("#{@local}/hello.txt").should be_true
76
+ end
77
+
78
+ it "should be able to pull after deleting a file and not have the file re-created" do
79
+ Dbox.create(@remote)
80
+ cd @name
81
+ touch "hello.txt"
82
+ Dbox.push
83
+ Dbox.pull
84
+ rm "hello.txt"
85
+ Dbox.pull
86
+ File.exists?("#{@local}/hello.txt").should be_false
87
+ end
88
+ end
89
+
90
+ describe "#push" do
91
+ it "should fail if the local dir is missing" do
92
+ expect { Dbox.push(@local) }.to raise_error(/No DB file found/)
93
+ end
94
+
95
+ it "should be able to push" do
96
+ Dbox.create(@remote)
97
+ expect { Dbox.push(@local) }.to_not raise_error
98
+ end
99
+
100
+ it "should be able to push from inside the dir" do
101
+ Dbox.create(@remote)
102
+ cd @local
103
+ expect { Dbox.push }.to_not raise_error
104
+ end
105
+
106
+ it "should be able to push new file" do
107
+ Dbox.create(@remote)
108
+ touch File.join(@local, "foo.txt")
109
+ expect { Dbox.push(@local) }.to_not raise_error
110
+ end
111
+
112
+ it "should create the remote dir if it is missing" do
113
+ Dbox.create(@remote)
114
+ touch File.join(@local, "foo.txt")
115
+ @new_name = randname()
116
+ @new_remote = File.join(REMOTE_TEST_PATH, @new_name)
117
+ modify_dbfile {|s| s.sub(/^remote_path: \/.*$/, "remote_path: #{@new_remote}") }
118
+ expect { Dbox.push(@local) }.to_not raise_error
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,26 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require "rspec"
4
+ require "dbox"
5
+ require "fileutils"
6
+
7
+ LOCAL_TEST_PATH = File.expand_path(File.join(File.dirname(__FILE__), "..", "tmp", "test_dirs"))
8
+ ALTERNATE_LOCAL_TEST_PATH = File.join(LOCAL_TEST_PATH, "alternate")
9
+ FileUtils.mkdir_p(LOCAL_TEST_PATH)
10
+ FileUtils.mkdir_p(ALTERNATE_LOCAL_TEST_PATH)
11
+
12
+ REMOTE_TEST_PATH = "/dbox_test_dirs"
13
+
14
+ LOGGER = Logger.new(File.expand_path(File.join(File.dirname(__FILE__), "..", "tmp", "test.log")))
15
+
16
+ def randname
17
+ u = `uuidgen`.chomp
18
+ "test-#{u}"
19
+ end
20
+
21
+ def modify_dbfile
22
+ dbfile = File.join(@local, Dbox::DB::DB_FILE)
23
+ s = File.open(dbfile, "r").read
24
+ s = yield s
25
+ File.open(dbfile, "w") {|f| f << s }
26
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dbox
3
3
  version: !ruby/object:Gem::Version
4
- hash: 27
4
+ hash: 23
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 1
8
+ - 2
9
9
  - 0
10
- version: 0.1.0
10
+ version: 0.2.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Ken Pratt
@@ -35,11 +35,13 @@ files:
35
35
  - TODO.txt
36
36
  - VERSION
37
37
  - bin/dbox
38
+ - dbox.gemspec
38
39
  - lib/dbox.rb
39
40
  - lib/dbox/api.rb
40
41
  - lib/dbox/db.rb
41
- - test/helper.rb
42
- - test/test_dbox.rb
42
+ - lib/dbox/loggable.rb
43
+ - spec/dbox_spec.rb
44
+ - spec/spec_helper.rb
43
45
  - vendor/dropbox-client-ruby/LICENSE
44
46
  - vendor/dropbox-client-ruby/README
45
47
  - vendor/dropbox-client-ruby/Rakefile
data/test/helper.rb DELETED
@@ -1,10 +0,0 @@
1
- require 'rubygems'
2
- require 'test/unit'
3
- require 'shoulda'
4
-
5
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
- $LOAD_PATH.unshift(File.dirname(__FILE__))
7
- require 'dbox'
8
-
9
- class Test::Unit::TestCase
10
- end
data/test/test_dbox.rb DELETED
@@ -1,7 +0,0 @@
1
- require 'helper'
2
-
3
- class TestDbox < Test::Unit::TestCase
4
- should "probably rename this file and start testing for real" do
5
- flunk "hey buddy, you should probably rename this file and start testing for real"
6
- end
7
- end