sundae 1.0.4 → 1.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +27 -4
- data/lib/sundae.rb +99 -89
- data/test/test_sundae.rb +10 -9
- data/version.txt +1 -1
- metadata +46 -38
data/README.rdoc
CHANGED
@@ -67,7 +67,11 @@ this:
|
|
67
67
|
| | |-- real_files_and_dirs
|
68
68
|
| | ` ...
|
69
69
|
| |-- mnt2/
|
70
|
-
|
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 <-- "
|
118
|
-
| |--
|
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
|
-
|
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
|
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
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
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
|
-
|
160
|
-
|
161
|
-
@paths.each do |path|
|
165
|
+
@paths.map do |path|
|
162
166
|
next unless path.exist?
|
163
|
-
|
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.
|
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
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
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
|
268
|
-
# there is currently no file at <em>
|
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
|
273
|
+
# link location and _old_ in a new directory and proceed
|
271
274
|
# recursively.
|
272
275
|
#
|
273
|
-
def self.minimally_create_links(
|
274
|
-
|
275
|
-
|
276
|
+
def self.minimally_create_links(old, new)
|
277
|
+
old = Pathname.new(old)
|
278
|
+
new = Pathname.new(new)
|
276
279
|
|
277
|
-
unless
|
278
|
-
raise "attempt to create links from missing directory: " +
|
280
|
+
unless old.exist?
|
281
|
+
raise "attempt to create links from missing directory: " + old
|
279
282
|
end
|
280
283
|
|
281
|
-
|
282
|
-
next if path ==
|
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.
|
286
|
-
link_name =
|
287
|
-
|
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
|
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(
|
310
|
-
|
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(
|
320
|
+
create_directory_link(old, new)
|
313
321
|
rescue => message
|
314
322
|
puts message
|
315
323
|
end
|
316
|
-
elsif
|
317
|
-
create_file_link(
|
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>
|
329
|
+
# Create a symbolic link to <em>old</em> from <em>new</em>.
|
322
330
|
#
|
323
|
-
def self.create_file_link(
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
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
|
-
|
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>
|
340
|
-
# <em>
|
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(
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
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
|
356
|
+
case new.ftype
|
350
357
|
when 'file'
|
351
|
-
raise "Could not link #{
|
358
|
+
raise "Could not link #{new} to #{old}: old exists."
|
352
359
|
when 'directory'
|
353
|
-
minimally_create_links(
|
360
|
+
minimally_create_links(old, new)
|
354
361
|
when 'link'
|
355
|
-
case
|
362
|
+
case new.realpath.ftype
|
356
363
|
when 'file'
|
357
|
-
raise "Could not link #{
|
364
|
+
raise "Could not link #{new} to #{old}: another link exists there."
|
358
365
|
when 'directory'
|
359
|
-
combine_directories(
|
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>
|
373
|
+
# <em>old1</em> and <em>old2</em>.
|
367
374
|
#
|
368
|
-
def self.combine_directories(
|
369
|
-
|
370
|
-
|
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
|
-
|
373
|
-
|
374
|
-
minimally_create_links(
|
375
|
-
minimally_create_links(
|
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
|
-
|
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(
|
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
|
-
|
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
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
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.
|
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
|
+
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-
|
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:
|
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.
|
21
|
+
version: 1.6.12
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
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:
|
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.
|
37
|
+
version: 2.9.1
|
33
38
|
type: :runtime
|
34
39
|
prerelease: false
|
35
|
-
version_requirements:
|
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:
|
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:
|
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:
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
50
65
|
none: false
|
51
66
|
requirements:
|
52
67
|
- - ! '>='
|
53
68
|
- !ruby/object:Gem::Version
|
54
|
-
version: 3.
|
69
|
+
version: 3.8.0
|
55
70
|
type: :development
|
56
71
|
prerelease: false
|
57
|
-
version_requirements:
|
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
|
-
-
|
62
|
-
c3VuZGFl
|
81
|
+
- sundae
|
63
82
|
extensions: []
|
64
83
|
extra_rdoc_files:
|
65
|
-
-
|
66
|
-
|
67
|
-
- !binary |-
|
68
|
-
YmluL3N1bmRhZQ==
|
84
|
+
- README.rdoc
|
85
|
+
- bin/sundae
|
69
86
|
files:
|
70
|
-
-
|
71
|
-
|
72
|
-
-
|
73
|
-
|
74
|
-
-
|
75
|
-
|
76
|
-
-
|
77
|
-
|
78
|
-
-
|
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.
|
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.
|