dutiful 0.0.9 → 0.0.10
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.
- checksums.yaml +4 -4
- data/db/osx-screencapture.toml +7 -0
- data/lib/dutiful/application.rb +20 -24
- data/lib/dutiful/application_default.rb +83 -0
- data/lib/dutiful/application_file.rb +53 -30
- data/lib/dutiful/commands/backup.rb +7 -5
- data/lib/dutiful/commands/list.rb +8 -3
- data/lib/dutiful/commands/main.rb +14 -2
- data/lib/dutiful/commands/restore.rb +7 -5
- data/lib/dutiful/commands.rb +0 -1
- data/lib/dutiful/config.rb +4 -0
- data/lib/dutiful/logger.rb +22 -0
- data/lib/dutiful/storage.rb +1 -1
- data/lib/dutiful/version.rb +1 -1
- data/lib/dutiful.rb +2 -0
- data/template/config.toml +6 -0
- metadata +21 -4
- data/lib/dutiful/commands/sync.rb +0 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 77661dc7c89c798e383b4866921f2d12d10e4c63
|
4
|
+
data.tar.gz: 4e7a2cc9951499ab8761dbd8547a56bd1e1f50d6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e3febcb417b4a94930ddad87562e7d013481848eaa2ae6f83e9a822a39a76bcdf905f7e722ab8d5fbfa80212066b1608dd6d36f6ef5e4ba6e4eae8db5601df49
|
7
|
+
data.tar.gz: 60f80377d514e640f756b942901a6f8ccd2d8fbc869d9fa5f0e69347c18f6922fce176ce9eab9a2e1279d84a4b14ccb598e2e3aa91197f9436e33cdbbfe060c5
|
data/lib/dutiful/application.rb
CHANGED
@@ -3,8 +3,14 @@ class Dutiful::Application
|
|
3
3
|
@path = path
|
4
4
|
end
|
5
5
|
|
6
|
+
def defaults
|
7
|
+
return [] unless Dutiful::Config.osx?
|
8
|
+
|
9
|
+
Array(content[:default]).map { |default| Dutiful::ApplicationDefault.new default }
|
10
|
+
end
|
11
|
+
|
6
12
|
def files
|
7
|
-
content[:file].map { |file| Dutiful::ApplicationFile.new file[:path], file[:condition] }
|
13
|
+
Array(content[:file]).map { |file| Dutiful::ApplicationFile.new file[:path], file[:condition] }
|
8
14
|
end
|
9
15
|
|
10
16
|
def name
|
@@ -12,25 +18,19 @@ class Dutiful::Application
|
|
12
18
|
end
|
13
19
|
|
14
20
|
def backup(&block)
|
15
|
-
|
21
|
+
(files + defaults).each do |file|
|
22
|
+
if file.exist?
|
23
|
+
yield file, file.backup if block_given?
|
24
|
+
else
|
25
|
+
yield file if block_given?
|
26
|
+
end
|
27
|
+
end
|
16
28
|
end
|
17
29
|
|
18
30
|
def restore(&block)
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
def sync(backup_only: false, restore_only: false)
|
23
|
-
files.each do |file|
|
24
|
-
if file.should_sync?
|
25
|
-
result = if backup_only && file.exist?
|
26
|
-
Dutiful::Config.storage.backup(file)
|
27
|
-
elsif restore_only && file.has_backup?
|
28
|
-
Dutiful::Config.storage.restore(file)
|
29
|
-
else
|
30
|
-
Dutiful::Config.storage.sync(file)
|
31
|
-
end
|
32
|
-
|
33
|
-
yield file, result if block_given?
|
31
|
+
(files + defaults).each do |file|
|
32
|
+
if file.has_backup?
|
33
|
+
yield file, file.restore if block_given?
|
34
34
|
else
|
35
35
|
yield file if block_given?
|
36
36
|
end
|
@@ -38,21 +38,17 @@ class Dutiful::Application
|
|
38
38
|
end
|
39
39
|
|
40
40
|
def exist?
|
41
|
-
files.any? &:exist?
|
41
|
+
files.any?(&:exist?) || defaults.any?(&:exist?)
|
42
42
|
end
|
43
43
|
|
44
44
|
def has_backup?
|
45
|
-
files.any? &:has_backup?
|
45
|
+
files.any?(&:has_backup?) || defaults.any?(&:has_backup?)
|
46
46
|
end
|
47
47
|
|
48
|
-
def
|
48
|
+
def tracked?
|
49
49
|
exist? || has_backup?
|
50
50
|
end
|
51
51
|
|
52
|
-
def synced?
|
53
|
-
files.all? &:synced?
|
54
|
-
end
|
55
|
-
|
56
52
|
def self.all
|
57
53
|
Dir["#{Dutiful.dir}/db/*.toml"].map do |filename|
|
58
54
|
Dutiful::Application.new filename
|
@@ -0,0 +1,83 @@
|
|
1
|
+
class Dutiful::ApplicationDefault
|
2
|
+
Result = Struct.new(:error, :success?)
|
3
|
+
|
4
|
+
def initialize(hash)
|
5
|
+
@description = hash[:description]
|
6
|
+
@domain = hash[:domain]
|
7
|
+
@key = hash[:key]
|
8
|
+
end
|
9
|
+
|
10
|
+
def backup
|
11
|
+
FileUtils.mkdir_p File.dirname "#{backup_path}"
|
12
|
+
File.open(backup_path, 'w') { |file| file << value }
|
13
|
+
|
14
|
+
Result.new nil, true
|
15
|
+
rescue => ex
|
16
|
+
Result.new ex.message, false
|
17
|
+
end
|
18
|
+
|
19
|
+
def backup_value
|
20
|
+
value = nil
|
21
|
+
File.open(backup_path) { |file| value = file.read }
|
22
|
+
|
23
|
+
value
|
24
|
+
end
|
25
|
+
|
26
|
+
def restore
|
27
|
+
`defaults write #{@domain} #{@key} #{backup_value}`
|
28
|
+
Result.new nil, true
|
29
|
+
rescue => ex
|
30
|
+
Result.new ex.message, false
|
31
|
+
end
|
32
|
+
|
33
|
+
def name
|
34
|
+
"#{@domain} #{@key}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def value
|
38
|
+
@value ||= `defaults read #{@domain} #{@key} 2>/dev/null`
|
39
|
+
end
|
40
|
+
|
41
|
+
def exist?
|
42
|
+
@exists ||= begin
|
43
|
+
@value = `defaults read #{@domain} #{@key} 2>/dev/null`
|
44
|
+
$?.success?
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def has_backup?
|
49
|
+
File.exist? backup_path
|
50
|
+
end
|
51
|
+
|
52
|
+
def tracked?
|
53
|
+
exist? || has_backup?
|
54
|
+
end
|
55
|
+
|
56
|
+
def in_sync?
|
57
|
+
value == backup_value
|
58
|
+
end
|
59
|
+
|
60
|
+
def to_s
|
61
|
+
if exist?
|
62
|
+
if has_backup?
|
63
|
+
if in_sync?
|
64
|
+
"#{name} ✔".green
|
65
|
+
else
|
66
|
+
"#{name} (modified)".yellow
|
67
|
+
end
|
68
|
+
else
|
69
|
+
"#{name} (pending backup)".yellow
|
70
|
+
end
|
71
|
+
elsif has_backup?
|
72
|
+
"#{name} (pending restore)".yellow
|
73
|
+
else
|
74
|
+
"#{name} does not exist (skipping)".light_black
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def backup_path
|
81
|
+
Dutiful::Config.storage.path "defaults/#{@domain}/#{@key}"
|
82
|
+
end
|
83
|
+
end
|
@@ -1,17 +1,16 @@
|
|
1
1
|
class Dutiful::ApplicationFile
|
2
|
-
attr_reader :
|
2
|
+
attr_reader :path
|
3
3
|
|
4
4
|
def initialize(path, condition)
|
5
|
-
@
|
6
|
-
|
7
|
-
@expected_status = condition[:expected_status] if condition
|
8
|
-
@path = path
|
5
|
+
@condition, @path = condition, path
|
6
|
+
end
|
9
7
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
8
|
+
def backup
|
9
|
+
Dutiful::Config.storage.backup self
|
10
|
+
end
|
11
|
+
|
12
|
+
def restore
|
13
|
+
Dutiful::Config.storage.restore self
|
15
14
|
end
|
16
15
|
|
17
16
|
def backup_path
|
@@ -22,46 +21,70 @@ class Dutiful::ApplicationFile
|
|
22
21
|
File.mtime backup_path if has_backup?
|
23
22
|
end
|
24
23
|
|
25
|
-
def
|
26
|
-
|
24
|
+
def command
|
25
|
+
@condition[:command] if has_condition?
|
27
26
|
end
|
28
27
|
|
29
|
-
def
|
30
|
-
if has_condition?
|
31
|
-
|
28
|
+
def expected_output
|
29
|
+
@condition[:expected_output] if has_condition?
|
30
|
+
end
|
32
31
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
32
|
+
def expected_status
|
33
|
+
@condition[:expected_status] if has_condition?
|
34
|
+
end
|
35
|
+
|
36
|
+
def full_path
|
37
|
+
if directory?
|
38
|
+
"#{File.expand_path "~/#{path}"}/"
|
38
39
|
else
|
39
|
-
|
40
|
+
File.expand_path "~/#{path}"
|
40
41
|
end
|
41
42
|
end
|
42
43
|
|
44
|
+
def name
|
45
|
+
path
|
46
|
+
end
|
47
|
+
|
48
|
+
def timestamp
|
49
|
+
File.mtime full_path if exist?
|
50
|
+
end
|
51
|
+
|
43
52
|
def directory?
|
44
53
|
path.chars.last == '/'
|
45
54
|
end
|
46
55
|
|
47
56
|
def exist?
|
48
|
-
File.exist?
|
57
|
+
File.exist?(full_path) && meets_conditions?
|
49
58
|
end
|
50
59
|
|
51
60
|
def has_backup?
|
52
|
-
Dutiful::Config.storage.exist?
|
61
|
+
Dutiful::Config.storage.exist?(self) && meets_conditions?
|
53
62
|
end
|
54
63
|
|
55
64
|
def has_condition?
|
56
|
-
@
|
65
|
+
@condition
|
66
|
+
end
|
67
|
+
|
68
|
+
def meets_conditions?
|
69
|
+
if has_condition?
|
70
|
+
output = `#{command}`
|
71
|
+
|
72
|
+
if expected_status
|
73
|
+
$?.exitstatus == expected_status
|
74
|
+
else
|
75
|
+
$?.success? && output.strip == expected_output
|
76
|
+
end
|
77
|
+
else
|
78
|
+
true
|
79
|
+
end
|
57
80
|
end
|
58
81
|
|
59
|
-
def
|
60
|
-
|
82
|
+
def tracked?
|
83
|
+
exist? || has_backup?
|
61
84
|
end
|
62
85
|
|
63
|
-
def
|
64
|
-
has_backup? && Dutiful::Config.storage.
|
86
|
+
def in_sync?
|
87
|
+
has_backup? && Dutiful::Config.storage.in_sync?(self)
|
65
88
|
end
|
66
89
|
|
67
90
|
def to_s
|
@@ -69,7 +92,7 @@ class Dutiful::ApplicationFile
|
|
69
92
|
|
70
93
|
if exist?
|
71
94
|
if has_backup?
|
72
|
-
if
|
95
|
+
if in_sync?
|
73
96
|
"#{path} ✔".green
|
74
97
|
else
|
75
98
|
"#{path} (modified)".yellow
|
@@ -80,7 +103,7 @@ class Dutiful::ApplicationFile
|
|
80
103
|
elsif has_backup?
|
81
104
|
"#{path} (pending restore)".yellow
|
82
105
|
else
|
83
|
-
"#{path} does not exist (skipping)".
|
106
|
+
"#{path} does not exist (skipping)".light_black
|
84
107
|
end
|
85
108
|
end
|
86
109
|
end
|
@@ -1,21 +1,23 @@
|
|
1
1
|
class Dutiful::Command::Backup < Clamp::Command
|
2
|
+
option ['-q', '--quiet'], :flag, 'Quiet mode'
|
2
3
|
option ['-v', '--verbose'], :flag, 'Verbose mode'
|
3
4
|
|
4
5
|
def execute
|
5
|
-
|
6
|
+
Dutiful::Logger.set quiet?, verbose?
|
7
|
+
Dutiful::Logger.info "Storage: #{Dutiful::Config.storage.name}\n\n"
|
6
8
|
|
7
9
|
Dutiful::Application.each do |application|
|
8
|
-
|
10
|
+
Dutiful::Logger.info "#{application.name}:\n" if application.exist? || verbose?
|
9
11
|
|
10
12
|
application.backup do |file, result|
|
11
13
|
if result
|
12
14
|
if result.success?
|
13
|
-
|
15
|
+
Dutiful::Logger.success " #{file.name} ✔"
|
14
16
|
else
|
15
|
-
|
17
|
+
Dutiful::Logger.error " #{file.name} ✖ - #{result.error}"
|
16
18
|
end
|
17
19
|
elsif verbose?
|
18
|
-
|
20
|
+
Dutiful::Logger.info " #{file}"
|
19
21
|
end
|
20
22
|
end
|
21
23
|
end
|
@@ -2,13 +2,18 @@ class Dutiful::Command::List < Clamp::Command
|
|
2
2
|
option ['-v', '--verbose'], :flag, 'Verbose mode'
|
3
3
|
|
4
4
|
def execute
|
5
|
-
|
5
|
+
Dutiful::Logger.set false, verbose?
|
6
|
+
Dutiful::Logger.info "Storage: #{Dutiful::Config.storage.name}\n\n"
|
6
7
|
|
7
8
|
Dutiful::Application.each do |application|
|
8
|
-
|
9
|
+
Dutiful::Logger.info "#{application.name}:\n" if application.tracked? || verbose?
|
9
10
|
|
10
11
|
application.files.map do |file|
|
11
|
-
|
12
|
+
Dutiful::Logger.info " #{file}" if file.tracked? || verbose?
|
13
|
+
end.compact.join("\n")
|
14
|
+
|
15
|
+
application.defaults.map do |default|
|
16
|
+
Dutiful::Logger.info " #{default}" if default.tracked? || verbose?
|
12
17
|
end.compact.join("\n")
|
13
18
|
end
|
14
19
|
end
|
@@ -1,11 +1,23 @@
|
|
1
1
|
class Dutiful::Command::Main < Clamp::Command
|
2
2
|
option ['-v', '--version'], :flag, 'Show version' do
|
3
|
-
|
3
|
+
Dutiful::Logger.info "dutiful #{Dutiful::VERSION}"
|
4
|
+
exit 0
|
5
|
+
end
|
6
|
+
|
7
|
+
option ['--init'], :flag, 'Initialize your computer with dutiful.' do
|
8
|
+
if File.exist? Dutiful::Config::PATH
|
9
|
+
Dutiful::Logger.warning "Configuration file already exist: '~/.dutiful/config.toml'."
|
10
|
+
exit 1
|
11
|
+
end
|
12
|
+
|
13
|
+
FileUtils.mkdir_p Dutiful.dir
|
14
|
+
FileUtils.cp File.expand_path("#{Dutiful.dir}/template/config.toml"), Dutiful::Config::PATH
|
15
|
+
|
16
|
+
Dutiful::Logger.success "Configuration file successfully created: '~/.dutiful/config.toml'."
|
4
17
|
exit 0
|
5
18
|
end
|
6
19
|
|
7
20
|
subcommand 'backup', 'Backup all preference files', Dutiful::Command::Backup
|
8
21
|
subcommand 'list', 'List all preference files', Dutiful::Command::List
|
9
22
|
subcommand 'restore', 'Restore all preference files', Dutiful::Command::Restore
|
10
|
-
subcommand 'sync', 'Sync all preference files', Dutiful::Command::Sync
|
11
23
|
end
|
@@ -1,21 +1,23 @@
|
|
1
1
|
class Dutiful::Command::Restore < Clamp::Command
|
2
|
+
option ['-q', '--quiet'], :flag, 'Quiet mode'
|
2
3
|
option ['-v', '--verbose'], :flag, 'Verbose mode'
|
3
4
|
|
4
5
|
def execute
|
5
|
-
|
6
|
+
Dutiful::Logger.set quiet?, verbose?
|
7
|
+
Dutiful::Logger.info "Storage: #{Dutiful::Config.storage.name}\n\n"
|
6
8
|
|
7
9
|
Dutiful::Application.each do |application|
|
8
|
-
|
10
|
+
Dutiful::Logger.info "#{application.name}:\n" if application.has_backup? || verbose?
|
9
11
|
|
10
12
|
application.restore do |file, result|
|
11
13
|
if result
|
12
14
|
if result.success?
|
13
|
-
|
15
|
+
Dutiful::Logger.success " #{file.name} ✔"
|
14
16
|
else
|
15
|
-
|
17
|
+
Dutiful::Logger.error " #{file.name} ✖ - #{result.error}"
|
16
18
|
end
|
17
19
|
elsif verbose?
|
18
|
-
|
20
|
+
Dutiful::Logger.info " #{file}"
|
19
21
|
end
|
20
22
|
end
|
21
23
|
end
|
data/lib/dutiful/commands.rb
CHANGED
data/lib/dutiful/config.rb
CHANGED
@@ -0,0 +1,22 @@
|
|
1
|
+
class Dutiful::Logger
|
2
|
+
def self.set(quiet, verbose)
|
3
|
+
@quiet = quiet
|
4
|
+
@verbose = verbose
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.info(message)
|
8
|
+
puts message unless @quiet
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.success(message)
|
12
|
+
puts message.green unless @quiet
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.warning(message)
|
16
|
+
puts message.yellow unless @quiet
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.error(message)
|
20
|
+
puts message.red unless @quiet
|
21
|
+
end
|
22
|
+
end
|
data/lib/dutiful/storage.rb
CHANGED
data/lib/dutiful/version.rb
CHANGED
data/lib/dutiful.rb
CHANGED
@@ -5,9 +5,11 @@ module Dutiful
|
|
5
5
|
end
|
6
6
|
|
7
7
|
require 'dutiful/application'
|
8
|
+
require 'dutiful/application_default'
|
8
9
|
require 'dutiful/application_file'
|
9
10
|
require 'dutiful/commands'
|
10
11
|
require 'dutiful/config'
|
12
|
+
require 'dutiful/logger'
|
11
13
|
require 'dutiful/storage'
|
12
14
|
require 'colorize'
|
13
15
|
require 'rsync'
|
@@ -0,0 +1,6 @@
|
|
1
|
+
[storage]
|
2
|
+
# If the storage name is either iCloud or Dropbox, the storage path will default to
|
3
|
+
# '~/Dropbox' and '~/Library/Mobile Documents' respectively.
|
4
|
+
|
5
|
+
name = 'iCloud' # The storage name is going to be printed when running the dutiful commands.
|
6
|
+
#path = '~/custom-folder' # The folder where your preference files are synced to.
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dutiful
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bruno Pinto
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-08-
|
11
|
+
date: 2015-08-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: clamp
|
@@ -80,6 +80,20 @@ dependencies:
|
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0.6'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rake
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '10'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '10'
|
83
97
|
- !ruby/object:Gem::Dependency
|
84
98
|
name: rspec
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -113,6 +127,7 @@ files:
|
|
113
127
|
- db/git.toml
|
114
128
|
- db/iterm2.toml
|
115
129
|
- db/oh-my-zsh.toml
|
130
|
+
- db/osx-screencapture.toml
|
116
131
|
- db/popcorn-time.toml
|
117
132
|
- db/rubymine.toml
|
118
133
|
- db/the_silver_searcher.toml
|
@@ -123,16 +138,18 @@ files:
|
|
123
138
|
- db/zsh.toml
|
124
139
|
- lib/dutiful.rb
|
125
140
|
- lib/dutiful/application.rb
|
141
|
+
- lib/dutiful/application_default.rb
|
126
142
|
- lib/dutiful/application_file.rb
|
127
143
|
- lib/dutiful/commands.rb
|
128
144
|
- lib/dutiful/commands/backup.rb
|
129
145
|
- lib/dutiful/commands/list.rb
|
130
146
|
- lib/dutiful/commands/main.rb
|
131
147
|
- lib/dutiful/commands/restore.rb
|
132
|
-
- lib/dutiful/commands/sync.rb
|
133
148
|
- lib/dutiful/config.rb
|
149
|
+
- lib/dutiful/logger.rb
|
134
150
|
- lib/dutiful/storage.rb
|
135
151
|
- lib/dutiful/version.rb
|
152
|
+
- template/config.toml
|
136
153
|
homepage: http://github.com/bpinto/dutiful
|
137
154
|
licenses:
|
138
155
|
- MIT
|
@@ -156,5 +173,5 @@ rubyforge_project:
|
|
156
173
|
rubygems_version: 2.4.5
|
157
174
|
signing_key:
|
158
175
|
specification_version: 4
|
159
|
-
summary: dutiful-0.0.
|
176
|
+
summary: dutiful-0.0.10
|
160
177
|
test_files: []
|
@@ -1,23 +0,0 @@
|
|
1
|
-
class Dutiful::Command::Sync < Clamp::Command
|
2
|
-
option ['-v', '--verbose'], :flag, 'Verbose mode'
|
3
|
-
|
4
|
-
def execute
|
5
|
-
puts "Storage: #{Dutiful::Config.storage.name}\n\n"
|
6
|
-
|
7
|
-
Dutiful::Application.each do |application|
|
8
|
-
puts "#{application.name}:\n" if application.should_sync? || verbose?
|
9
|
-
|
10
|
-
application.sync do |file, result|
|
11
|
-
if result
|
12
|
-
if result.success?
|
13
|
-
puts " #{file.path} ✔".green
|
14
|
-
else
|
15
|
-
puts " #{file.path} ✖ - #{result.error}".red
|
16
|
-
end
|
17
|
-
elsif verbose?
|
18
|
-
puts " #{file}"
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|