sundae 1.0.4 → 1.0.5

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.
Files changed (5) hide show
  1. data/README.rdoc +27 -4
  2. data/lib/sundae.rb +99 -89
  3. data/test/test_sundae.rb +10 -9
  4. data/version.txt +1 -1
  5. metadata +46 -38
data/README.rdoc CHANGED
@@ -67,7 +67,11 @@ this:
67
67
  | | |-- real_files_and_dirs
68
68
  | | ` ...
69
69
  | |-- mnt2/
70
- `-- collection2/
70
+ |-- collection2_really_a_mnt/
71
+ | |-- .sundae_path <--- empty
72
+ | |-- real_files_and_dirs
73
+ | | ` ...
74
+ `-- collection3/
71
75
  ` ...
72
76
 
73
77
  Sundae will act on all of the <em>mnt</em>s--subdirectories of the
@@ -75,6 +79,19 @@ Sundae will act on all of the <em>mnt</em>s--subdirectories of the
75
79
  <em>path</em>. The "collections" are only there to facilitate
76
80
  grouping common files and syncronizing them between computers.
77
81
 
82
+ This sub-subdirectory business is there so you can, for example, throw
83
+ all of your linux files under version control but have some of them
84
+ inserted into your home directory and some of them go somewhere else.
85
+
86
+ If you have one collection of files that you syncronize across
87
+ computers that all get deposited into the same path, you don't need a
88
+ collection of mnts. You can make can make the top level
89
+ directory--that would normally be treated as a collection--be treated
90
+ as a mnt by including a .sundae_path file in that folder (even if it's
91
+ empty). Like in the 'collection2_really_a_mnt' example above. It
92
+ seems complicated to have both options, but it's really not. I
93
+ promise.
94
+
78
95
  By default, all of the contents in each of the <em>mnt</em>s are
79
96
  placed in the user's home directory. This can be altered by creating
80
97
  a file called <tt>.sundae_path</tt> in the top of the <em>mnt</em>;
@@ -114,8 +131,11 @@ For example, the hierarchy in my <em>path</em>s looks sort of like this:
114
131
  | ` ...
115
132
  ` ...
116
133
  ~/mnt/sync/ <-- "path"
117
- |-- reference <-- "collection"
118
- | |-- home/ <-- "mnt"
134
+ |-- reference <-- "mnt"
135
+ | |-- .sundae_path (empty)
136
+ | |-- doc/
137
+ | ` ...
138
+ | |-- share/
119
139
  | ` ...
120
140
  |-- music
121
141
  | |-- home/
@@ -127,7 +147,7 @@ most of the time you can share a whole folder between computers,
127
147
  sometimes you want to mix your config files into a folder that also
128
148
  contains nonsymlinked files.
129
149
 
130
- For example, your ~/.ssh folder probably has a public and private key
150
+ For example, your "~/.ssh" folder probably has a public and private key
131
151
  that you want to stay unique to that machine, but you might want to
132
152
  mix in a "config" file that has host aliases that you share between
133
153
  machines. You can put the file in "~/mnt/nix/.ssh/config" in the
@@ -147,6 +167,9 @@ of files beneath "~/a/", then you don't want sundae to look through
147
167
  everything every time it runs. Just put it in a mnt directory with a
148
168
  <tt>.sundae_path</tt> of "~/a/b/c/d".
149
169
 
170
+ Again, if you want the first-level subdirectories of a "path" treated
171
+ as a mnt instead of a collection of mnts, just add a .sundae_path file.
172
+
150
173
  == Author
151
174
  <don@ohspite.net>
152
175
 
data/lib/sundae.rb CHANGED
@@ -115,8 +115,12 @@ module Sundae
115
115
  def self.install_location(mnt)
116
116
  mnt = Pathname.new(mnt).expand_path
117
117
  mnt_config = mnt + '.sundae_path'
118
+
118
119
  if mnt_config.exist?
119
- return Pathname.new(mnt_config.readlines[0].strip).expand_path
120
+ line = mnt_config.readlines[0]
121
+ if line then
122
+ return Pathname.new(line.strip).expand_path
123
+ end
120
124
  end
121
125
 
122
126
  base = mnt.basename.to_s
@@ -141,28 +145,27 @@ module Sundae
141
145
  def self.mnts_in_path(path)
142
146
  Pathname.new(path).expand_path
143
147
  mnts = []
144
- collections = path.children(false).delete_if {|c| c.to_s =~ /^\./}
145
-
146
- collections.each do |c|
147
- collection_mnts = (path + c).children(false).delete_if {|kid| kid.to_s =~ /^\./}
148
- collection_mnts.map! { |mnt| (c + mnt) }
148
+ collections = path.children.delete_if {|c| c.basename.to_s =~ /^\./}
149
149
 
150
- mnts |= collection_mnts # |= is the union
151
- end
152
-
153
- return mnts.sort.uniq
150
+ return collections.map do |c|
151
+ if c.children(false).include? Pathname.new('.sundae_path')
152
+ c
153
+ else
154
+ collection_mnts = c.children.delete_if {|kid| kid.basename.to_s =~ /^\./}
155
+ # collection_mnts.keep_if { |k| (path + c + k).directory? }
156
+ collection_mnts.reject! { |k| ! (path + c + k).directory? }
157
+ collection_mnts.map! { |mnt| (c + mnt) }
158
+ end
159
+ end.flatten.sort.uniq
154
160
  end
155
161
 
156
162
  # Return all mnts for every path as an array.
157
163
  #
158
164
  def self.all_mnts
159
- mnts = []
160
-
161
- @paths.each do |path|
165
+ @paths.map do |path|
162
166
  next unless path.exist?
163
- mnts |= mnts_in_path(path).map { |mnt| path + mnt } # |= is the union operator
164
- end
165
- return mnts
167
+ mnts_in_path(path)
168
+ end.compact.flatten
166
169
  end
167
170
 
168
171
  # Return all subdirectories and files in the mnts returned by
@@ -198,7 +201,7 @@ module Sundae
198
201
  files = location.entries.map { |f| location + f }
199
202
  files.each do |file|
200
203
  next unless file.symlink?
201
- next if file.readlink.exist?
204
+ next if file.exist?
202
205
  next unless root_path(file.readlink)
203
206
  file.delete
204
207
  end
@@ -217,22 +220,22 @@ module Sundae
217
220
  return nil
218
221
  end
219
222
 
220
- # Delete each generated directory if there aren't any real files in
221
- # them.
222
- #
223
- def self.remove_generated_directories
224
- generated_directories.each do |dir|
225
- # don't get rid of the linked config file
226
- next if dir.basename.to_s == '.sundae'
227
- remove_generated_stuff dir
228
-
229
- # if sf = find_static_file(dir)
230
- # puts "found static file: #{sf}"
231
- # else
232
- # dir.rmtree
233
- # end
234
- end
235
- end
223
+ # # Delete each generated directory if there aren't any real files in
224
+ # # them.
225
+ # #
226
+ # def self.remove_generated_directories
227
+ # generated_directories.each do |dir|
228
+ # # don't get rid of the linked config file
229
+ # next if dir.basename.to_s == '.sundae'
230
+ # remove_generated_stuff dir
231
+
232
+ # # if sf = find_static_file(dir)
233
+ # # puts "found static file: #{sf}"
234
+ # # else
235
+ # # dir.rmtree
236
+ # # end
237
+ # end
238
+ # end
236
239
 
237
240
  def self.remove_generated_files
238
241
  generated_files.each do |fod|
@@ -243,7 +246,7 @@ module Sundae
243
246
  end
244
247
 
245
248
  def self.remove_generated_stuff(fod)
246
- return unless fod.exist?
249
+ return unless fod.exist? || fod.symlink?
247
250
  if fod.ftype == 'directory'
248
251
  fod.each_child do |c|
249
252
  remove_generated_stuff c
@@ -264,29 +267,31 @@ module Sundae
264
267
  end
265
268
  end
266
269
 
267
- # For each directory and file in _target_, create a link at <em>link_name</em>. If
268
- # there is currently no file at <em>link_path</em>, create a symbolic link there.
270
+ # For each directory and file in _old_, create a link at <em>link_name</em>. If
271
+ # there is currently no file at <em>new</em>, create a symbolic link there.
269
272
  # If there is currently a symbolic link, combine the contents at the
270
- # link location and _target_ in a new directory and proceed
273
+ # link location and _old_ in a new directory and proceed
271
274
  # recursively.
272
275
  #
273
- def self.minimally_create_links(target, link_path)
274
- target = File.expand_path(target)
275
- link_path = File.expand_path(link_path)
276
+ def self.minimally_create_links(old, new)
277
+ old = Pathname.new(old)
278
+ new = Pathname.new(new)
276
279
 
277
- unless File.exist?(target)
278
- raise "attempt to create links from missing directory: " + target
280
+ unless old.exist?
281
+ raise "attempt to create links from missing directory: " + old
279
282
  end
280
283
 
281
- Find.find(target) do |path|
282
- next if path == target
284
+ old.realpath.find do |path|
285
+ next if path == old.realpath
283
286
  Find.prune if ignore_file?(File.basename(path))
284
287
 
285
- rel_path = path.gsub(target, '')
286
- link_name = File.join(link_path, rel_path)
287
- create_link(path, link_name)
288
+ rel_path = path.relative_path_from(old.realpath)
289
+ link_name = new + rel_path
290
+
291
+ # puts "#{link_name} -> #{old + rel_path}"
292
+ create_link(old + rel_path, link_name)
288
293
 
289
- Find.prune if File.directory?(path)
294
+ Find.prune if path.directory?
290
295
  end
291
296
  end
292
297
 
@@ -306,78 +311,83 @@ module Sundae
306
311
 
307
312
  # Dispatch calls to create_directory_link and create_file_link.
308
313
  #
309
- def self.create_link(target, link_name)
310
- if File.directory?(target)
314
+ def self.create_link(old, new)
315
+ old = Pathname.new(old)
316
+ new = Pathname.new(new)
317
+
318
+ if old.directory?
311
319
  begin
312
- create_directory_link(target, link_name)
320
+ create_directory_link(old, new)
313
321
  rescue => message
314
322
  puts message
315
323
  end
316
- elsif File.file?(target)
317
- create_file_link(target, link_name)
324
+ elsif old.file?
325
+ create_file_link(old, new)
318
326
  end
319
327
  end
320
328
 
321
- # Create a symbolic link to <em>target</em> from <em>link_name</em>.
329
+ # Create a symbolic link to <em>old</em> from <em>new</em>.
322
330
  #
323
- def self.create_file_link(target, link_name)
324
- raise ArgumentError, "#{target} does not exist" unless File.file?(target)
325
- if File.exist?(link_name)
326
- raise ArgumentError, "#{link_name} cannot be overwritten for #{target}." unless File.symlink?(link_name)
327
- if (not File.exist?(File.readlink(link_name)))
328
- FileUtils.ln_sf(target, link_name)
329
- else
330
- unless (File.expand_path(File.readlink(link_name)) == File.expand_path(target))
331
- raise ArgumentError, "#{link_name} points to #{File.readlink(link_name)}, not #{target}" unless File.symlink?(link_name)
332
- end
333
- end
331
+ def self.create_file_link(old, new)
332
+ old = Pathname.new(old)
333
+ new = Pathname.new(new)
334
+
335
+ raise ArgumentError, "#{old} does not exist" unless old.file? || old.symlink?
336
+ if new.symlink?
337
+ raise ArgumentError, "#{new.to_s} cannot be overwritten for #{old}: points to #{new.readlink.to_s}" unless new.readlink.to_s == old.to_s
334
338
  else
335
- FileUtils.ln_s(target, link_name)
339
+ raise ArgumentError, "#{new} cannot be overwritten for #{old}." if new.exist?
340
+ new.make_symlink(old)
336
341
  end
337
342
  end
338
343
 
339
- # Create a symbolic link to the directory at <em>target</em> from
340
- # <em>link_name</em>, unless <em>link_name</em> already exists. In that case,
344
+ # Create a symbolic link to the directory at <em>old</em> from
345
+ # <em>new</em>, unless <em>new</em> already exists. In that case,
341
346
  # create a directory and recursively run minimally_create_links.
342
347
  #
343
- def self.create_directory_link(target, link_name)
344
- raise ArgumentError unless File.directory?(target)
345
- if (not File.exist?(link_name)) ||
346
- (File.symlink?(link_name) && (not File.exist?(File.readlink(link_name))))
347
- FileUtils.ln_sf(target, link_name)
348
+ def self.create_directory_link(old, new)
349
+ old = Pathname.new(old)
350
+ new = Pathname.new(new)
351
+
352
+ raise ArgumentError unless old.directory?
353
+ if not new.exist? || new.symlink?
354
+ new.make_symlink(old)
348
355
  else
349
- case File.ftype(link_name)
356
+ case new.ftype
350
357
  when 'file'
351
- raise "Could not link #{link_name} to #{target}: target exists."
358
+ raise "Could not link #{new} to #{old}: old exists."
352
359
  when 'directory'
353
- minimally_create_links(target, link_name)
360
+ minimally_create_links(old, new)
354
361
  when 'link'
355
- case File.ftype(File.readlink(link_name))
362
+ case new.realpath.ftype
356
363
  when 'file'
357
- raise "Could not link #{link_name} to #{target}: another link exists there."
364
+ raise "Could not link #{new} to #{old}: another link exists there."
358
365
  when 'directory'
359
- combine_directories(link_name, target, File.readlink(link_name))
366
+ combine_directories(old, new.readlink, new)
360
367
  end
361
368
  end
362
369
  end
363
370
  end
364
371
 
365
372
  # Create a directory and create links in it pointing to
366
- # <em>target_path1</em> and <em>target_path2</em>.
373
+ # <em>old1</em> and <em>old2</em>.
367
374
  #
368
- def self.combine_directories(link_name, target_path1, target_path2)
369
- raise unless File.symlink?(link_name)
370
- return if target_path1 == target_path2
375
+ def self.combine_directories(old1, old2, new)
376
+ new = Pathname.new(new)
377
+ old1 = Pathname.new(old1).expand_path
378
+ old2 = Pathname.new(old2).expand_path
379
+
380
+ raise "combine_directories in #{new}" unless new.symlink?
381
+ return if old1 == old2
371
382
 
372
- FileUtils.rm(link_name)
373
- FileUtils.mkdir_p(link_name)
374
- minimally_create_links(target_path1, link_name)
375
- minimally_create_links(target_path2, link_name)
383
+ new.delete
384
+ new.mkpath
385
+ minimally_create_links(old1, new)
386
+ minimally_create_links(old2, new)
376
387
  end
377
388
 
378
389
  def self.update_filesystem
379
- remove_dead_links
380
- remove_generated_files
390
+ remove_filesystem
381
391
  create_filesystem
382
392
  end
383
393
 
data/test/test_sundae.rb CHANGED
@@ -58,7 +58,7 @@ class TestSundae < Test::Unit::TestCase
58
58
  d2 = @@mnts_dir + 'c1/d2'
59
59
  link = @@sandbox + 'link'
60
60
  FileUtils.ln_s(d1, link)
61
- Sundae.combine_directories(link, d1, d2)
61
+ Sundae.combine_directories(d1, d2, link)
62
62
  assert File.symlink?(link + 'f11')
63
63
  end
64
64
 
@@ -162,7 +162,8 @@ class TestSundae < Test::Unit::TestCase
162
162
  end
163
163
 
164
164
  def test_class_mnts_in_path
165
- assert_equal ['c1/d1', 'c1/d2', 'c2/d1', 'c2/d3'].map {|x| Pathname.new(x)},
165
+ md = Pathname.new(@@mnts_dir)
166
+ assert_equal ['c1/d1', 'c1/d2', 'c2/d1', 'c2/d3'].map {|x| (md + x).expand_path},
166
167
  Sundae.mnts_in_path(@@path1)
167
168
  end
168
169
 
@@ -175,13 +176,13 @@ class TestSundae < Test::Unit::TestCase
175
176
  assert (@@sandbox + 'perm_file').exist?
176
177
  end
177
178
 
178
- def test_class_remove_generated_directories
179
- Sundae.generated_directories.each { |d| FileUtils.mkdir_p(d) }
180
- Sundae.remove_generated_directories
181
- Sundae.generated_directories.each do |d|
182
- assert ! File.exist?(d)
183
- end
184
- end
179
+ # def test_class_remove_generated_directories
180
+ # Sundae.generated_directories.each { |d| FileUtils.mkdir_p(d) }
181
+ # Sundae.remove_generated_directories
182
+ # Sundae.generated_directories.each do |d|
183
+ # assert ! File.exist?(d)
184
+ # end
185
+ # end
185
186
 
186
187
  def test_class_root_path
187
188
  assert_equal @@mnts_dir +'c1', Sundae.root_path(@@mnts_dir + 'c1')
data/version.txt CHANGED
@@ -1 +1 @@
1
- 1.0.4
1
+ 1.0.5
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sundae
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.4
4
+ version: 1.0.5
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,33 +9,43 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-04-13 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: highline
16
- requirement: &82655870 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
20
20
  - !ruby/object:Gem::Version
21
- version: 1.6.11
21
+ version: 1.6.12
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *82655870
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 1.6.12
25
30
  - !ruby/object:Gem::Dependency
26
31
  name: configatron
27
- requirement: &82654320 !ruby/object:Gem::Requirement
32
+ requirement: !ruby/object:Gem::Requirement
28
33
  none: false
29
34
  requirements:
30
35
  - - ! '>='
31
36
  - !ruby/object:Gem::Version
32
- version: 2.9.0
37
+ version: 2.9.1
33
38
  type: :runtime
34
39
  prerelease: false
35
- version_requirements: *82654320
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 2.9.1
36
46
  - !ruby/object:Gem::Dependency
37
47
  name: rdoc
38
- requirement: &82653700 !ruby/object:Gem::Requirement
48
+ requirement: !ruby/object:Gem::Requirement
39
49
  none: false
40
50
  requirements:
41
51
  - - ! '>='
@@ -43,48 +53,46 @@ dependencies:
43
53
  version: '3.12'
44
54
  type: :runtime
45
55
  prerelease: false
46
- version_requirements: *82653700
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '3.12'
47
62
  - !ruby/object:Gem::Dependency
48
63
  name: bones
49
- requirement: &82653190 !ruby/object:Gem::Requirement
64
+ requirement: !ruby/object:Gem::Requirement
50
65
  none: false
51
66
  requirements:
52
67
  - - ! '>='
53
68
  - !ruby/object:Gem::Version
54
- version: 3.7.3
69
+ version: 3.8.0
55
70
  type: :development
56
71
  prerelease: false
57
- version_requirements: *82653190
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: 3.8.0
58
78
  description: Mix collections of files while maintaining complete separation.
59
79
  email: don@ohspite.net
60
80
  executables:
61
- - !binary |-
62
- c3VuZGFl
81
+ - sundae
63
82
  extensions: []
64
83
  extra_rdoc_files:
65
- - !binary |-
66
- UkVBRE1FLnJkb2M=
67
- - !binary |-
68
- YmluL3N1bmRhZQ==
84
+ - README.rdoc
85
+ - bin/sundae
69
86
  files:
70
- - !binary |-
71
- LmdpdGlnbm9yZQ==
72
- - !binary |-
73
- Q0hBTkdFTE9H
74
- - !binary |-
75
- Q09QWUlORw==
76
- - !binary |-
77
- UkVBRE1FLnJkb2M=
78
- - !binary |-
79
- UmFrZWZpbGU=
80
- - !binary |-
81
- YmluL3N1bmRhZQ==
82
- - !binary |-
83
- bGliL3N1bmRhZS5yYg==
84
- - !binary |-
85
- dGVzdC90ZXN0X3N1bmRhZS5yYg==
86
- - !binary |-
87
- dmVyc2lvbi50eHQ=
87
+ - .gitignore
88
+ - CHANGELOG
89
+ - COPYING
90
+ - README.rdoc
91
+ - Rakefile
92
+ - bin/sundae
93
+ - lib/sundae.rb
94
+ - test/test_sundae.rb
95
+ - version.txt
88
96
  homepage: https://github.com/ohspite/sundae
89
97
  licenses: []
90
98
  post_install_message:
@@ -107,7 +115,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
107
115
  version: '0'
108
116
  requirements: []
109
117
  rubyforge_project: sundae
110
- rubygems_version: 1.8.15
118
+ rubygems_version: 1.8.24
111
119
  signing_key:
112
120
  specification_version: 3
113
121
  summary: Mix collections of files while maintaining complete separation.