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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c6accd15285afd3686ae7f9734291ed27ffd6409
4
- data.tar.gz: 180766e86e1aa148189557e706168bc16c237d4a
3
+ metadata.gz: 77661dc7c89c798e383b4866921f2d12d10e4c63
4
+ data.tar.gz: 4e7a2cc9951499ab8761dbd8547a56bd1e1f50d6
5
5
  SHA512:
6
- metadata.gz: defc3556e4b1652c792fb7a9e8a0da99d085d6c9e291e1857fe7bbc03c9902dd31243ff20636a188ca637ba0e288406574b648375dfad59e3b83d73b0ecb3217
7
- data.tar.gz: e5b1708c80fe35343b227b75141c3ef7a6627f4b54afdfe9bbf5868b293a99cddae5bfda251b843ae610f6b9f688df0daa591869498837faecb6c82325364900
6
+ metadata.gz: e3febcb417b4a94930ddad87562e7d013481848eaa2ae6f83e9a822a39a76bcdf905f7e722ab8d5fbfa80212066b1608dd6d36f6ef5e4ba6e4eae8db5601df49
7
+ data.tar.gz: 60f80377d514e640f756b942901a6f8ccd2d8fbc869d9fa5f0e69347c18f6922fce176ce9eab9a2e1279d84a4b14ccb598e2e3aa91197f9436e33cdbbfe060c5
@@ -0,0 +1,7 @@
1
+ [application]
2
+ name = 'OSX Screencapture'
3
+
4
+ [[default]]
5
+ description = 'Change where screenshots are saved to'
6
+ domain = 'com.apple.screencapture'
7
+ key = 'location'
@@ -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
- sync backup_only: true, &block
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
- sync restore_only: true, &block
20
- end
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 should_sync?
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 :full_path, :path
2
+ attr_reader :path
3
3
 
4
4
  def initialize(path, condition)
5
- @command = condition[:command] if condition
6
- @expected_output = condition[:expected_output] if condition
7
- @expected_status = condition[:expected_status] if condition
8
- @path = path
5
+ @condition, @path = condition, path
6
+ end
9
7
 
10
- if directory?
11
- @full_path = "#{File.expand_path "~/#{path}"}/"
12
- else
13
- @full_path = File.expand_path "~/#{path}"
14
- end
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 timestamp
26
- File.mtime full_path if exist?
24
+ def command
25
+ @condition[:command] if has_condition?
27
26
  end
28
27
 
29
- def meets_conditions?
30
- if has_condition?
31
- output = `#{@command}`
28
+ def expected_output
29
+ @condition[:expected_output] if has_condition?
30
+ end
32
31
 
33
- if @expected_status
34
- $?.exitstatus == @expected_status
35
- else
36
- $?.success? && output.strip == @expected_output
37
- end
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
- true
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? full_path
57
+ File.exist?(full_path) && meets_conditions?
49
58
  end
50
59
 
51
60
  def has_backup?
52
- Dutiful::Config.storage.exist? self
61
+ Dutiful::Config.storage.exist?(self) && meets_conditions?
53
62
  end
54
63
 
55
64
  def has_condition?
56
- @command
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 should_sync?
60
- (exist? || has_backup?) && meets_conditions?
82
+ def tracked?
83
+ exist? || has_backup?
61
84
  end
62
85
 
63
- def synced?
64
- has_backup? && Dutiful::Config.storage.synced?(self)
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 synced?
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)".yellow
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
- puts "Storage: #{Dutiful::Config.storage.name}\n\n"
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
- puts "#{application.name}:\n" if application.should_sync? && application.exist? || verbose?
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
- puts " #{file.path} ✔".green
15
+ Dutiful::Logger.success " #{file.name} ✔"
14
16
  else
15
- puts " #{file.path} ✖ - #{result.error}".red
17
+ Dutiful::Logger.error " #{file.name} ✖ - #{result.error}"
16
18
  end
17
19
  elsif verbose?
18
- puts " #{file}"
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
- puts "Storage: #{Dutiful::Config.storage.name}\n\n"
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
- puts "#{application.name}:\n" if application.should_sync? || verbose?
9
+ Dutiful::Logger.info "#{application.name}:\n" if application.tracked? || verbose?
9
10
 
10
11
  application.files.map do |file|
11
- puts " #{file}" if (file.should_sync?) || verbose?
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
- puts "dutiful #{Dutiful::VERSION}"
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
- puts "Storage: #{Dutiful::Config.storage.name}\n\n"
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
- puts "#{application.name}:\n" if application.should_sync? && application.has_backup? || verbose?
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
- puts " #{file.path} ✔".green
15
+ Dutiful::Logger.success " #{file.name} ✔"
14
16
  else
15
- puts " #{file.path} ✖ - #{result.error}".red
17
+ Dutiful::Logger.error " #{file.name} ✖ - #{result.error}"
16
18
  end
17
19
  elsif verbose?
18
- puts " #{file}"
20
+ Dutiful::Logger.info " #{file}"
19
21
  end
20
22
  end
21
23
  end
@@ -4,5 +4,4 @@ require 'clamp'
4
4
  require 'dutiful/commands/backup'
5
5
  require 'dutiful/commands/list'
6
6
  require 'dutiful/commands/restore'
7
- require 'dutiful/commands/sync'
8
7
  require 'dutiful/commands/main'
@@ -12,6 +12,10 @@ class Dutiful::Config
12
12
  end
13
13
  end
14
14
 
15
+ def self.osx?
16
+ `uname -s`.strip == 'Darwin'
17
+ end
18
+
15
19
  private
16
20
 
17
21
  def self.content
@@ -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
@@ -56,7 +56,7 @@ class Dutiful::Storage
56
56
  end
57
57
  end
58
58
 
59
- def synced?(file)
59
+ def in_sync?(file)
60
60
  if file.directory?
61
61
  Dir.glob("#{file.full_path}*").all? do |file_path|
62
62
  filename = File.basename(file_path)
@@ -1,3 +1,3 @@
1
1
  module Dutiful
2
- VERSION = '0.0.9'
2
+ VERSION = '0.0.10'
3
3
  end
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.9
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-02 00:00:00.000000000 Z
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.9
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