cloud_encrypted_sync 0.1.2 → 0.2.0

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/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