cloud_encrypted_sync 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cloud_encrypted_sync (0.1.1)
4
+ cloud_encrypted_sync (0.2.0)
5
5
 
6
6
  GEM
7
7
  remote: http://rubygems.org/
data/README.md CHANGED
@@ -54,3 +54,5 @@ tries to do something weird).
54
54
  ## Creating your own adapter
55
55
 
56
56
  TODO
57
+
58
+ [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/jsgarvin/cloud_encrypted_sync)
@@ -1,3 +1,4 @@
1
+ require 'active_support/core_ext/hash/indifferent_access'
1
2
  require 'active_support/core_ext/string'
2
3
  require 'digest'
3
4
  require 'fileutils'
@@ -1,11 +1,13 @@
1
1
  module CloudEncryptedSync
2
2
  module Adapters
3
3
  class Template
4
+ include Singleton
4
5
 
5
6
  class << self
6
7
 
7
8
  def inherited(subclass)
8
- register_subclass_with_parent(subclass)
9
+ register_with_parent(subclass)
10
+ super
9
11
  end
10
12
 
11
13
  def children
@@ -13,39 +15,60 @@ module CloudEncryptedSync
13
15
  end
14
16
 
15
17
  def parse_command_line_options(opts,command_line_options)
16
- raise Errors::TemplateMethodCalled.new('parse_command_line_options')
18
+ instance.parse_command_line_options(opts,command_line_options)
17
19
  end
18
20
 
19
21
  def write(data, key)
20
- raise Errors::TemplateMethodCalled.new('write')
22
+ instance.write(data, key)
21
23
  end
22
24
 
23
25
  def read(key)
24
- raise Errors::TemplateMethodCalled.new('read')
26
+ instance.read(key)
25
27
  end
26
28
 
27
29
  def delete(key)
28
- raise Errors::TemplateMethodCalled.new('delete')
30
+ instance.delete(key)
29
31
  end
30
32
 
31
33
  def key_exists?(key)
32
- raise Errors::TemplateMethodCalled.new('key_exists?')
34
+ instance.key_exists?(key)
33
35
  end
34
36
 
35
37
  #######
36
38
  private
37
39
  #######
38
40
 
39
- def register_subclass_with_parent(subclass)
41
+ def register_with_parent(subclass)
40
42
  name = formated_name_of(subclass)
41
43
  children[name] ||= subclass
42
44
  end
43
45
 
44
46
  def formated_name_of(subclass)
45
- puts "Subclass: #{subclass}"
46
47
  subclass.name.match(/([^:]+)$/)[0].underscore.to_sym
47
48
  end
49
+
50
+ end
51
+
52
+ def parse_command_line_options(opts,command_line_options)
53
+ raise Errors::TemplateMethodCalled.new('parse_command_line_options')
54
+ end
55
+
56
+ def write(data, key)
57
+ raise Errors::TemplateMethodCalled.new('write')
58
+ end
59
+
60
+ def read(key)
61
+ raise Errors::TemplateMethodCalled.new('read')
62
+ end
63
+
64
+ def delete(key)
65
+ raise Errors::TemplateMethodCalled.new('delete')
48
66
  end
67
+
68
+ def key_exists?(key)
69
+ raise Errors::TemplateMethodCalled.new('key_exists?')
70
+ end
71
+
49
72
  end
50
73
  end
51
74
  end
@@ -6,7 +6,7 @@ module CloudEncryptedSync
6
6
  attr_reader :option_parser
7
7
 
8
8
  def settings
9
- @settings ||= load_settings
9
+ @settings ||= load
10
10
  end
11
11
 
12
12
  def data_folder_path
@@ -17,17 +17,30 @@ module CloudEncryptedSync
17
17
  private
18
18
  #######
19
19
 
20
- def load_settings
20
+ def load
21
21
  touch_data_folder
22
- loaded_settings = {}
23
- loaded_settings = YAML.load_file(config_file_path) if File.exist?(config_file_path)
24
- loaded_settings.merge!(command_line_options)
25
- loaded_settings = loaded_settings.inject({}) do |options, (key, value)|
26
- options[(key.to_sym rescue key) || key] = value
27
- options
28
- end
22
+ loaded_settings = config_file_settings.merge(command_line_options).with_indifferent_access
23
+
29
24
  loaded_settings[:sync_path] = ARGV.shift unless ARGV.empty?
30
25
 
26
+ validate_settings(loaded_settings)
27
+
28
+ return loaded_settings
29
+ end
30
+
31
+ def config_file_settings
32
+ @config_file_settings ||= load_config_file_settings
33
+ end
34
+
35
+ def load_config_file_settings
36
+ if File.exist?(config_file_path)
37
+ YAML.load_file(config_file_path)
38
+ else
39
+ {}
40
+ end
41
+ end
42
+
43
+ def validate_settings(loaded_settings)
31
44
  if loaded_settings[:sync_path].nil?
32
45
  message = "You must supply a path to a folder to sync.\n\n#{option_parser.help}"
33
46
  raise Errors::IncompleteConfigurationError.new(message)
@@ -35,8 +48,6 @@ module CloudEncryptedSync
35
48
  message = "You must supply an encryption key.\n\n#{option_parser.help}"
36
49
  raise Errors::IncompleteConfigurationError.new(message)
37
50
  end
38
-
39
- return loaded_settings
40
51
  end
41
52
 
42
53
  def touch_data_folder
@@ -2,45 +2,42 @@ module CloudEncryptedSync
2
2
  module Adapters
3
3
  class Dummy < Template
4
4
 
5
- class << self
6
-
7
- def write(data,key)
8
- stored_data[bucket_name][key] = data
9
- end
10
-
11
- def parse_command_line_options(opts,command_line_options)
12
- opts.on('--bucket BUCKETNAME', 'Name of cloud adapter to use.') do |bucket_name|
13
- command_line_options[:bucket] = bucket_name
14
- end
15
- return command_line_options
16
- end
5
+ def write(data,key)
6
+ stored_data[bucket_name][key] = data
7
+ end
17
8
 
18
- def read(key)
19
- raise Errors::NoSuchKey.new("key doesn't exist: #{key}") unless key_exists?(key)
20
- stored_data[bucket_name][key]
9
+ def parse_command_line_options(opts,command_line_options)
10
+ opts.on('--bucket BUCKETNAME', 'Name of cloud adapter to use.') do |bucket_name|
11
+ command_line_options[:bucket] = bucket_name
21
12
  end
13
+ return command_line_options
14
+ end
22
15
 
23
- def delete(key)
24
- stored_data[bucket_name].delete(key)
25
- end
16
+ def read(key)
17
+ raise Errors::NoSuchKey.new("key doesn't exist: #{key}") unless key_exists?(key)
18
+ stored_data[bucket_name][key]
19
+ end
26
20
 
27
- def key_exists?(key)
28
- stored_data[bucket_name][key] ? true : false
29
- end
21
+ def delete(key)
22
+ stored_data[bucket_name].delete(key)
23
+ end
30
24
 
31
- #######
32
- private
33
- #######
25
+ def key_exists?(key)
26
+ stored_data[bucket_name][key] ? true : false
27
+ end
34
28
 
35
- def stored_data
36
- @stored_data ||= { bucket_name => {} }
37
- end
29
+ #######
30
+ private
31
+ #######
38
32
 
39
- def bucket_name
40
- Configuration.settings[:bucket].to_sym
41
- end
33
+ def stored_data
34
+ @stored_data ||= { bucket_name => {} }
35
+ end
42
36
 
37
+ def bucket_name
38
+ Configuration.settings[:bucket].to_sym
43
39
  end
40
+
44
41
  end
45
42
  end
46
43
  end
@@ -43,16 +43,14 @@ module CloudEncryptedSync
43
43
 
44
44
  def compile_local_hash
45
45
  hash = {}
46
- progress_meter = ProgressMeter.new(Dir["#{normalized_sync_path}/**/*"].length,:label => 'Compiling Local Index: ')
47
- completed_files = 0
48
- Find.find(normalized_sync_path) do |path|
49
- print progress_meter.update(completed_files)
50
- unless FileTest.directory?(path)
51
- hash[file_key(path)] = relative_file_path(path)
46
+ ProgressMeter.new(Dir["#{normalized_sync_path}/**/*"].length,:label => 'Compiling Local Index: ') do |progress_meter|
47
+ Find.find(normalized_sync_path) do |path|
48
+ unless FileTest.directory?(path)
49
+ hash[file_key(path)] = relative_file_path(path)
50
+ end
51
+ progress_meter.increment_completed_index
52
52
  end
53
- completed_files += 1
54
53
  end
55
- puts #newline for progress meter
56
54
  return hash
57
55
  end
58
56
 
@@ -8,18 +8,11 @@ module CloudEncryptedSync
8
8
  @label = options[:label] || ''
9
9
  @completed_index = 0.0
10
10
  @start_time = Time.now
11
+ yield self if block_given?
11
12
  end
12
13
 
13
- def to_s
14
- sprintf("\r#{label}%0.1f%% Complete. Time Remaining %s", percent_completed, estimated_time_remaining.strftime('%M:%S'))
15
- end
16
-
17
- def percent_completed
18
- (completed_index/max_index)*100
19
- end
20
-
21
- def time_elapsed
22
- Time.now - start_time
14
+ def estimated_time_remaining
15
+ Time.at(estimated_finish_time - Time.now)
23
16
  end
24
17
 
25
18
  def estimated_finish_time
@@ -30,14 +23,25 @@ module CloudEncryptedSync
30
23
  end
31
24
  end
32
25
 
33
- def estimated_time_remaining
34
- Time.at(estimated_finish_time - Time.now)
26
+ def percent_completed
27
+ (completed_index/max_index)*100
28
+ end
29
+
30
+ def time_elapsed
31
+ Time.now - start_time
35
32
  end
36
33
 
37
- def update(completed_index)
38
- self.completed_index = completed_index
39
- return self
34
+ def increment_completed_index(amount = 1)
35
+ self.completed_index += amount
36
+ notify
40
37
  end
41
38
 
39
+ #######
40
+ private
41
+ #######
42
+
43
+ def notify
44
+ print sprintf("\r#{label}%0.1f%% Complete. Time Remaining %s", percent_completed, estimated_time_remaining.strftime('%M:%S'))
45
+ end
42
46
  end
43
47
  end
@@ -16,45 +16,59 @@ module CloudEncryptedSync
16
16
  end
17
17
  end
18
18
 
19
+ #######
20
+ private
21
+ #######
22
+
19
23
  def push_files
20
- progress_meter = ProgressMeter.new(files_to_push.keys.size,:label => 'Pushing Files: ')
21
- pushed_files_counter = 0
22
- files_to_push.each_pair do |key,relative_path|
23
- puts #newline for progress meter
24
- if liaison.key_exists?(key)
25
- #already exists. probably left over from an earlier aborted push
26
- puts "Not Pushing (already exists): #{relative_path}"
27
- else
28
- puts "Pushing: #{relative_path}"
29
- liaison.push(File.read(Index.full_file_path(relative_path)),key)
30
- self.finalize_required = true
24
+ ProgressMeter.new(files_to_push.keys.size,:label => 'Pushing Files: ') do |progress_meter|
25
+ pushed_files_counter = 0
26
+ files_to_push.each_pair do |key,relative_path|
27
+ push_file_if_necessary(key,relative_path)
28
+ progress_meter.increment_completed_index
31
29
  end
32
- pushed_files_counter += 1
33
- print progress_meter.update(pushed_files_counter)
30
+ end
31
+ end
32
+
33
+ def push_file_if_necessary(key,relative_path)
34
+ if liaison.key_exists?(key)
35
+ #already exists. probably left over from an earlier aborted push
36
+ puts "\nNot Pushing (already exists): #{relative_path}"
37
+ else
38
+ puts "\nPushing: #{relative_path}"
39
+ liaison.push(File.read(Index.full_file_path(relative_path)),key)
40
+ self.finalize_required = true
34
41
  end
35
42
  end
36
43
 
37
44
  def pull_files
38
- progress_meter = ProgressMeter.new(files_to_pull.keys.size,:label => 'Pulling Files: ')
39
- pulled_files_counter = 0
40
- files_to_pull.each_pair do |key,relative_path|
41
- full_path = Index.full_file_path(relative_path)
42
- puts #newline for progress meter
43
- if File.exist?(full_path) and (Index.file_key(full_path) == key)
44
- #already exists. probably left over from an earlier aborted pull
45
- puts "Not Pulling (already exists): #{full_path}"
46
- else
47
- Dir.mkdir(File.dirname(full_path)) unless File.exist?(File.dirname(full_path))
48
- puts "Pulling: #{relative_path}"
49
- begin
50
- File.open(full_path,'w') { |file| file.write(liaison.pull(key)) }
51
- self.finalize_required = true
52
- rescue Errors::NoSuchKey
53
- puts "Failed to pull #{relative_path}"
54
- end
45
+ ProgressMeter.new(files_to_pull.keys.size,:label => 'Pulling Files: ') do |progress_meter|
46
+ files_to_pull.each_pair do |key,relative_path|
47
+ pull_file_if_necessary(key,relative_path)
48
+ progress_meter.increment_completed_index
55
49
  end
56
- pulled_files_counter += 1
57
- print progress_meter.update(pulled_files_counter)
50
+ end
51
+ end
52
+
53
+ def pull_file_if_necessary(key,relative_path)
54
+ full_path = Index.full_file_path(relative_path)
55
+ if File.exist?(full_path) and (Index.file_key(full_path) == key)
56
+ #already exists. probably left over from an earlier aborted pull
57
+ puts "\nNot Pulling (already exists): #{full_path}"
58
+ else
59
+ Dir.mkdir(File.dirname(full_path)) unless File.exist?(File.dirname(full_path))
60
+ puts "\nPulling: #{relative_path}"
61
+ pull_file_or_rescue(key,relative_path)
62
+ end
63
+ end
64
+
65
+ def pull_file_or_rescue(key,relative_path)
66
+ full_path = Index.full_file_path(relative_path)
67
+ begin
68
+ File.open(full_path,'w') { |file| file.write(liaison.pull(key)) }
69
+ self.finalize_required = true
70
+ rescue Errors::NoSuchKey
71
+ puts "\nFailed to pull #{relative_path}"
58
72
  end
59
73
  end
60
74
 
@@ -83,10 +97,6 @@ module CloudEncryptedSync
83
97
  Index.write if finalize_required
84
98
  end
85
99
 
86
- #######
87
- private
88
- #######
89
-
90
100
  def liaison
91
101
  AdapterLiaison.instance
92
102
  end
@@ -1,3 +1,3 @@
1
1
  module CloudEncryptedSync
2
- VERSION = '0.1.2'
2
+ VERSION = '0.2.0'
3
3
  end
@@ -4,7 +4,7 @@ module CloudEncryptedSync
4
4
  class AdapterTemplateTest < ActiveSupport::TestCase
5
5
 
6
6
  test 'should register with parent class on inheritance' do
7
- Adapters::Template.expects(:register_subclass_with_parent).returns(true)
7
+ Adapters::Template.expects(:register_with_parent).returns(true)
8
8
  Class.new(Adapters::Template)
9
9
  end
10
10
 
@@ -9,32 +9,36 @@ module CloudEncryptedSync
9
9
  end
10
10
 
11
11
  test 'should calculate percent completed' do
12
- @progress_meter.update(1)
12
+ @progress_meter.increment_completed_index
13
13
  assert_equal(25,@progress_meter.percent_completed)
14
14
  end
15
15
 
16
16
  test 'should calculate time elapsed' do
17
- assert_in_delta(42,@progress_meter.time_elapsed,0.01)
17
+ assert_in_delta(42,@progress_meter.time_elapsed,0.02)
18
18
  end
19
19
 
20
20
  test 'should estimate finish time' do
21
- @progress_meter.update(1)
22
- assert_in_delta(Time.now+(42*3),@progress_meter.estimated_finish_time,0.01)
21
+ @progress_meter.increment_completed_index
22
+ assert_in_delta(Time.now+(42*3),@progress_meter.estimated_finish_time,0.02)
23
23
  end
24
24
 
25
25
  test 'should estimate time remaining' do
26
- @progress_meter.update(1)
27
- assert_in_delta((42*3),@progress_meter.estimated_time_remaining.to_f,0.01)
26
+ @progress_meter.increment_completed_index
27
+ assert_in_delta((42*3),@progress_meter.estimated_time_remaining.to_f,0.02)
28
28
  end
29
29
 
30
- test 'should update progress and return self' do
30
+ test 'should increment counter and write to stdout' do
31
+ assert_equal('',$stdout.string)
31
32
  assert_difference('@progress_meter.completed_index',2) do
32
- assert_equal(@progress_meter,@progress_meter.update(2))
33
+ @progress_meter.increment_completed_index(2)
33
34
  end
35
+ assert_match(/\% Complete/,$stdout.string)
34
36
  end
35
37
 
36
38
  test 'should render string' do
37
- assert_match(/0\% Complete/,@progress_meter.to_s)
39
+ assert_equal('',$stdout.string)
40
+ @progress_meter.send(:notify)
41
+ assert_match(/\% Complete/,$stdout.string)
38
42
  end
39
43
  end
40
44
  end
@@ -25,13 +25,13 @@ module CloudEncryptedSync
25
25
  Adapters::Dummy.expects(:write)
26
26
 
27
27
  assert_equal('',$stdout.string)
28
- Synchronizer.push_files
28
+ Synchronizer.send(:push_files)
29
29
  assert_match(/\% Complete/,$stdout.string)
30
30
  end
31
31
 
32
32
  test 'should not push files that already exist' do
33
33
  AdapterLiaison.instance.stubs(:key_exists?).returns(true)
34
- Synchronizer.push_files
34
+ Synchronizer.send(:push_files)
35
35
  assert_match(/\(already exists\)/,$stdout.string)
36
36
  end
37
37
 
@@ -40,7 +40,7 @@ module CloudEncryptedSync
40
40
  Adapters::Dummy.expects(:read).with('new_file_key').returns(Cryptographer.encrypt_data('foobar'))
41
41
  assert_equal('',$stdout.string)
42
42
  assert_difference('Dir["#{test_source_folder}/**/*"].length') do
43
- Synchronizer.pull_files
43
+ Synchronizer.send(:pull_files)
44
44
  end
45
45
  assert_match(/\% Complete/,$stdout.string)
46
46
  end
@@ -49,14 +49,14 @@ module CloudEncryptedSync
49
49
  Synchronizer.stubs(:files_to_pull).returns({:foo => 'bar'})
50
50
  File.stubs(:exist?).returns(true)
51
51
  Index.stubs(:file_key).returns(:foo)
52
- Synchronizer.pull_files
52
+ Synchronizer.send(:pull_files)
53
53
  assert_match(/\(already exists\)/,$stdout.string)
54
54
  end
55
55
 
56
56
  test 'should gracefully recover if pull fails' do
57
57
  Synchronizer.stubs(:files_to_pull).returns({:foo => 'bar'})
58
58
  AdapterLiaison.instance.stubs(:pull).raises(Errors::NoSuchKey)
59
- Synchronizer.pull_files
59
+ Synchronizer.send(:pull_files)
60
60
  assert_match(/Failed to pull/,$stdout.string)
61
61
  end
62
62
 
@@ -65,7 +65,7 @@ module CloudEncryptedSync
65
65
  Index.stubs(:local).returns({'saved_file_key' => 'test_sub_folder/saved_file.txt'})
66
66
  Synchronizer.stubs(:last_sync_hash).returns({'saved_file_key' => 'test_sub_folder/saved_file.txt', 'deleted_file_key' => 'test_sub_folder/deleted_file.txt'})
67
67
  Adapters::Dummy.expects(:delete).with('deleted_file_key').returns(true)
68
- Synchronizer.delete_remote_files
68
+ Synchronizer.send(:delete_remote_files)
69
69
  assert_match(/Deleting Remote/,$stdout.string)
70
70
  end
71
71
 
@@ -73,7 +73,7 @@ module CloudEncryptedSync
73
73
  Index.stubs(:remote).returns({'saved_file_key' => 'test_sub_folder/saved_file.txt'})
74
74
  Synchronizer.stubs(:last_sync_hash).returns({'saved_file_key' => 'test_sub_folder/saved_file.txt'}.merge(Index.local))
75
75
  assert_difference('Dir["#{test_source_folder}/**/*"].length',-1) do
76
- Synchronizer.delete_local_files
76
+ Synchronizer.send(:delete_local_files)
77
77
  end
78
78
  assert_match(/Deleting Local/,$stdout.string)
79
79
  end
@@ -81,7 +81,7 @@ module CloudEncryptedSync
81
81
  test 'should gracefully recover if local file disappears before delete' do
82
82
  Synchronizer.stubs(:local_files_to_delete).returns({:foo => 'bar'})
83
83
  File.stubs(:exist?).returns(false)
84
- Synchronizer.delete_local_files
84
+ Synchronizer.send(:delete_local_files)
85
85
  assert_match(/Not Deleting Local/,$stdout.string)
86
86
  end
87
87
 
@@ -89,7 +89,7 @@ module CloudEncryptedSync
89
89
  Synchronizer.instance_variable_set(:@finalize_required,true)
90
90
  Index.expects(:write)
91
91
 
92
- Synchronizer.finalize
92
+ Synchronizer.send(:finalize)
93
93
  end
94
94
 
95
95
  test 'should want to push everything on first run with local files and empty remote' do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cloud_encrypted_sync
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-20 00:00:00.000000000 Z
12
+ date: 2012-11-21 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: mocha