dotrepo 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3731baa7ddeb1d596e711460693b68a99861b8f0
4
+ data.tar.gz: 220caf4af84fbbd39993a8374e3c77f8fcb71fd9
5
+ SHA512:
6
+ metadata.gz: 245a836d8fefc3ec02827f67375d5ceff5f44387f2fc2dde11af556405a8b74a376445d7fb61a91417ffccdddfc0fe6def3537664d9a5237b2519052be0b2937
7
+ data.tar.gz: fe26393479992bb2016698801968dcb10791e4a9689aaf08415b7b29871558c37d1649ccad85b8e7725a0b1a76b4020c100feaeaf56bed5b65710770ee6abd92
data/bin/dotrepo ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/dotrepo/cli'
4
+ Dotrepo::CLI.start
@@ -0,0 +1,60 @@
1
+ require 'thor'
2
+ require 'dotrepo'
3
+
4
+ module Dotrepo
5
+ class CLI < Thor
6
+
7
+ desc "version", "display version"
8
+ def version
9
+ say "Dotrepo::VERSION #{Dotrepo::VERSION}"
10
+ end
11
+
12
+ desc "info", "display current configuration"
13
+ def info
14
+ config = Dotrepo::ConfigFile.new
15
+ [:source, :destination].each do |attr|
16
+ shell.say_status "#{attr}:", config.send(attr), :bold
17
+ end
18
+ end
19
+
20
+ desc "setup", "setup your dotbox"
21
+ method_option :repo,
22
+ aliases: "-r",
23
+ desc: "repo to pull dotfiles from"
24
+ def setup
25
+ unless options[:repo]
26
+ shell.say_status "error", "you must specify a repo", :red
27
+ return
28
+ end
29
+
30
+ system "git clone #{options[:repo]} #{config.source}"
31
+ DotFileManager.new( config.source, config.destination ).symlink_dotfiles
32
+ end
33
+
34
+ desc "refresh", "update linked dotfiles"
35
+ def refresh
36
+ # runs the manager again to symlink new files & prune abandoned files
37
+ DotFileManager.new( config.source, config.destination ).symlink_dotfiles
38
+ end
39
+
40
+ dec "uninstall", "revert symlinked files to plain dotfiles"
41
+ def uninstall
42
+ end
43
+
44
+ desc "doctor", "analyze your setup for common issues"
45
+ def doctor
46
+ # do some smart checking based on info
47
+ # - does the source exist as a git repo
48
+ # - is the source up to date & free of modified files
49
+ # - does the source have any dotfiles
50
+ # - does the destination exist
51
+ end
52
+
53
+ private
54
+
55
+ def config
56
+ @_config ||= Dotrepo::ConfigFile.new
57
+ end
58
+
59
+ end
60
+ end
@@ -0,0 +1,37 @@
1
+ module Dotrepo
2
+ class ConfigFile
3
+ FILE_PATH = File.join( File.expand_path("~"), ".dotbox/config" )
4
+
5
+ attr_reader :data
6
+
7
+ def initialize
8
+ if File.exists? FILE_PATH
9
+ @data = defaults.merge YAML.load_file FILE_PATH
10
+ else
11
+ @data = defaults
12
+ end
13
+ end
14
+
15
+ def source
16
+ fetch_from_data "source"
17
+ end
18
+
19
+ def destination
20
+ fetch_from_data "destination"
21
+ end
22
+
23
+ private
24
+
25
+ def fetch_from_data key
26
+ data[key]
27
+ end
28
+
29
+ def defaults
30
+ {
31
+ "source" => '~/.dotbox/box',
32
+ "destination" => '~/'
33
+ }
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,47 @@
1
+ module Dotrepo
2
+ class DotFile
3
+ attr_reader :path, :source, :destination
4
+
5
+ def initialize path, manager
6
+ @path = path
7
+ @source = File.join manager.source, path
8
+ @destination = File.join manager.destination, path
9
+ end
10
+
11
+ def source_exists?
12
+ File.exist? source
13
+ end
14
+
15
+ def destination_exists?
16
+ File.exist? destination
17
+ end
18
+
19
+ def backup_destination
20
+ File.rename( destination, "#{destination}.bak" )
21
+ end
22
+
23
+ def linked?
24
+ destination_exists? &&
25
+ File.symlink?(destination) &&
26
+ File.readlink(destination) == source
27
+ end
28
+
29
+ def symlink_to_destination
30
+ return if linked?
31
+
32
+ if destination_exists?
33
+ print "#{destination} exists. backup existing file and link? (y|n) "
34
+ input = $stdin.gets.chomp
35
+ return unless input.downcase == "y"
36
+
37
+ backup_destination
38
+ end
39
+ symlink_to_destination!
40
+ end
41
+
42
+ def symlink_to_destination!
43
+ File.symlink source, destination
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,25 @@
1
+ module Dotrepo
2
+ class DotFileManager
3
+ attr_reader :source, :destination
4
+
5
+ def initialize source, destination
6
+ @source = File.expand_path( source )
7
+ @destination = File.expand_path( destination )
8
+ end
9
+
10
+ def dotfiles
11
+ Dir.glob( File.join( source, '**/.*' ) )
12
+ .map { |f| f.sub( /#{source}\/?/, '' ) }
13
+ .reject { |f| f.match /.git/ }
14
+ .map { |path| DotFile.new( path, self ) }
15
+ end
16
+
17
+ def symlink_dotfiles
18
+ dotfiles.each do |df|
19
+ df.symlink_to_destination
20
+ end
21
+ end
22
+
23
+ end
24
+
25
+ end
@@ -0,0 +1,3 @@
1
+ module Dotrepo
2
+ VERSION = "0.0.1"
3
+ end
data/lib/dotrepo.rb ADDED
@@ -0,0 +1,9 @@
1
+ require 'yaml'
2
+
3
+ require_relative 'dotrepo/dot_file'
4
+ require_relative 'dotrepo/dot_file_manager'
5
+ require_relative 'dotrepo/config_file'
6
+
7
+ unless Dotrepo.const_get(:VERSION)
8
+ require_relative 'dotrepo/version'
9
+ end
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+
3
+ describe Dotrepo::ConfigFile do
4
+
5
+ before :each do
6
+ allow( File ).to receive(:exists?)
7
+ .with( Dotrepo::ConfigFile::FILE_PATH )
8
+ .and_return( false )
9
+ end
10
+
11
+ describe "#initialize" do
12
+ it "uses data from config file if present" do
13
+ allow( File ).to receive(:exists?)
14
+ .with( Dotrepo::ConfigFile::FILE_PATH )
15
+ .and_return( true )
16
+
17
+ allow( YAML ).to receive(:load_file)
18
+ .with( Dotrepo::ConfigFile::FILE_PATH )
19
+ .and_return( { "source" => "source",
20
+ "destination" => "destination" })
21
+
22
+ expect( described_class.new.data ).to eq({ "source" => "source",
23
+ "destination" => "destination" })
24
+ end
25
+
26
+ it "uses default data w/ no config file" do
27
+ expect( described_class.new.data ).to eq({ "source" => "~/.dotbox/box",
28
+ "destination" => "~/" })
29
+ end
30
+ end
31
+
32
+ describe "#source" do
33
+ it "returns the 'source' from #data" do
34
+ expect( described_class.new.source ).to eq "~/.dotbox/box"
35
+ end
36
+ end
37
+
38
+ describe "#destination" do
39
+ it "returns the 'destination' from #data" do
40
+ expect( described_class.new.destination ).to eq "~/"
41
+ end
42
+ end
43
+
44
+ end
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+
3
+ describe Dotrepo::DotFileManager do
4
+
5
+ after :each do
6
+ Given.cleanup!
7
+ end
8
+
9
+ describe "#initialize" do
10
+ it "sets given expanded source & destination" do
11
+ subject = Dotrepo::DotFileManager.new "source", "destination"
12
+
13
+ expect( subject.source ).to eq File.expand_path("source")
14
+ expect( subject.destination ).to eq File.expand_path("destination")
15
+ end
16
+ end
17
+
18
+ describe "#dotfiles" do
19
+ it "returns DotFiles" do
20
+ Given.file '.bashrc'
21
+ subject = Dotrepo::DotFileManager.new Given::TMP, "destination"
22
+
23
+ expect( subject.dotfiles.first ).to be_a Dotrepo::DotFile
24
+ end
25
+
26
+ it "returns an array of only dotfiles from source" do
27
+ Given.file '.bashrc'
28
+ Given.file 'foo_bar'
29
+ Given.file 'dir/.dot'
30
+ Given.file 'dir/file'
31
+ subject = Dotrepo::DotFileManager.new Given::TMP, "destination"
32
+
33
+ expect( subject.dotfiles.map { |df| df.path } ).to match_array [".bashrc","dir/.dot"]
34
+ end
35
+
36
+ it "ignores git related files" do
37
+ Given.file '.git/config'
38
+ Given.file '.gitignore'
39
+ subject = Dotrepo::DotFileManager.new Given::TMP, "destination"
40
+
41
+ expect( subject.dotfiles.map { |df| df.path } ).to be_empty
42
+ end
43
+ end
44
+
45
+ describe "#symlink_dotfiles" do
46
+ it "calls symlink_to_destination on each dotfile" do
47
+ dotfile_one = instance_double("Dotrepo::DotFile")
48
+ dotfile_two = instance_double("Dotrepo::DotFile")
49
+
50
+ subject = Dotrepo::DotFileManager.new "source", "destination"
51
+ allow( subject ).to receive(:dotfiles)
52
+ .and_return [dotfile_one, dotfile_two]
53
+
54
+ expect( dotfile_one ).to receive(:symlink_to_destination)
55
+ expect( dotfile_two ).to receive(:symlink_to_destination)
56
+
57
+ subject.symlink_dotfiles
58
+ end
59
+ end
60
+
61
+ end
@@ -0,0 +1,248 @@
1
+ require 'spec_helper'
2
+
3
+ describe Dotrepo::DotFile do
4
+
5
+ describe "#initialize" do
6
+
7
+ let(:manager_double) {
8
+ instance_double("Dotrepo::DotFileManager",
9
+ source: "/source",
10
+ destination: "/destination" )
11
+ }
12
+ let(:subject) { Dotrepo::DotFile.new("/foo/bar", manager_double) }
13
+
14
+ it "sets the given path" do
15
+ expect( subject.path ).to eq "/foo/bar"
16
+ end
17
+
18
+ it "sets up source & destination" do
19
+ expect( subject.source ).to eq "/source/foo/bar"
20
+ expect( subject.destination ).to eq "/destination/foo/bar"
21
+ end
22
+
23
+ end
24
+
25
+ describe "#source_exists?" do
26
+
27
+ let(:manager_double) {
28
+ instance_double("Dotrepo::DotFileManager",
29
+ source: "/source",
30
+ destination: "/destination" )
31
+ }
32
+ let(:subject) { Dotrepo::DotFile.new("/foo/bar", manager_double) }
33
+
34
+ it "is true if source exists" do
35
+ allow( File ).to receive(:exist?).with("/source/foo/bar")
36
+ .and_return(true)
37
+
38
+ expect( subject.source_exists? ).to be_truthy
39
+ end
40
+ it "is false if source doesn't exist" do
41
+ allow( File ).to receive(:exist?).with("/source/foo/bar")
42
+ .and_return(false)
43
+
44
+ expect( subject.source_exists? ).to be_falsy
45
+ end
46
+ end
47
+
48
+ describe "#destination_exists?" do
49
+
50
+ let(:manager_double) {
51
+ double("Manager",
52
+ source: "/source",
53
+ destination: "/destination" )
54
+ }
55
+ let(:subject) { Dotrepo::DotFile.new("/foo/bar", manager_double) }
56
+
57
+ it "is true if destination exists" do
58
+ allow( File ).to receive(:exist?).with("/destination/foo/bar")
59
+ .and_return(true)
60
+
61
+ expect( subject.destination_exists? ).to be_truthy
62
+ end
63
+ it "is false if destination doesn't exist" do
64
+ allow( File ).to receive(:exist?).with("/destination/foo/bar")
65
+ .and_return(false)
66
+
67
+ expect( subject.destination_exists? ).to be_falsy
68
+ end
69
+ end
70
+
71
+ describe "#backup_destination" do
72
+ let(:manager_double) {
73
+ double("Manager",
74
+ source: "/source",
75
+ destination: "/destination" )
76
+ }
77
+ let(:subject) { Dotrepo::DotFile.new("/foo/bar", manager_double) }
78
+
79
+ it "moves destination file to backup name" do
80
+ expect( File ).to receive(:rename)
81
+ .with("/destination/foo/bar", "/destination/foo/bar.bak")
82
+
83
+ subject.backup_destination
84
+ end
85
+ end
86
+
87
+ describe "linked?" do
88
+ let(:manager_double) {
89
+ double("Manager",
90
+ source: "/source",
91
+ destination: "/destination" )
92
+ }
93
+ let(:subject) { Dotrepo::DotFile.new("/foo/bar", manager_double) }
94
+
95
+
96
+ it "is false if destination doesn't exist" do
97
+ allow( File ).to receive(:exist?)
98
+ .with("/destination/foo/bar")
99
+ .and_return(false)
100
+
101
+ expect( subject.linked? ).to be_falsy
102
+ end
103
+
104
+ it "is true if destination is linked to source" do
105
+ allow( File ).to receive(:exist?)
106
+ .with("/destination/foo/bar")
107
+ .and_return(true)
108
+ allow( File ).to receive(:symlink?)
109
+ .with("/destination/foo/bar")
110
+ .and_return(true)
111
+ allow( File ).to receive(:readlink)
112
+ .with("/destination/foo/bar")
113
+ .and_return("/source/foo/bar")
114
+
115
+ expect( subject.linked? ).to be_truthy
116
+ end
117
+
118
+ it "is false if destination is a file" do
119
+ allow( File ).to receive(:exist?)
120
+ .with("/destination/foo/bar")
121
+ .and_return(true)
122
+ allow( File ).to receive(:symlink?)
123
+ .with("/destination/foo/bar")
124
+ .and_return(false)
125
+
126
+ expect( subject.linked? ).to be_falsy
127
+ end
128
+
129
+ it "is false if destination is linked not to source" do
130
+ allow( File ).to receive(:exist?)
131
+ .with("/destination/foo/bar")
132
+ .and_return(true)
133
+ allow( File ).to receive(:symlink?)
134
+ .with("/destination/foo/bar")
135
+ .and_return(true)
136
+ allow( File ).to receive(:readlink)
137
+ .with("/destination/foo/bar")
138
+ .and_return("/not/source/path")
139
+
140
+ expect( subject.linked? ).to be_falsy
141
+ end
142
+ end
143
+
144
+ describe "#symlink_to_destination!" do
145
+
146
+ let(:manager_double) {
147
+ double("Manager",
148
+ source: "/source",
149
+ destination: "/destination" )
150
+ }
151
+ let(:subject) { Dotrepo::DotFile.new("/foo/bar", manager_double) }
152
+
153
+ it "symlinks source to destination" do
154
+ expect( File ).to receive(:symlink)
155
+ .with("/source/foo/bar","/destination/foo/bar")
156
+
157
+ subject.symlink_to_destination!
158
+ end
159
+ end
160
+
161
+ describe "#symlink_to_destination" do
162
+
163
+ let(:manager_double) {
164
+ double("Manager",
165
+ source: "/source",
166
+ destination: "/destination" )
167
+ }
168
+ let(:subject) {
169
+ described_class.new "foo/bar", manager_double
170
+ }
171
+
172
+ context "when already linked" do
173
+ it "skips linking" do
174
+ subject = described_class.new ".path", manager_double
175
+ allow( subject ).to receive(:linked?)
176
+ .and_return(true)
177
+
178
+ expect( subject ).not_to receive(:symlink_to_destination!)
179
+ subject.symlink_to_destination
180
+ end
181
+ end
182
+
183
+ context "when destination exists as file" do
184
+
185
+ before :each do
186
+ allow( File ).to receive(:exist?).and_call_original
187
+ allow( File ).to receive(:exist?)
188
+ .with("/destination/foo/bar")
189
+ .and_return(true)
190
+ allow( subject ).to receive(:symlink_to_destination!) # just to kill it from trying to actually symlink anything
191
+ end
192
+
193
+ it "asks to backup or skip" do
194
+ out = CatchAndRelease::Catch.stdout do
195
+ CatchAndRelease::Release.stdin 'n' do
196
+ subject.symlink_to_destination
197
+ end
198
+ end
199
+
200
+ expect( out ).to match /^#{"/destination/foo/bar exists. backup existing file and link?"}/
201
+ end
202
+
203
+ context "user says skip" do
204
+ it "skips linking" do
205
+ expect( subject ).not_to receive(:symlink_to_destination!)
206
+
207
+ CatchAndRelease::Release.stdin 'n' do
208
+ subject.symlink_to_destination
209
+ end
210
+ end
211
+ end
212
+
213
+ context "user says backup" do
214
+ it "backs up destination file" do
215
+ expect( subject ).to receive(:backup_destination)
216
+
217
+ CatchAndRelease::Release.stdin 'y' do
218
+ subject.symlink_to_destination
219
+ end
220
+ end
221
+ it "symlinks to destination" do
222
+ allow( subject ).to receive(:backup_destination)
223
+ expect( subject ).to receive(:symlink_to_destination!)
224
+
225
+ CatchAndRelease::Release.stdin 'y' do
226
+ subject.symlink_to_destination
227
+ end
228
+ end
229
+ end
230
+ end
231
+
232
+ context "when destination doesn't exist" do
233
+
234
+ before :each do
235
+ allow( File ).to receive(:exist?)
236
+ .with("/destination/foo/bar")
237
+ .and_return(false)
238
+ end
239
+
240
+ it "symlinks file to destination" do
241
+ expect( subject ).to receive(:symlink_to_destination!)
242
+ subject.symlink_to_destination
243
+ end
244
+
245
+ end
246
+ end
247
+
248
+ end
@@ -0,0 +1,23 @@
1
+ require 'rspec'
2
+ require 'catch_and_release'
3
+ require 'support/given'
4
+
5
+ require 'pry'
6
+
7
+ RSpec.configure do |c|
8
+ c.mock_with :rspec do |mocks|
9
+ mocks.verify_doubled_constant_names = true
10
+ mocks.verify_partial_doubles = true
11
+ end
12
+ end
13
+
14
+ # for eating up stdout & stderr
15
+ unless ENV['VERBOSE']
16
+ stdout = StringIO.open('','w+')
17
+ $stdout = stdout
18
+
19
+ stderr = StringIO.open('','w+')
20
+ $stderr = stderr
21
+ end
22
+
23
+ require 'dotrepo'
@@ -0,0 +1,28 @@
1
+ module Given
2
+ ROOT = Dir.pwd
3
+ TMP = File.join( Dir.pwd, 'tmp' )
4
+
5
+ class << self
6
+
7
+ def fixture name
8
+ cleanup!
9
+
10
+ `rsync -av ./spec/fixtures/#{name}/ #{TMP}/`
11
+ Dir.chdir TMP
12
+ end
13
+
14
+ def file name, content=''
15
+ file_path = File.join( TMP, name )
16
+ FileUtils.mkdir_p( File.dirname(file_path) )
17
+ File.open( file_path, 'w' ) do |file|
18
+ file.write content
19
+ end
20
+ end
21
+
22
+ def cleanup!
23
+ Dir.chdir ROOT
24
+ `rm -rf #{TMP}`
25
+ end
26
+
27
+ end
28
+ end
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dotrepo
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Steven Sloan
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-07-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thor
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: ' A simple manager for your dotfiles '
28
+ email:
29
+ - stevenosloan@gmail.com
30
+ executables:
31
+ - dotrepo
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - bin/dotrepo
36
+ - lib/dotrepo.rb
37
+ - lib/dotrepo/cli.rb
38
+ - lib/dotrepo/config_file.rb
39
+ - lib/dotrepo/dot_file.rb
40
+ - lib/dotrepo/dot_file_manager.rb
41
+ - lib/dotrepo/version.rb
42
+ - spec/lib/dotrepo/config_file_spec.rb
43
+ - spec/lib/dotrepo/dot_file_manager_spec.rb
44
+ - spec/lib/dotrepo/dot_file_spec.rb
45
+ - spec/spec_helper.rb
46
+ - spec/support/given.rb
47
+ homepage: http://github.com/stevenosloan/dotrepo
48
+ licenses:
49
+ - MIT
50
+ metadata: {}
51
+ post_install_message:
52
+ rdoc_options: []
53
+ require_paths:
54
+ - lib
55
+ required_ruby_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - '>='
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - '>='
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ requirements: []
66
+ rubyforge_project:
67
+ rubygems_version: 2.2.2
68
+ signing_key:
69
+ specification_version: 4
70
+ summary: A simple manager for your dotfiles
71
+ test_files:
72
+ - spec/lib/dotrepo/config_file_spec.rb
73
+ - spec/lib/dotrepo/dot_file_manager_spec.rb
74
+ - spec/lib/dotrepo/dot_file_spec.rb
75
+ - spec/spec_helper.rb
76
+ - spec/support/given.rb