homesick 0.8.1 → 0.9.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.
@@ -1,4 +1,5 @@
1
1
  language: ruby
2
2
  rvm:
3
+ - 2.0.0
3
4
  - 1.9.3
4
5
  - 1.8.7
@@ -1,3 +1,6 @@
1
+ # 0.9.0
2
+ * Introduce .homesick_subdir #39
3
+
1
4
  # 0.8.1
2
5
  *Fixed `homesick list` bug on ruby 2.0 #37
3
6
 
@@ -51,6 +51,84 @@ Not sure what else homesick has up its sleeve? There's always the built in help:
51
51
 
52
52
  homesick help
53
53
 
54
+ ## .homesick_subdir
55
+
56
+ `homesick symlink` basically makes symlink to only first depth in `castle/home`. If you want to link nested files/directories, please use .homesick_subdir.
57
+
58
+ For example, when you have castle like this:
59
+
60
+ castle/home
61
+ `-- .config
62
+ `-- fooapp
63
+ |-- config1
64
+ |-- config2
65
+ `-- config3
66
+
67
+ and have home like this:
68
+
69
+ $ tree -a
70
+ ~
71
+ |-- .config
72
+ | `-- barapp
73
+ | |-- config1
74
+ | |-- config2
75
+ | `-- config3
76
+ `-- .emacs.d
77
+ |-- elisp
78
+ `-- inits
79
+
80
+ You may want to symlink only to `castle/home/.config/fooapp` instead of `castle/home/.config` because you already have `~/.config/barapp`. In this case, you can use .homesick_subdir. Please write "directories you want to look up sub direcoties (instead of just first depth)" in this file.
81
+
82
+ castle/home/.homesick_subdir
83
+
84
+ .config
85
+
86
+ and run `homesick symlink CASTLE`. The result is:
87
+
88
+ ~
89
+ |-- .config
90
+ | |-- barapp
91
+ | | |-- config1
92
+ | | |-- config2
93
+ | | `-- config3
94
+ | `-- fooapp -> castle/home/.config/fooapp
95
+ `-- .emacs.d
96
+ |-- elisp
97
+ `-- inits
98
+
99
+ Or `homesick track NESTED_FILE CASTLE` adds a line automatically. For example:
100
+
101
+ homesick track .emacs.d/elisp castle
102
+
103
+ castle/home/.homesick_subdir
104
+
105
+ .config
106
+ .emacs.d
107
+
108
+ home directory
109
+
110
+ ~
111
+ |-- .config
112
+ | |-- barapp
113
+ | | |-- config1
114
+ | | |-- config2
115
+ | | `-- config3
116
+ | `-- fooapp -> castle/home/.config/fooapp
117
+ `-- .emacs.d
118
+ |-- elisp -> castle/home/.emacs.d/elisp
119
+ `-- inits
120
+
121
+ and castle
122
+
123
+ castle/home
124
+ |-- .config
125
+ | `-- fooapp
126
+ | |-- config1
127
+ | |-- config2
128
+ | `-- config3
129
+ `-- .emacs.d
130
+ `-- elisp
131
+
54
132
  ## Note on Patches/Pull Requests
55
133
 
56
134
  * Fork the project.
data/Rakefile CHANGED
@@ -22,7 +22,7 @@ Jeweler::Tasks.new do |gem|
22
22
  gem.email = ["josh@technicalpickles.com", "info@muratayusuke.com"]
23
23
  gem.homepage = "http://github.com/technicalpickles/homesick"
24
24
  gem.authors = ["Joshua Nichols", "Yusuke Murata"]
25
- gem.version = "0.8.1"
25
+ gem.version = "0.9.0"
26
26
  gem.license = "MIT"
27
27
  # Have dependencies? Add them to Gemfile
28
28
 
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "homesick"
8
- s.version = "0.8.1"
8
+ s.version = "0.9.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Joshua Nichols", "Yusuke Murata"]
12
- s.date = "2013-05-19"
12
+ s.date = "2013-06-06"
13
13
  s.description = "\n A man's home (directory) is his castle, so don't leave home with out it.\n\n Homesick is sorta like rip, but for dotfiles. It uses git to clone a repository containing dotfiles, and saves them in ~/.homesick. It then allows you to symlink all the dotfiles into place with a single command. \n\n "
14
14
  s.email = ["josh@technicalpickles.com", "info@muratayusuke.com"]
15
15
  s.executables = ["homesick"]
@@ -10,6 +10,7 @@ class Homesick < Thor
10
10
  add_runtime_options!
11
11
 
12
12
  GITHUB_NAME_REPO_PATTERN = /\A([A-Za-z_-]+\/[A-Za-z_-]+)\Z/
13
+ SUBDIR_FILENAME = ".homesick_subdir"
13
14
 
14
15
  def initialize(args=[], options={}, config={})
15
16
  super
@@ -96,15 +97,14 @@ class Homesick < Thor
96
97
  check_castle_existance(name, "symlink")
97
98
 
98
99
  inside castle_dir(name) do
99
- files = Pathname.glob('{.*,*}').reject{|a| [".",".."].include?(a.to_s)}
100
- files.each do |path|
101
- absolute_path = path.expand_path
100
+ subdirs = subdirs(name)
102
101
 
103
- inside home_dir do
104
- adjusted_path = (home_dir + path).basename
102
+ # link files
103
+ symlink_each(name, castle_dir(name), subdirs)
105
104
 
106
- ln_s absolute_path, adjusted_path
107
- end
105
+ # link files in subdirs
106
+ subdirs.each do |subdir|
107
+ symlink_each(name, subdir, subdirs)
108
108
  end
109
109
  end
110
110
  end
@@ -112,22 +112,46 @@ class Homesick < Thor
112
112
  desc "track FILE CASTLE", "add a file to a castle"
113
113
  def track(file, castle)
114
114
  castle = Pathname.new(castle)
115
- file = Pathname.new(file)
115
+ file = Pathname.new(file.chomp('/'))
116
116
  check_castle_existance(castle, 'track')
117
117
 
118
118
  absolute_path = file.expand_path
119
- castle_path = castle_dir(castle)
120
- mv absolute_path, castle_path
119
+ relative_dir = absolute_path.relative_path_from(home_dir).dirname
120
+ castle_path = Pathname.new(castle_dir(castle)).join(relative_dir)
121
+ FileUtils.mkdir_p castle_path
122
+
123
+ # Are we already tracking this or anything inside it?
124
+ target = Pathname.new(castle_path.join(file.basename))
125
+ if target.exist?
126
+ if absolute_path.directory?
127
+ move_dir_contents(target, absolute_path)
128
+ absolute_path.rmtree
129
+ subdir_remove(castle, relative_dir + file.basename)
130
+
131
+ elsif more_recent? absolute_path, target
132
+ target.delete
133
+ mv absolute_path, castle_path
134
+ else
135
+ shell.say_status(:track, "#{target} already exists, and is more recent than #{file}. Run 'homesick SYMLINK CASTLE' to create symlinks.", :blue) unless options[:quiet]
136
+ end
137
+ else
138
+ mv absolute_path, castle_path
139
+ end
121
140
 
122
141
  inside home_dir do
123
- absolute_path = castle_dir(castle) + file.basename
124
- home_path = home_dir + file
142
+ absolute_path = castle_path + file.basename
143
+ home_path = home_dir + relative_dir + file.basename
125
144
  ln_s absolute_path, home_path
126
145
  end
127
146
 
128
147
  inside castle_path do
129
148
  git_add absolute_path
130
149
  end
150
+
151
+ # are we tracking something nested? Add the parent dir to the manifest
152
+ unless relative_dir.eql?(Pathname.new('.'))
153
+ subdir_add(castle, relative_dir)
154
+ end
131
155
  end
132
156
 
133
157
  desc "list", "List cloned castles"
@@ -219,4 +243,99 @@ class Homesick < Thor
219
243
  git_push
220
244
  end
221
245
  end
246
+
247
+ def subdir_file(castle)
248
+ repos_dir.join(castle, SUBDIR_FILENAME)
249
+ end
250
+
251
+ def subdirs(castle)
252
+ subdir_filepath = subdir_file(castle)
253
+ subdirs = []
254
+ if subdir_filepath.exist?
255
+ subdir_filepath.readlines.each do |subdir|
256
+ subdirs.push(subdir.chomp)
257
+ end
258
+ end
259
+ subdirs
260
+ end
261
+
262
+ def subdir_add(castle, path)
263
+ subdir_filepath = subdir_file(castle)
264
+ File.open(subdir_filepath, 'a+') do |subdir|
265
+ subdir.puts path unless subdir.readlines.inject(false) { |memo, line| line.eql?("#{path.to_s}\n") || memo }
266
+ end
267
+
268
+ inside castle_dir(castle) do
269
+ git_add subdir_filepath
270
+ end
271
+ end
272
+
273
+ def subdir_remove(castle, path)
274
+ subdir_filepath = subdir_file(castle)
275
+ if subdir_filepath.exist?
276
+ lines = IO.readlines(subdir_filepath).delete_if { |line| line == "#{path}\n" }
277
+ File.open(subdir_filepath, 'w') { |manfile| manfile.puts lines }
278
+ end
279
+
280
+ inside castle_dir(castle) do
281
+ git_add subdir_filepath
282
+ end
283
+ end
284
+
285
+ def move_dir_contents(target, dir_path)
286
+ child_files = dir_path.children
287
+ child_files.each do |child|
288
+
289
+ target_path = target.join(child.basename)
290
+ if target_path.exist?
291
+ if more_recent?(child, target_path) && target.file?
292
+ target_path.delete
293
+ mv child, target
294
+ end
295
+ next
296
+ end
297
+
298
+ mv child, target
299
+ end
300
+ end
301
+
302
+ def more_recent?(first, second)
303
+ first_p = Pathname.new(first)
304
+ second_p = Pathname.new(second)
305
+ first_p.mtime > second_p.mtime && !first_p.symlink?
306
+ end
307
+
308
+ def symlink_each(castle, basedir, subdirs)
309
+ absolute_basedir = Pathname.new(basedir).expand_path
310
+ inside basedir do
311
+ files = Pathname.glob('{.*,*}').reject{|a| [".", ".."].include?(a.to_s)}
312
+ files.each do |path|
313
+ absolute_path = path.expand_path
314
+ castle_home = castle_dir(castle)
315
+
316
+ # make ignore dirs
317
+ ignore_dirs = []
318
+ subdirs.each do |subdir|
319
+ # ignore all parent of each line in subdir file
320
+ Pathname.new(subdir).ascend do |p|
321
+ ignore_dirs.push(p)
322
+ end
323
+ end
324
+
325
+ # ignore dirs written in subdir file
326
+ matched = false
327
+ ignore_dirs.uniq.each do |ignore_dir|
328
+ if absolute_path == castle_home.join(ignore_dir)
329
+ matched = true
330
+ break
331
+ end
332
+ end
333
+ next if matched
334
+
335
+ relative_dir = absolute_basedir.relative_path_from(castle_home)
336
+ home_path = home_dir.join(relative_dir).join(path)
337
+ ln_s absolute_path, home_path
338
+ end
339
+ end
340
+ end
222
341
  end
@@ -93,6 +93,7 @@ class Homesick
93
93
  def ln_s(source, destination, config = {})
94
94
  source = Pathname.new(source)
95
95
  destination = Pathname.new(destination)
96
+ FileUtils.mkdir_p destination.dirname
96
97
 
97
98
  if destination.symlink?
98
99
  if destination.readlink == source
@@ -120,12 +120,59 @@ describe "homesick" do
120
120
  existing_dotdir_link.readlink.should == dotdir
121
121
  end
122
122
  end
123
+
124
+ context "with '.config' in .homesick_subdir" do
125
+ let(:castle) { given_castle("glencairn", [".config"]) }
126
+ it "can symlink in sub directory" do
127
+ dotdir = castle.directory(".config")
128
+ dotfile = dotdir.file(".some_dotfile")
129
+
130
+ homesick.symlink("glencairn")
131
+
132
+ home_dotdir = home.join(".config")
133
+ home_dotdir.symlink?.should == false
134
+ home_dotdir.join(".some_dotfile").readlink.should == dotfile
135
+ end
136
+ end
137
+
138
+ context "with '.config/appA' in .homesick_subdir" do
139
+ let(:castle) { given_castle("glencairn", [".config/appA"]) }
140
+ it "can symlink in nested sub directory" do
141
+ dotdir = castle.directory(".config").directory("appA")
142
+ dotfile = dotdir.file(".some_dotfile")
143
+
144
+ homesick.symlink("glencairn")
145
+
146
+ home_dotdir = home.join(".config").join("appA")
147
+ home_dotdir.symlink?.should == false
148
+ home_dotdir.join(".some_dotfile").readlink.should == dotfile
149
+ end
150
+ end
151
+
152
+ context "with '.config' and '.config/appA' in .homesick_subdir" do
153
+ let(:castle) { given_castle("glencairn", [".config", ".config/appA"]) }
154
+ it "can symlink under both of .config and .config/appA" do
155
+ config_dir = castle.directory(".config")
156
+ config_dotfile = config_dir.file(".some_dotfile")
157
+ appA_dir = config_dir.directory("appA")
158
+ appA_dotfile = appA_dir.file(".some_appfile")
159
+
160
+ homesick.symlink("glencairn")
161
+
162
+ home_config_dir = home.join(".config")
163
+ home_appA_dir = home_config_dir.join("appA")
164
+ home_config_dir.symlink?.should == false
165
+ home_config_dir.join(".some_dotfile").readlink.should == config_dotfile
166
+ home_appA_dir.symlink?.should == false
167
+ home_appA_dir.join(".some_appfile").readlink.should == appA_dotfile
168
+ end
169
+ end
123
170
  end
124
171
 
125
172
  describe "list" do
126
173
  it "should say each castle in the castle directory" do
127
174
  given_castle('zomg')
128
- given_castle('zomg', 'wtf/zomg')
175
+ given_castle('wtf/zomg')
129
176
 
130
177
  homesick.should_receive(:say_status).with("zomg", "git://github.com/technicalpickles/zomg.git", :cyan)
131
178
  homesick.should_receive(:say_status).with("wtf/zomg", "git://github.com/technicalpickles/zomg.git", :cyan)
@@ -169,5 +216,70 @@ describe "homesick" do
169
216
 
170
217
  some_rc_file.readlink.should == tracked_file
171
218
  end
219
+
220
+ it 'should track a file in nested folder structure' do
221
+ castle = given_castle('castle_repo')
222
+
223
+ some_nested_file = home.file('some/nested/file.txt')
224
+ homesick.track(some_nested_file.to_s, 'castle_repo')
225
+
226
+ tracked_file = castle.join('some/nested/file.txt')
227
+ tracked_file.should exist
228
+ some_nested_file.readlink.should == tracked_file
229
+ end
230
+
231
+ it 'should track a nested directory' do
232
+ castle = given_castle('castle_repo')
233
+
234
+ some_nested_dir = home.directory('some/nested/directory/')
235
+ homesick.track(some_nested_dir.to_s, 'castle_repo')
236
+
237
+ tracked_file = castle.join('some/nested/directory/')
238
+ tracked_file.should exist
239
+ some_nested_dir.realpath.should == tracked_file.realpath
240
+ end
241
+
242
+ describe "subdir_file" do
243
+
244
+ it 'should add the nested files parent to the subdir_file' do
245
+ castle = given_castle('castle_repo')
246
+
247
+ some_nested_file = home.file('some/nested/file.txt')
248
+ homesick.track(some_nested_file.to_s, 'castle_repo')
249
+
250
+ subdir_file = castle.parent.join(Homesick::SUBDIR_FILENAME)
251
+ File.open(subdir_file, 'r') do |f|
252
+ f.readline.should == "some/nested\n"
253
+ end
254
+ end
255
+
256
+ it 'should NOT add anything if the files parent is already listed' do
257
+ castle = given_castle('castle_repo')
258
+
259
+ some_nested_file = home.file('some/nested/file.txt')
260
+ other_nested_file = home.file('some/nested/other.txt')
261
+ homesick.track(some_nested_file.to_s, 'castle_repo')
262
+ homesick.track(other_nested_file.to_s, 'castle_repo')
263
+
264
+ subdir_file = castle.parent.join(Homesick::SUBDIR_FILENAME)
265
+ File.open(subdir_file, 'r') do |f|
266
+ f.readlines.size.should == 1
267
+ end
268
+ end
269
+
270
+ it 'should remove the parent of a tracked file from the subdir_file if the parent itself is tracked' do
271
+ castle = given_castle('castle_repo')
272
+
273
+ some_nested_file = home.file('some/nested/file.txt')
274
+ nested_parent = home.directory('some/nested/')
275
+ homesick.track(some_nested_file.to_s, 'castle_repo')
276
+ homesick.track(nested_parent.to_s, 'castle_repo')
277
+
278
+ subdir_file = castle.parent.join(Homesick::SUBDIR_FILENAME)
279
+ File.open(subdir_file, 'r') do |f|
280
+ f.each_line { |line| line.should_not == "some/nested\n" }
281
+ end
282
+ end
283
+ end
172
284
  end
173
285
  end
@@ -16,11 +16,18 @@ RSpec.configure do |config|
16
16
  homesick.stub(:say_status)
17
17
  end
18
18
 
19
- def given_castle(name, path=name)
19
+ def given_castle(path, subdirs=[])
20
+ name = Pathname.new(path).basename
20
21
  castles.directory(path) do |castle|
21
22
  Dir.chdir(castle) do
22
23
  system "git init >/dev/null 2>&1"
23
24
  system "git remote add origin git://github.com/technicalpickles/#{name}.git >/dev/null 2>&1"
25
+ if subdirs then
26
+ subdir_file = castle.join(Homesick::SUBDIR_FILENAME)
27
+ subdirs.each do |subdir|
28
+ system "echo #{subdir} >> #{subdir_file}"
29
+ end
30
+ end
24
31
  return castle.directory("home")
25
32
  end
26
33
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: homesick
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.1
4
+ version: 0.9.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2013-05-19 00:00:00.000000000 Z
13
+ date: 2013-06-06 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: thor
@@ -171,7 +171,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
171
171
  version: '0'
172
172
  segments:
173
173
  - 0
174
- hash: -3689253364441722964
174
+ hash: -1508111784299001289
175
175
  required_rubygems_version: !ruby/object:Gem::Requirement
176
176
  none: false
177
177
  requirements: