unmac 0.6

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.
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2009 Xavier Noria
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
data/README ADDED
@@ -0,0 +1,71 @@
1
+ unmac -- Delete spurious Mac files under a directory or volume
2
+
3
+ Usage:
4
+ unmac [OPTIONS] directory ...
5
+
6
+ Options:
7
+ -h, --help Print this help
8
+ -v, --verbose Show processing
9
+ -p, --pretend Show what would be deleted, but don't do it
10
+ -s, --keep-spotlight Do not delete Spotlight data
11
+ -f, --keep-fsevents Do not delete Time Machine stuff
12
+ -t, --keep-trashes Do not delete volume trashes
13
+ -m, --keep-macosx Do not delete "__MACOSX" directories
14
+ -r, --keep-dsstore Do not delete ".DS_Store" files
15
+ -d, --keep-apple-double Do not delete "._*" ghost files
16
+ -o, --keep-apple-double-orphans Delete "._foo.txt" only if "foo.txt" exists
17
+ -i, --keep-custom-folder-icons Do not delete custom folder icons
18
+
19
+ Description:
20
+ When a Mac copies files to volumes that have a different file system it adds
21
+ some auxiliary files that represent different kinds of metadata. This typically
22
+ happens with memory sticks, network drives, SD cards, etc. Mac archivers like
23
+ zip(1) add special files as well, like "__MACOSX", you'll see them for example
24
+ if you extract a Zip file like that on Windows.
25
+
26
+ The purpose of these special files is to let another Mac rebuild the metadata
27
+ from them, Spotlight integration, etc. But if you are not interested in these
28
+ features your directory or volume just gets cluttered. This utility removes
29
+ all those extra files.
30
+
31
+ What's deleted:
32
+ This is just a summary, for more detailed explanations and pointers see the
33
+ comments in unmacer.rb:
34
+
35
+ * Spotlight leaves a folder called ".Spotlight-V100" in the root directory
36
+ of volumes.
37
+
38
+ * Time Machine relies on a folder called ".fseventsd" in the root directory
39
+ of volumes.
40
+
41
+ * A folder called ".Trashes" in the root directory of volumes stores their
42
+ trashes. Those ones are taken into account by the Trash in the Dock.
43
+
44
+ * Some archivers create auxiliary directories called "__MACOSX".
45
+
46
+ * Finder and Spotlight data related to each folder gets stored in an
47
+ extra file called ".DS_Store".
48
+
49
+ * For each file "foo.txt", its resource fork and some additional stuff
50
+ are stored in an accompaining ghost file called "._foo.txt". The pattern
51
+ is to prepend "._" to the original file name.
52
+
53
+ * If a folder has a custom icon you get a file "Icon^M" (that ^M is a hard CR).
54
+
55
+ Some stuff is only found in the root folder of volumes, unmac looks for them
56
+ in any directory you pass as argument, but not on their subdirectories.
57
+
58
+ Installation:
59
+ unmac is distributed as a Ruby gem. Once you have Ruby and RubyGems installed
60
+ execute
61
+
62
+ gem sources -a http://gems.github.com # you need this only once
63
+ gem install fxn-unmac
64
+
65
+ This program is portable.
66
+
67
+ License:
68
+ See the MIT-LICENSE file included in the distribution.
69
+
70
+ Copyright:
71
+ Copyright (C) 2009 Xavier Noria.
@@ -0,0 +1,7 @@
1
+ task :default => [:test]
2
+
3
+ task :test do
4
+ Dir['test/test_*.rb'].each do |test|
5
+ ruby "-Ilib #{test}"
6
+ end
7
+ end
@@ -0,0 +1,107 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'getoptlong'
4
+ require 'rubygems'
5
+ require 'unmacer'
6
+
7
+ def usage
8
+ puts <<USAGE
9
+ unmac -- Delete spurious Mac files under a directory or volume
10
+
11
+ Usage:
12
+ unmac [OPTIONS] directory ...
13
+
14
+ Options:
15
+ -h, --help Print this help
16
+ -v, --verbose Show processing
17
+ -p, --pretend Show what would be deleted, but don't do it
18
+ -s, --keep-spotlight Do not delete Spotlight data
19
+ -f, --keep-fsevents Do not delete Time Machine stuff
20
+ -t, --keep-trashes Do not delete volume trashes
21
+ -m, --keep-macosx Do not delete "__MACOSX" directories
22
+ -r, --keep-dsstore Do not delete ".DS_Store" files
23
+ -d, --keep-apple-double Do not delete "._*" ghost files
24
+ -o, --keep-apple-double-orphans Delete "._foo.txt" only if "foo.txt" exists
25
+ -i, --keep-custom-folder-icons Do not delete custom folder icons
26
+
27
+ Description:
28
+ When a Mac copies files to volumes that have a different file system it adds
29
+ some auxiliary files that represent different kinds of metadata. This typically
30
+ happens with memory sticks, network drives, SD cards, etc. Mac archivers like
31
+ zip(1) add special files as well, like "#{Unmacer::MACOSX}", you'll see them
32
+ for example if you extract a Zip file like that on Windows.
33
+
34
+ The purpose of these special files is to let another Mac rebuild the metadata
35
+ from them, Spotlight integration, etc. But if you are not interested in these
36
+ features your directory or volume just gets cluttered. This utility removes
37
+ all those extra files.
38
+
39
+ What's deleted:
40
+ This is just a summary, for more detailed explanations and pointers see the
41
+ comments in unmacer.rb:
42
+
43
+ * Spotlight leaves a folder called "#{Unmacer::SPOTLIGHT}" in the root directory
44
+ of volumes.
45
+
46
+ * Time Machine relies on a folder called "#{Unmacer::FSEVENTS}" in the root directory
47
+ of volumes.
48
+
49
+ * A folder called "#{Unmacer::TRASHES}" in the root directory of volumes stores their
50
+ trashes. Those ones are taken into account by the Trash in the Dock.
51
+
52
+ * Some archivers create auxiliary directories called "#{Unmacer::MACOSX}".
53
+
54
+ * Finder and Spotlight data related to each folder gets stored in an
55
+ extra file called "#{Unmacer::DSSTORE}".
56
+
57
+ * For each file "foo.txt", its resource fork and some additional stuff
58
+ are stored in an accompaining ghost file called "._foo.txt". The pattern
59
+ is to prepend "._" to the original file name.
60
+
61
+ * If a folder has a custom icon you get a file "Icon^M" (that ^M is a hard CR).
62
+
63
+ Some stuff is only found in the root folder of volumes, unmac looks for them
64
+ in any directory you pass as argument, but not on their subdirectories.
65
+ USAGE
66
+ end
67
+
68
+ opts = GetoptLong.new(
69
+ ['--help', '-h', GetoptLong::NO_ARGUMENT],
70
+ ['--verbose', '-v', GetoptLong::NO_ARGUMENT],
71
+ ['--pretend', '-p', GetoptLong::NO_ARGUMENT],
72
+ ['--keep-spotlight', '-s', GetoptLong::NO_ARGUMENT],
73
+ ['--keep-fsevents', '-f', GetoptLong::NO_ARGUMENT],
74
+ ['--keep-trashes', '-t', GetoptLong::NO_ARGUMENT],
75
+ ['--keep-macosx', '-m', GetoptLong::NO_ARGUMENT],
76
+ ['--keep-dsstore', '-r', GetoptLong::NO_ARGUMENT],
77
+ ['--keep-apple-double', '-d', GetoptLong::NO_ARGUMENT],
78
+ ['--keep-apple-double-orphans', '-o', GetoptLong::NO_ARGUMENT],
79
+ ['--keep-custom-folder-icons', '-i', GetoptLong::NO_ARGUMENT],
80
+ ['--test', GetoptLong::NO_ARGUMENT]
81
+ )
82
+
83
+ test = false
84
+ begin
85
+ unmacer = Unmacer.new
86
+ opts.each do |opt, val|
87
+ case opt
88
+ when '--help'
89
+ usage and exit
90
+ when '--verbose'
91
+ unmacer.verbose = true
92
+ when '--pretend'
93
+ unmacer.pretend = true
94
+ when /\A--(keep-.*)/
95
+ flag = $1.gsub('-', '_')
96
+ unmacer.send("#{flag}=", true)
97
+ when '--test'
98
+ test = true
99
+ end
100
+ end
101
+ unmacer.unmac!(ARGV) unless test
102
+ rescue GetoptLong::InvalidOption
103
+ usage and exit 1
104
+ end
105
+
106
+ # The test suite for this file expects the unmacer to be the last expression.
107
+ unmacer
@@ -0,0 +1,160 @@
1
+ require 'fileutils'
2
+ require 'find'
3
+
4
+ class Unmacer
5
+ attr_accessor :verbose,
6
+ :pretend,
7
+ :keep_spotlight,
8
+ :keep_fsevents,
9
+ :keep_trashes,
10
+ :keep_macosx,
11
+ :keep_dsstore,
12
+ :keep_apple_double,
13
+ :keep_apple_double_orphans,
14
+ :keep_custom_folder_icons
15
+
16
+ SPOTLIGHT = '.Spotlight-V100'
17
+ FSEVENTS = '.fseventsd'
18
+ TRASHES = '.Trashes'
19
+ MACOSX = '__MACOSX'
20
+ DSSTORE = '.DS_Store'
21
+ ICON = "Icon\cM"
22
+
23
+ def initialize
24
+ self.verbose = false
25
+ self.pretend = false
26
+ self.keep_spotlight = false
27
+ self.keep_fsevents = false
28
+ self.keep_trashes = false
29
+ self.keep_macosx = false
30
+ self.keep_dsstore = false
31
+ self.keep_apple_double = false
32
+ self.keep_apple_double_orphans = false
33
+ self.keep_custom_folder_icons = false
34
+ end
35
+
36
+ def unmac!(dirnames)
37
+ dirnames.each do |dirname|
38
+ unmac_root(dirname)
39
+ find_skipping_root(dirname) do |f|
40
+ unmac_folder(f) if File.directory?(f)
41
+ end
42
+ end
43
+ end
44
+
45
+ def find_skipping_root(path)
46
+ root_seen = false
47
+ Find.find(path) do |f|
48
+ if !root_seen
49
+ root_seen = true
50
+ else
51
+ yield f
52
+ end
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ def unmac_root(dirname)
59
+ # Order is important because ".Trashes" has "._.Trashes". Otherwise,
60
+ # "._.Trashes" could be left as an orphan.
61
+ unmac_folder(dirname)
62
+ delete_spotlight(dirname) unless keep_spotlight
63
+ delete_fsevents(dirname) unless keep_fsevents
64
+ delete_trashes(dirname) unless keep_trashes
65
+ end
66
+
67
+ def unmac_folder(dirname)
68
+ delete_macosx(dirname) unless keep_macosx
69
+ delete_dsstore(dirname) unless keep_dsstore
70
+ delete_apple_double(dirname) unless keep_apple_double
71
+ delete_custom_folder_icon(dirname) unless keep_custom_folder_icons
72
+ end
73
+
74
+ def delete(parent, file_or_directory)
75
+ name = File.join(parent, file_or_directory)
76
+ if File.exists?(name)
77
+ if pretend
78
+ puts "would delete #{name}"
79
+ else
80
+ # Vanilla rm_r cannot delete a directory that has AppleDoubles.
81
+ FileUtils.rmtree(name)
82
+ puts "deleted #{name}" if verbose
83
+ end
84
+ end
85
+ rescue Exception => e
86
+ $stderr.puts("could not delete #{name}: #{e.message}")
87
+ end
88
+
89
+ # Spotlight saves all its index-related files in the .Spotlight-V100 directory
90
+ # at the root level of a volume it has indexed.
91
+ #
92
+ # See http://www.thexlab.com/faqs/stopspotlightindex.html.
93
+ def delete_spotlight(dirname)
94
+ delete(dirname, SPOTLIGHT)
95
+ end
96
+
97
+ # The FSEvents framework has a daemon that dumps events from /dev/fsevents
98
+ # into log files stored in a .fseventsd directory at the root of the volume
99
+ # the events are for.
100
+ #
101
+ # See http://arstechnica.com/reviews/os/mac-os-x-10-5.ars/7.
102
+ def delete_fsevents(dirname)
103
+ delete(dirname, FSEVENTS)
104
+ end
105
+
106
+ # A volume may have a ".Trashes" folder in the root directory. The Trash in
107
+ # the Dock shows the contents of the ".Trash" folder in your home directory
108
+ # and the content of all the ".Trashes/uid" in the rest of volumes.
109
+ #
110
+ # See http://discussions.apple.com/thread.jspa?messageID=1145130.
111
+ def delete_trashes(dirname)
112
+ delete(dirname, TRASHES)
113
+ end
114
+
115
+ # Apple's builtin Zip archiver stores some metadata in a directory called
116
+ # "__MACOSX".
117
+ #
118
+ # See http://floatingsun.net/2007/02/07/whats-with-__macosx-in-zip-files.
119
+ def delete_macosx(dirname)
120
+ delete(dirname, MACOSX)
121
+ end
122
+
123
+ # In each directory the ".DS_Store" file stores info about Finder window
124
+ # settings and Spotlight comments of its files.
125
+ #
126
+ # See http://en.wikipedia.org/wiki/.DS_Store.
127
+ def delete_dsstore(dirname)
128
+ delete(dirname, DSSTORE)
129
+ end
130
+
131
+ # When a file is copied to a volume that does not natively support HFS file
132
+ # characteristics its data fork is stored under the file's regular name, and
133
+ # the additional HFS information (resource fork, type & creator codes, etc.)
134
+ # is stored in a second file in AppleDouble format, with a name that starts
135
+ # with "._". So if the original file is called "foo.txt" you get a spurious
136
+ # ghost file called "._foo.txt".
137
+ #
138
+ # If you drag & drop a file onto a Windows partition, or you unpack a tarball
139
+ # on Linux that was built with Apple's tar(1) archiver you'll get that extra
140
+ # stuff. Reason is if the tarball is extracted into another Mac the metadata
141
+ # is preserved with this hack.
142
+ #
143
+ # See http://www.westwind.com/reference/OS-X/invisibles.html.
144
+ # See http://en.wikipedia.org/wiki/Resource_fork.
145
+ # See http://en.wikipedia.org/wiki/AppleSingle.
146
+ def delete_apple_double(dirname)
147
+ basenames = Dir.entries(dirname)
148
+ basenames.select do |basename|
149
+ basename =~ /\A\._(.*)/ &&
150
+ !keep_apple_double_orphans ||
151
+ basenames.include?($1)
152
+ end.each do |basename|
153
+ delete(dirname, basename)
154
+ end
155
+ end
156
+
157
+ def delete_custom_folder_icon(dirname)
158
+ delete(dirname, ICON)
159
+ end
160
+ end
@@ -0,0 +1,241 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
2
+
3
+ require 'stringio'
4
+ require 'unmacer'
5
+ require 'test/unit'
6
+
7
+ UNMAC_RB = File.join(File.dirname(__FILE__), '..', 'bin', 'unmac')
8
+ UNMAC_SRC = File.open(UNMAC_RB) { |f| f.read }
9
+
10
+ class TestUnmac < Test::Unit::TestCase
11
+ def call_unmac(*args)
12
+ ARGV.clear
13
+ ARGV.concat(['--test', *args])
14
+ eval UNMAC_SRC
15
+ end
16
+
17
+ def test_no_options
18
+ unmacer = call_unmac('dummy')
19
+ assert !unmacer.verbose
20
+ assert !unmacer.pretend
21
+ assert !unmacer.keep_spotlight
22
+ assert !unmacer.keep_fsevents
23
+ assert !unmacer.keep_trashes
24
+ assert !unmacer.keep_macosx
25
+ assert !unmacer.keep_dsstore
26
+ assert !unmacer.keep_apple_double
27
+ assert !unmacer.keep_apple_double_orphans
28
+ assert !unmacer.keep_custom_folder_icons
29
+ assert_equal %w(dummy), ARGV
30
+ end
31
+
32
+ def test_verbose
33
+ for opt in ['--verbose', '-v']
34
+ unmacer = call_unmac(opt, 'dummy')
35
+ assert unmacer.verbose
36
+ assert !unmacer.pretend
37
+ assert !unmacer.keep_spotlight
38
+ assert !unmacer.keep_fsevents
39
+ assert !unmacer.keep_trashes
40
+ assert !unmacer.keep_macosx
41
+ assert !unmacer.keep_dsstore
42
+ assert !unmacer.keep_apple_double
43
+ assert !unmacer.keep_apple_double_orphans
44
+ assert !unmacer.keep_custom_folder_icons
45
+ assert_equal %w(dummy), ARGV
46
+ end
47
+ end
48
+
49
+ def test_pretend
50
+ for opt in ['--pretend', '-p']
51
+ unmacer = call_unmac(opt, 'dummy')
52
+ assert !unmacer.verbose
53
+ assert unmacer.pretend
54
+ assert !unmacer.keep_spotlight
55
+ assert !unmacer.keep_fsevents
56
+ assert !unmacer.keep_trashes
57
+ assert !unmacer.keep_macosx
58
+ assert !unmacer.keep_dsstore
59
+ assert !unmacer.keep_apple_double
60
+ assert !unmacer.keep_apple_double_orphans
61
+ assert !unmacer.keep_custom_folder_icons
62
+ assert_equal %w(dummy), ARGV
63
+ end
64
+ end
65
+
66
+ def test_spotlight
67
+ for opt in ['--keep-spotlight', '-s']
68
+ unmacer = call_unmac(opt, 'dummy')
69
+ assert !unmacer.verbose
70
+ assert !unmacer.pretend
71
+ assert unmacer.keep_spotlight
72
+ assert !unmacer.keep_fsevents
73
+ assert !unmacer.keep_trashes
74
+ assert !unmacer.keep_macosx
75
+ assert !unmacer.keep_dsstore
76
+ assert !unmacer.keep_apple_double
77
+ assert !unmacer.keep_apple_double_orphans
78
+ assert !unmacer.keep_custom_folder_icons
79
+ assert_equal %w(dummy), ARGV
80
+ end
81
+ end
82
+
83
+ def test_fsevents
84
+ for opt in ['--keep-fsevents', '-f']
85
+ unmacer = call_unmac(opt, 'dummy')
86
+ assert !unmacer.verbose
87
+ assert !unmacer.pretend
88
+ assert !unmacer.keep_spotlight
89
+ assert unmacer.keep_fsevents
90
+ assert !unmacer.keep_trashes
91
+ assert !unmacer.keep_macosx
92
+ assert !unmacer.keep_dsstore
93
+ assert !unmacer.keep_apple_double
94
+ assert !unmacer.keep_apple_double_orphans
95
+ assert !unmacer.keep_custom_folder_icons
96
+ assert_equal %w(dummy), ARGV
97
+ end
98
+ end
99
+
100
+ def test_trashes
101
+ for opt in ['--keep-trashes', '-t']
102
+ unmacer = call_unmac(opt, 'dummy')
103
+ assert !unmacer.verbose
104
+ assert !unmacer.pretend
105
+ assert !unmacer.keep_spotlight
106
+ assert !unmacer.keep_fsevents
107
+ assert unmacer.keep_trashes
108
+ assert !unmacer.keep_macosx
109
+ assert !unmacer.keep_dsstore
110
+ assert !unmacer.keep_apple_double
111
+ assert !unmacer.keep_apple_double_orphans
112
+ assert !unmacer.keep_custom_folder_icons
113
+ assert_equal %w(dummy), ARGV
114
+ end
115
+ end
116
+
117
+ def test_macosx
118
+ for opt in ['--keep-macosx', '-m']
119
+ unmacer = call_unmac(opt, 'dummy')
120
+ assert !unmacer.verbose
121
+ assert !unmacer.pretend
122
+ assert !unmacer.keep_spotlight
123
+ assert !unmacer.keep_fsevents
124
+ assert !unmacer.keep_trashes
125
+ assert unmacer.keep_macosx
126
+ assert !unmacer.keep_dsstore
127
+ assert !unmacer.keep_apple_double
128
+ assert !unmacer.keep_apple_double_orphans
129
+ assert !unmacer.keep_custom_folder_icons
130
+ assert_equal %w(dummy), ARGV
131
+ end
132
+ end
133
+
134
+ def test_dsstore
135
+ for opt in ['--keep-dsstore', '-r']
136
+ unmacer = call_unmac(opt, 'dummy')
137
+ assert !unmacer.verbose
138
+ assert !unmacer.pretend
139
+ assert !unmacer.keep_spotlight
140
+ assert !unmacer.keep_fsevents
141
+ assert !unmacer.keep_trashes
142
+ assert !unmacer.keep_macosx
143
+ assert unmacer.keep_dsstore
144
+ assert !unmacer.keep_apple_double
145
+ assert !unmacer.keep_apple_double_orphans
146
+ assert !unmacer.keep_custom_folder_icons
147
+ assert_equal %w(dummy), ARGV
148
+ end
149
+ end
150
+
151
+ def test_apple_double
152
+ for opt in ['--keep-apple-double', '-d']
153
+ unmacer = call_unmac(opt, 'dummy')
154
+ assert !unmacer.verbose
155
+ assert !unmacer.pretend
156
+ assert !unmacer.keep_spotlight
157
+ assert !unmacer.keep_fsevents
158
+ assert !unmacer.keep_trashes
159
+ assert !unmacer.keep_macosx
160
+ assert !unmacer.keep_dsstore
161
+ assert unmacer.keep_apple_double
162
+ assert !unmacer.keep_apple_double_orphans
163
+ assert !unmacer.keep_custom_folder_icons
164
+ assert_equal %w(dummy), ARGV
165
+ end
166
+ end
167
+
168
+ def test_apple_double_orphans
169
+ for opt in ['--keep-apple-double-orphans', '-o']
170
+ unmacer = call_unmac(opt, 'dummy')
171
+ assert !unmacer.verbose
172
+ assert !unmacer.pretend
173
+ assert !unmacer.keep_spotlight
174
+ assert !unmacer.keep_fsevents
175
+ assert !unmacer.keep_trashes
176
+ assert !unmacer.keep_macosx
177
+ assert !unmacer.keep_dsstore
178
+ assert !unmacer.keep_apple_double
179
+ assert unmacer.keep_apple_double_orphans
180
+ assert !unmacer.keep_custom_folder_icons
181
+ assert_equal %w(dummy), ARGV
182
+ end
183
+ end
184
+
185
+ def test_custom_folder_icons
186
+ for opt in ['--keep-custom-folder-icons', '-i']
187
+ unmacer = call_unmac(opt, 'dummy')
188
+ assert !unmacer.verbose
189
+ assert !unmacer.pretend
190
+ assert !unmacer.keep_spotlight
191
+ assert !unmacer.keep_fsevents
192
+ assert !unmacer.keep_trashes
193
+ assert !unmacer.keep_macosx
194
+ assert !unmacer.keep_dsstore
195
+ assert !unmacer.keep_apple_double
196
+ assert !unmacer.keep_apple_double_orphans
197
+ assert unmacer.keep_custom_folder_icons
198
+ assert_equal %w(dummy), ARGV
199
+ end
200
+ end
201
+
202
+ def test_mix_1
203
+ unmacer = call_unmac(*%w(-v -f -m -d -i dummy))
204
+ assert unmacer.verbose
205
+ assert !unmacer.pretend
206
+ assert !unmacer.keep_spotlight
207
+ assert unmacer.keep_fsevents
208
+ assert !unmacer.keep_trashes
209
+ assert unmacer.keep_macosx
210
+ assert !unmacer.keep_dsstore
211
+ assert unmacer.keep_apple_double
212
+ assert !unmacer.keep_apple_double_orphans
213
+ assert unmacer.keep_custom_folder_icons
214
+ assert_equal %w(dummy), ARGV
215
+ end
216
+
217
+ def test_mix_2
218
+ unmacer = call_unmac(*%w(-s -t -r -o dummy))
219
+ assert !unmacer.verbose
220
+ assert !unmacer.pretend
221
+ assert unmacer.keep_spotlight
222
+ assert !unmacer.keep_fsevents
223
+ assert unmacer.keep_trashes
224
+ assert !unmacer.keep_macosx
225
+ assert unmacer.keep_dsstore
226
+ assert !unmacer.keep_apple_double
227
+ assert unmacer.keep_apple_double_orphans
228
+ assert !unmacer.keep_custom_folder_icons
229
+ assert_equal %w(dummy), ARGV
230
+ end
231
+
232
+ def test_help
233
+ buf = ''
234
+ $> = StringIO.open(buf, 'w')
235
+ call_unmac('-h')
236
+ $> = $stdout
237
+ assert_match /^unmac\b/, buf
238
+ assert_match /^Usage\b/, buf
239
+ assert ARGV.empty?
240
+ end
241
+ end
@@ -0,0 +1,234 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
2
+
3
+ require 'stringio'
4
+ require 'fileutils'
5
+ require 'set'
6
+ require 'unmacer'
7
+ require 'test/unit'
8
+
9
+ TEST_DIR = File.expand_path(File.dirname(__FILE__))
10
+ TMP_DIR = File.join(TEST_DIR, 'tmp')
11
+
12
+ class TestUnmacer < Test::Unit::TestCase
13
+ def in_test_dir
14
+ Dir.chdir(TEST_DIR) { yield }
15
+ end
16
+
17
+ def in_tmp_dir
18
+ Dir.chdir(TMP_DIR) { yield }
19
+ end
20
+
21
+ def setup
22
+ in_test_dir { Dir.mkdir('tmp') }
23
+ @unmacer = Unmacer.new
24
+ end
25
+
26
+ def teardown
27
+ in_test_dir { FileUtils.rm_r('tmp') }
28
+ end
29
+
30
+ def unmac!
31
+ @unmacer.unmac!(TMP_DIR)
32
+ end
33
+
34
+ def create_struct(dirnames=[], fnames=[])
35
+ in_tmp_dir do
36
+ FileUtils.mkdir_p([*dirnames])
37
+ FileUtils.touch([*fnames])
38
+ end
39
+ end
40
+
41
+ def read_struct
42
+ entries = []
43
+ in_tmp_dir do
44
+ @unmacer.find_skipping_root('.') do |f|
45
+ # File.find returns "./foo.txt", remove the leading stuff.
46
+ entries << f.sub(%r{\A\.[/\\]}, '')
47
+ end
48
+ end
49
+ entries
50
+ end
51
+
52
+ def test_an_empty_directory_should_remain_empty
53
+ create_struct
54
+ unmac!
55
+ assert read_struct.empty?
56
+ end
57
+
58
+ def test_pretend
59
+ dirs = [Unmacer::SPOTLIGHT, Unmacer::FSEVENTS, Unmacer::TRASHES, Unmacer::MACOSX]
60
+ create_struct(dirs, '._dummy')
61
+ buf = ''
62
+ $> = StringIO.new(buf, 'w')
63
+ @unmacer.pretend = true
64
+ unmac!
65
+ $> = $stdout
66
+ assert Set.new(dirs + ['._dummy']), Set.new(read_struct)
67
+ assert_match /^would delete.*\.Spotlight/, buf
68
+ assert_match /^would delete.*\.fsevents/, buf
69
+ assert_match /^would delete.*\.Trashes/, buf
70
+ assert_match /^would delete.*__MACOSX/, buf
71
+ assert_match /^would delete.*\._dummy/, buf
72
+ end
73
+
74
+ def test_spotlight
75
+ create_struct(Unmacer::SPOTLIGHT)
76
+ unmac!
77
+ assert read_struct.empty?
78
+ end
79
+
80
+ def test_spotlight_ignored_if_not_in_root
81
+ fake = File.join('dummy', Unmacer::SPOTLIGHT)
82
+ create_struct(fake)
83
+ unmac!
84
+ assert read_struct.include?(fake)
85
+ end
86
+
87
+ def test_keep_spotlight
88
+ create_struct(Unmacer::SPOTLIGHT)
89
+ @unmacer.keep_spotlight = true
90
+ unmac!
91
+ assert [Unmacer::SPOTLIGHT], read_struct
92
+ end
93
+
94
+ def test_fsevents
95
+ create_struct(Unmacer::FSEVENTS)
96
+ unmac!
97
+ assert read_struct.empty?
98
+ end
99
+
100
+ def test_fsevents_ignored_if_not_in_root
101
+ fake = File.join('dummy', Unmacer::FSEVENTS)
102
+ create_struct(fake)
103
+ unmac!
104
+ assert read_struct.include?(fake)
105
+ end
106
+
107
+ def test_keep_fsevents
108
+ create_struct(Unmacer::FSEVENTS)
109
+ @unmacer.keep_fsevents = true
110
+ unmac!
111
+ assert_equal [Unmacer::FSEVENTS], read_struct
112
+ end
113
+
114
+ def test_trashes
115
+ create_struct(Unmacer::TRASHES)
116
+ unmac!
117
+ assert read_struct.empty?
118
+ end
119
+
120
+ def test_trashes_ignored_if_not_in_root
121
+ fake = File.join('dummy', Unmacer::TRASHES)
122
+ create_struct(fake)
123
+ unmac!
124
+ assert read_struct.include?(fake)
125
+ end
126
+
127
+ def test_keep_trashes
128
+ create_struct(Unmacer::TRASHES)
129
+ @unmacer.keep_trashes = true
130
+ unmac!
131
+ assert_equal [Unmacer::TRASHES], read_struct
132
+ end
133
+
134
+ def test_macosx
135
+ create_struct(Unmacer::MACOSX)
136
+ unmac!
137
+ assert read_struct.empty?
138
+ end
139
+
140
+ def test_keep_macosx
141
+ create_struct(Unmacer::MACOSX)
142
+ @unmacer.keep_macosx = true
143
+ unmac!
144
+ assert_equal [Unmacer::MACOSX], read_struct
145
+ end
146
+
147
+ def test_dsstore
148
+ create_struct([], Unmacer::DSSTORE)
149
+ unmac!
150
+ assert read_struct.empty?
151
+ end
152
+
153
+ def test_keep_dsstore
154
+ create_struct([], Unmacer::DSSTORE)
155
+ @unmacer.keep_dsstore = true
156
+ unmac!
157
+ assert_equal [Unmacer::DSSTORE], read_struct
158
+ end
159
+
160
+ def test_apple_double_with_pair
161
+ create_struct([], %w(foo.txt ._foo.txt))
162
+ unmac!
163
+ assert_equal %w(foo.txt), read_struct
164
+ end
165
+
166
+ def test_keep_apple_double_with_pair
167
+ create_struct([], %w(foo.txt ._foo.txt))
168
+ @unmacer.keep_apple_double = true
169
+ unmac!
170
+ assert_equal Set.new(%w(foo.txt ._foo.txt)), Set.new(read_struct)
171
+ end
172
+
173
+ def test_apple_double_with_orphan
174
+ create_struct([], '._foo.txt')
175
+ unmac!
176
+ assert read_struct.empty?
177
+ end
178
+
179
+ def test_keep_apple_double_with_orphan
180
+ create_struct([], '._foo.txt')
181
+ @unmacer.keep_apple_double = true
182
+ unmac!
183
+ assert ['._foo.txt'], read_struct
184
+ end
185
+
186
+ def test_keep_apple_double_orphans
187
+ create_struct([], '._foo.txt')
188
+ @unmacer.keep_apple_double_orphans = true
189
+ unmac!
190
+ assert_equal %w(._foo.txt), read_struct
191
+ end
192
+
193
+ def test_custom_folder_icons
194
+ create_struct([], Unmacer::ICON)
195
+ unmac!
196
+ assert read_struct.empty?
197
+ end
198
+
199
+ def test_keep_custom_folder_icons
200
+ create_struct([], Unmacer::ICON)
201
+ @unmacer.keep_custom_folder_icons = true
202
+ unmac!
203
+ assert_equal [Unmacer::ICON], read_struct
204
+ end
205
+
206
+ def test_a_handful_of_files
207
+ dummy = 'dummy'
208
+ dummy2 = File.join(dummy, 'dummy2')
209
+ ghost = File.join(dummy, '._ghost')
210
+ remain = File.join(dummy2, 'should_remain')
211
+ create_struct([
212
+ Unmacer::SPOTLIGHT,
213
+ Unmacer::FSEVENTS,
214
+ Unmacer::TRASHES,
215
+ Unmacer::MACOSX,
216
+ dummy,
217
+ dummy2
218
+ ], [
219
+ Unmacer::DSSTORE,
220
+ File.join(dummy, Unmacer::DSSTORE),
221
+ File.join(dummy2, Unmacer::DSSTORE),
222
+ File.join(dummy, Unmacer::MACOSX),
223
+ File.join(dummy2, Unmacer::MACOSX),
224
+ File.join(dummy, Unmacer::ICON),
225
+ File.join(dummy2, Unmacer::ICON),
226
+ 'foo.txt',
227
+ '._foo.txt',
228
+ ghost,
229
+ remain
230
+ ])
231
+ unmac!
232
+ assert_equal Set.new(['foo.txt', dummy, dummy2, remain]), Set.new(read_struct)
233
+ end
234
+ end
@@ -0,0 +1,24 @@
1
+ Gem::Specification.new do |spec|
2
+ spec.name = 'unmac'
3
+ spec.version = '0.6'
4
+ spec.summary = 'Delete spurious Mac files under a directory or volume'
5
+ spec.homepage = 'http://github.com/fxn/unmac/tree/master'
6
+ spec.executables = %w(unmac)
7
+ spec.author = 'Xavier Noria'
8
+ spec.email = 'fxn@hashref.com'
9
+ spec.rubyforge_project = 'unmac'
10
+
11
+ spec.test_files = %w(
12
+ test/test_unmacer.rb
13
+ test/test_unmac.rb
14
+ )
15
+
16
+ spec.files = %w(
17
+ unmac.gemspec
18
+ Rakefile
19
+ README
20
+ MIT-LICENSE
21
+ bin/unmac
22
+ lib/unmacer.rb
23
+ ) + spec.test_files
24
+ end
metadata ADDED
@@ -0,0 +1,61 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: unmac
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.6"
5
+ platform: ruby
6
+ authors:
7
+ - Xavier Noria
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-01-08 00:00:00 +01:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description:
17
+ email: fxn@hashref.com
18
+ executables:
19
+ - unmac
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - unmac.gemspec
26
+ - Rakefile
27
+ - README
28
+ - MIT-LICENSE
29
+ - bin/unmac
30
+ - lib/unmacer.rb
31
+ - test/test_unmacer.rb
32
+ - test/test_unmac.rb
33
+ has_rdoc: false
34
+ homepage: http://github.com/fxn/unmac/tree/master
35
+ post_install_message:
36
+ rdoc_options: []
37
+
38
+ require_paths:
39
+ - lib
40
+ required_ruby_version: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: "0"
45
+ version:
46
+ required_rubygems_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: "0"
51
+ version:
52
+ requirements: []
53
+
54
+ rubyforge_project: unmac
55
+ rubygems_version: 1.3.1
56
+ signing_key:
57
+ specification_version: 2
58
+ summary: Delete spurious Mac files under a directory or volume
59
+ test_files:
60
+ - test/test_unmacer.rb
61
+ - test/test_unmac.rb