pathname3 1.2.3

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.
data/pathname3.gemspec ADDED
@@ -0,0 +1,31 @@
1
+ Gem::Specification.new do |s|
2
+ s.author = 'Stephen Touset'
3
+ s.email = 'stephen@touset.org'
4
+ s.homepage = 'http://github.com/stouset/pathname3/'
5
+
6
+ s.name = 'pathname3'
7
+ s.version = '1.2.3'
8
+ s.platform = Gem::Platform::RUBY
9
+ s.description = 'Faster replacement for pathname and pathname2'
10
+ s.summary = <<-DESC.strip.gsub(/\s+/, ' ')
11
+ pathname3 is a third attempt at rewriting the eminently useful Pathname
12
+ library in Ruby. The first version (packaged with Ruby) is extremely slow
13
+ for many operations, as is the second one (by Daniel J. Berger).
14
+
15
+ This version attempts to combine the best parts of both predecessors,
16
+ while also maintaining speed. Namely, we use Daniel's String-based
17
+ approach, while dumping Facade and sending messages to FileTest, File,
18
+ Dir, and FileUtils explicitly (for clarity and predictability).
19
+
20
+ Windows support will be provided in a later release. Donations of code
21
+ toward this end would be appreciated.
22
+ DESC
23
+
24
+ s.files = %w{ CHANGES README LICENSE }
25
+ s.files += %w{ pathname3.gemspec }
26
+ s.files += %w{ lib/pathname3.rb }
27
+ s.files += %w{ test/lib/test_pathname.rb }
28
+ s.files += %w{ test/lib/test_pathname3.rb }
29
+
30
+ s.has_rdoc = true
31
+ end
@@ -0,0 +1,486 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'test/unit'
4
+ require 'lib/pathname3'
5
+
6
+ require 'fileutils'
7
+ require 'tmpdir'
8
+ require 'enumerator'
9
+
10
+ class TestPathname < Test::Unit::TestCase
11
+
12
+ if RUBY_VERSION < "1.9"
13
+ FUNCALL = :__send__
14
+ else
15
+ FUNCALL = :funcall
16
+ end
17
+
18
+ def self.define_assertion(name, &block)
19
+ @defassert_num ||= {}
20
+ @defassert_num[name] ||= 0
21
+ @defassert_num[name] += 1
22
+ define_method("test_#{name}_#{@defassert_num[name]}", &block)
23
+ end
24
+
25
+ def self.defassert(name, result, *args)
26
+ define_assertion(name) {
27
+ assert_equal(result, self.send(name, *args), "#{name}(#{args.map {|a| a.inspect }.join(', ')})")
28
+ }
29
+ end
30
+
31
+ DOSISH = File::ALT_SEPARATOR != nil
32
+ DOSISH_DRIVE_LETTER = File.dirname("A:") == "A:."
33
+ DOSISH_UNC = File.dirname("//") == "//"
34
+
35
+ def cleanpath_aggressive(path)
36
+ Pathname.new(path).cleanpath.to_s
37
+ end
38
+
39
+ defassert(:cleanpath_aggressive, '/', '/')
40
+ defassert(:cleanpath_aggressive, '.', '')
41
+ defassert(:cleanpath_aggressive, '.', '.')
42
+ defassert(:cleanpath_aggressive, '..', '..')
43
+ defassert(:cleanpath_aggressive, 'a', 'a')
44
+ defassert(:cleanpath_aggressive, '/', '/.')
45
+ defassert(:cleanpath_aggressive, '/', '/..')
46
+ defassert(:cleanpath_aggressive, '/a', '/a')
47
+ defassert(:cleanpath_aggressive, '.', './')
48
+ defassert(:cleanpath_aggressive, '..', '../')
49
+ defassert(:cleanpath_aggressive, 'a', 'a/')
50
+ defassert(:cleanpath_aggressive, 'a/b', 'a//b')
51
+ defassert(:cleanpath_aggressive, 'a', 'a/.')
52
+ defassert(:cleanpath_aggressive, 'a', 'a/./')
53
+ defassert(:cleanpath_aggressive, '.', 'a/..')
54
+ defassert(:cleanpath_aggressive, '.', 'a/../')
55
+ defassert(:cleanpath_aggressive, '/a', '/a/.')
56
+ defassert(:cleanpath_aggressive, '..', './..')
57
+ defassert(:cleanpath_aggressive, '..', '../.')
58
+ defassert(:cleanpath_aggressive, '..', './../')
59
+ defassert(:cleanpath_aggressive, '..', '.././')
60
+ defassert(:cleanpath_aggressive, '/', '/./..')
61
+ defassert(:cleanpath_aggressive, '/', '/../.')
62
+ defassert(:cleanpath_aggressive, '/', '/./../')
63
+ defassert(:cleanpath_aggressive, '/', '/.././')
64
+ defassert(:cleanpath_aggressive, 'a/b/c', 'a/b/c')
65
+ defassert(:cleanpath_aggressive, 'b/c', './b/c')
66
+ defassert(:cleanpath_aggressive, 'a/c', 'a/./c')
67
+ defassert(:cleanpath_aggressive, 'a/b', 'a/b/.')
68
+ defassert(:cleanpath_aggressive, '.', 'a/../.')
69
+ defassert(:cleanpath_aggressive, '/a', '/../.././../a')
70
+ defassert(:cleanpath_aggressive, '../../d', 'a/b/../../../../c/../d')
71
+
72
+ if DOSISH_UNC
73
+ defassert(:cleanpath_aggressive, '//a/b/c', '//a/b/c/')
74
+ else
75
+ defassert(:cleanpath_aggressive, '/', '///')
76
+ defassert(:cleanpath_aggressive, '/a', '///a')
77
+ defassert(:cleanpath_aggressive, '/', '///..')
78
+ defassert(:cleanpath_aggressive, '/', '///.')
79
+ defassert(:cleanpath_aggressive, '/', '///a/../..')
80
+ end
81
+
82
+ def cleanpath_conservative(path)
83
+ Pathname.new(path).cleanpath(true).to_s
84
+ end
85
+
86
+ defassert(:cleanpath_conservative, '/', '/')
87
+ defassert(:cleanpath_conservative, '.', '')
88
+ defassert(:cleanpath_conservative, '.', '.')
89
+ defassert(:cleanpath_conservative, '..', '..')
90
+ defassert(:cleanpath_conservative, 'a', 'a')
91
+ defassert(:cleanpath_conservative, '/', '/.')
92
+ defassert(:cleanpath_conservative, '/', '/..')
93
+ defassert(:cleanpath_conservative, '/a', '/a')
94
+ defassert(:cleanpath_conservative, '.', './')
95
+ defassert(:cleanpath_conservative, '..', '../')
96
+ defassert(:cleanpath_conservative, 'a/', 'a/')
97
+ defassert(:cleanpath_conservative, 'a/b', 'a//b')
98
+ defassert(:cleanpath_conservative, 'a/.', 'a/.')
99
+ defassert(:cleanpath_conservative, 'a/.', 'a/./')
100
+ defassert(:cleanpath_conservative, 'a/..', 'a/../')
101
+ defassert(:cleanpath_conservative, '/a/.', '/a/.')
102
+ defassert(:cleanpath_conservative, '..', './..')
103
+ defassert(:cleanpath_conservative, '..', '../.')
104
+ defassert(:cleanpath_conservative, '..', './../')
105
+ defassert(:cleanpath_conservative, '..', '.././')
106
+ defassert(:cleanpath_conservative, '/', '/./..')
107
+ defassert(:cleanpath_conservative, '/', '/../.')
108
+ defassert(:cleanpath_conservative, '/', '/./../')
109
+ defassert(:cleanpath_conservative, '/', '/.././')
110
+ defassert(:cleanpath_conservative, 'a/b/c', 'a/b/c')
111
+ defassert(:cleanpath_conservative, 'b/c', './b/c')
112
+ defassert(:cleanpath_conservative, 'a/c', 'a/./c')
113
+ defassert(:cleanpath_conservative, 'a/b/.', 'a/b/.')
114
+ defassert(:cleanpath_conservative, 'a/..', 'a/../.')
115
+ defassert(:cleanpath_conservative, '/a', '/../.././../a')
116
+ defassert(:cleanpath_conservative, 'a/b/../../../../c/../d', 'a/b/../../../../c/../d')
117
+
118
+ if DOSISH_UNC
119
+ defassert(:cleanpath_conservative, '//', '//')
120
+ else
121
+ defassert(:cleanpath_conservative, '/', '//')
122
+ end
123
+
124
+ # has_trailing_separator?(path) -> bool
125
+ def has_trailing_separator?(path)
126
+ Pathname.allocate.send(FUNCALL, :has_trailing_separator?, path)
127
+ end
128
+
129
+ defassert(:has_trailing_separator?, false, "/")
130
+ defassert(:has_trailing_separator?, false, "///")
131
+ defassert(:has_trailing_separator?, false, "a")
132
+ defassert(:has_trailing_separator?, true, "a/")
133
+
134
+ def add_trailing_separator(path)
135
+ Pathname.allocate.send(FUNCALL, :add_trailing_separator, path)
136
+ end
137
+
138
+ def del_trailing_separator(path)
139
+ Pathname.allocate.send(FUNCALL, :del_trailing_separator, path)
140
+ end
141
+
142
+ defassert(:del_trailing_separator, "/", "/")
143
+ defassert(:del_trailing_separator, "/a", "/a")
144
+ defassert(:del_trailing_separator, "/a", "/a/")
145
+ defassert(:del_trailing_separator, "/a", "/a//")
146
+ defassert(:del_trailing_separator, ".", ".")
147
+ defassert(:del_trailing_separator, ".", "./")
148
+ defassert(:del_trailing_separator, ".", ".//")
149
+
150
+ if DOSISH_DRIVE_LETTER
151
+ defassert(:del_trailing_separator, "A:", "A:")
152
+ defassert(:del_trailing_separator, "A:/", "A:/")
153
+ defassert(:del_trailing_separator, "A:/", "A://")
154
+ defassert(:del_trailing_separator, "A:.", "A:.")
155
+ defassert(:del_trailing_separator, "A:.", "A:./")
156
+ defassert(:del_trailing_separator, "A:.", "A:.//")
157
+ end
158
+
159
+ if DOSISH_UNC
160
+ defassert(:del_trailing_separator, "//", "//")
161
+ defassert(:del_trailing_separator, "//a", "//a")
162
+ defassert(:del_trailing_separator, "//a", "//a/")
163
+ defassert(:del_trailing_separator, "//a", "//a//")
164
+ defassert(:del_trailing_separator, "//a/b", "//a/b")
165
+ defassert(:del_trailing_separator, "//a/b", "//a/b/")
166
+ defassert(:del_trailing_separator, "//a/b", "//a/b//")
167
+ defassert(:del_trailing_separator, "//a/b/c", "//a/b/c")
168
+ defassert(:del_trailing_separator, "//a/b/c", "//a/b/c/")
169
+ defassert(:del_trailing_separator, "//a/b/c", "//a/b/c//")
170
+ else
171
+ defassert(:del_trailing_separator, "/", "///")
172
+ defassert(:del_trailing_separator, "///a", "///a/")
173
+ end
174
+
175
+ if DOSISH
176
+ defassert(:del_trailing_separator, "a", "a\\")
177
+ defassert(:del_trailing_separator, "\225\\", "\225\\\\") # SJIS
178
+ end
179
+
180
+ def plus(path1, path2) # -> path
181
+ (Pathname.new(path1) + Pathname.new(path2)).to_s
182
+ end
183
+
184
+ defassert(:plus, '/', '/', '/')
185
+ defassert(:plus, 'a/b', 'a', 'b')
186
+ defassert(:plus, 'a', 'a', '.')
187
+ defassert(:plus, 'b', '.', 'b')
188
+ defassert(:plus, '.', '.', '.')
189
+ defassert(:plus, '/b', 'a', '/b')
190
+
191
+ defassert(:plus, '/', '/', '..')
192
+ defassert(:plus, '.', 'a', '..')
193
+ defassert(:plus, 'a', 'a/b', '..')
194
+ defassert(:plus, '../..', '..', '..')
195
+ defassert(:plus, '/c', '/', '../c')
196
+ defassert(:plus, 'c', 'a', '../c')
197
+ defassert(:plus, 'a/c', 'a/b', '../c')
198
+ defassert(:plus, '../../c', '..', '../c')
199
+
200
+ defassert(:plus, 'a//b/d//e', 'a//b/c', '../d//e')
201
+
202
+ def relative?(path)
203
+ Pathname.new(path).relative?
204
+ end
205
+
206
+ defassert(:relative?, false, '/')
207
+ defassert(:relative?, false, '/a')
208
+ defassert(:relative?, false, '/..')
209
+ defassert(:relative?, true, 'a')
210
+ defassert(:relative?, true, 'a/b')
211
+
212
+ if DOSISH_DRIVE_LETTER
213
+ defassert(:relative?, false, 'A:')
214
+ defassert(:relative?, false, 'A:/')
215
+ defassert(:relative?, false, 'A:/a')
216
+ end
217
+
218
+ if File.dirname('//') == '//'
219
+ defassert(:relative?, false, '//')
220
+ defassert(:relative?, false, '//a')
221
+ defassert(:relative?, false, '//a/')
222
+ defassert(:relative?, false, '//a/b')
223
+ defassert(:relative?, false, '//a/b/')
224
+ defassert(:relative?, false, '//a/b/c')
225
+ end
226
+
227
+ def relative_path_from(dest_directory, base_directory)
228
+ Pathname.new(dest_directory).relative_path_from(Pathname.new(base_directory)).to_s
229
+ end
230
+
231
+ defassert(:relative_path_from, "../a", "a", "b")
232
+ defassert(:relative_path_from, "../a", "a", "b/")
233
+ defassert(:relative_path_from, "../a", "a/", "b")
234
+ defassert(:relative_path_from, "../a", "a/", "b/")
235
+ defassert(:relative_path_from, "../a", "/a", "/b")
236
+ defassert(:relative_path_from, "../a", "/a", "/b/")
237
+ defassert(:relative_path_from, "../a", "/a/", "/b")
238
+ defassert(:relative_path_from, "../a", "/a/", "/b/")
239
+
240
+ defassert(:relative_path_from, "../b", "a/b", "a/c")
241
+ defassert(:relative_path_from, "../a", "../a", "../b")
242
+
243
+ defassert(:relative_path_from, "a", "a", ".")
244
+ defassert(:relative_path_from, "..", ".", "a")
245
+
246
+ defassert(:relative_path_from, ".", ".", ".")
247
+ defassert(:relative_path_from, ".", "..", "..")
248
+ defassert(:relative_path_from, "..", "..", ".")
249
+
250
+ defassert(:relative_path_from, "c/d", "/a/b/c/d", "/a/b")
251
+ defassert(:relative_path_from, "../..", "/a/b", "/a/b/c/d")
252
+ defassert(:relative_path_from, "../../../../e", "/e", "/a/b/c/d")
253
+ defassert(:relative_path_from, "../b/c", "a/b/c", "a/d")
254
+
255
+ defassert(:relative_path_from, "../a", "/../a", "/b")
256
+ defassert(:relative_path_from, "../../a", "../a", "b")
257
+ defassert(:relative_path_from, ".", "/a/../../b", "/b")
258
+ defassert(:relative_path_from, "..", "a/..", "a")
259
+ defassert(:relative_path_from, ".", "a/../b", "b")
260
+
261
+ defassert(:relative_path_from, "a", "a", "b/..")
262
+ defassert(:relative_path_from, "b/c", "b/c", "b/..")
263
+
264
+ def self.defassert_raise(name, exc, *args)
265
+ define_assertion(name) {
266
+ message = "#{name}(#{args.map {|a| a.inspect }.join(', ')})"
267
+ assert_raise(exc, message) { self.send(name, *args) }
268
+ }
269
+ end
270
+
271
+ defassert_raise(:relative_path_from, ArgumentError, "/", ".")
272
+ defassert_raise(:relative_path_from, ArgumentError, ".", "/")
273
+ defassert_raise(:relative_path_from, ArgumentError, "a", "..")
274
+ defassert_raise(:relative_path_from, ArgumentError, ".", "..")
275
+
276
+ def realpath(path)
277
+ Pathname.new(path).realpath.to_s
278
+ end
279
+
280
+ def test_realpath
281
+ begin
282
+ File.symlink(nil, nil)
283
+ rescue NotImplementedError
284
+ return
285
+ rescue TypeError
286
+ end
287
+ dir = "#{Dir.tmpdir}/tst-pathname-#$$"
288
+ Dir.mkdir(dir)
289
+ begin
290
+ File.symlink("not-exist-target", "#{dir}/not-exist")
291
+ assert_raise(Errno::ENOENT) { realpath("#{dir}/not-exist") }
292
+ File.symlink("loop", "#{dir}/loop")
293
+ assert_raise(Errno::ELOOP) { realpath("#{dir}/loop") }
294
+ ensure
295
+ FileUtils.rmtree(dir)
296
+ end
297
+ end
298
+
299
+ def descend(path)
300
+ Pathname.new(path).enum_for(:descend).map {|v| v.to_s }
301
+ end
302
+
303
+ defassert(:descend, %w[/ /a /a/b /a/b/c], "/a/b/c")
304
+ defassert(:descend, %w[a a/b a/b/c], "a/b/c")
305
+ defassert(:descend, %w[. ./a ./a/b ./a/b/c], "./a/b/c")
306
+ defassert(:descend, %w[a/], "a/")
307
+
308
+ def ascend(path)
309
+ Pathname.new(path).enum_for(:ascend).map {|v| v.to_s }
310
+ end
311
+
312
+ defassert(:ascend, %w[/a/b/c /a/b /a /], "/a/b/c")
313
+ defassert(:ascend, %w[a/b/c a/b a], "a/b/c")
314
+ defassert(:ascend, %w[./a/b/c ./a/b ./a .], "./a/b/c")
315
+ defassert(:ascend, %w[a/], "a/")
316
+
317
+ def test_initialize
318
+ p1 = Pathname.new('a')
319
+ assert_equal('a', p1.to_s)
320
+ p2 = Pathname.new(p1)
321
+ assert_equal(p1, p2)
322
+ end
323
+
324
+ def test_initialize_nul
325
+ assert_raise(ArgumentError) { Pathname.new("a\0") }
326
+ end
327
+
328
+ class AnotherStringLike # :nodoc:
329
+ def initialize(s) @s = s end
330
+ def to_str() @s end
331
+ def ==(other) @s == other end
332
+ end
333
+
334
+ def test_equality
335
+ obj = Pathname.new("a")
336
+ str = "a"
337
+ sym = :a
338
+ ano = AnotherStringLike.new("a")
339
+ assert_equal(false, obj == str)
340
+ assert_equal(false, str == obj)
341
+ assert_equal(false, obj == ano)
342
+ assert_equal(false, ano == obj)
343
+ assert_equal(false, obj == sym)
344
+ assert_equal(false, sym == obj)
345
+
346
+ obj2 = Pathname.new("a")
347
+ assert_equal(true, obj == obj2)
348
+ assert_equal(true, obj === obj2)
349
+ assert_equal(true, obj.eql?(obj2))
350
+ end
351
+
352
+ def test_hashkey
353
+ h = {}
354
+ h[Pathname.new("a")] = 1
355
+ h[Pathname.new("a")] = 2
356
+ assert_equal(1, h.size)
357
+ end
358
+
359
+ def assert_pathname_cmp(e, s1, s2)
360
+ p1 = Pathname.new(s1)
361
+ p2 = Pathname.new(s2)
362
+ r = p1 <=> p2
363
+ assert(e == r,
364
+ "#{p1.inspect} <=> #{p2.inspect}: <#{e}> expected but was <#{r}>")
365
+ end
366
+ def test_comparison
367
+ assert_pathname_cmp( 0, "a", "a")
368
+ assert_pathname_cmp( 1, "b", "a")
369
+ assert_pathname_cmp(-1, "a", "b")
370
+ ss = %w(
371
+ a
372
+ a/
373
+ a/b
374
+ a.
375
+ a0
376
+ )
377
+ s1 = ss.shift
378
+ ss.each {|s2|
379
+ assert_pathname_cmp(-1, s1, s2)
380
+ s1 = s2
381
+ }
382
+ end
383
+
384
+ def test_comparison_string
385
+ assert_equal(nil, Pathname.new("a") <=> "a")
386
+ assert_equal(nil, "a" <=> Pathname.new("a"))
387
+ end
388
+
389
+ def pathsub(path, pat, repl) Pathname.new(path).sub(pat, repl).to_s end
390
+ defassert(:pathsub, "a.o", "a.c", /\.c\z/, ".o")
391
+
392
+ def root?(path)
393
+ Pathname.new(path).root?
394
+ end
395
+
396
+ defassert(:root?, true, "/")
397
+ defassert(:root?, true, "//")
398
+ defassert(:root?, true, "///")
399
+ defassert(:root?, false, "")
400
+ defassert(:root?, false, "a")
401
+
402
+ def test_destructive_update
403
+ path = Pathname.new("a")
404
+ path.to_s.replace "b"
405
+ assert_equal(Pathname.new("a"), path)
406
+ end
407
+
408
+ def test_null_character
409
+ assert_raise(ArgumentError) { Pathname.new("\0") }
410
+ end
411
+
412
+ def test_taint
413
+ obj = Pathname.new("a"); assert_same(obj, obj.taint)
414
+ obj = Pathname.new("a"); assert_same(obj, obj.untaint)
415
+
416
+ assert_equal(false, Pathname.new("a" ) .tainted?)
417
+ assert_equal(false, Pathname.new("a" ) .to_s.tainted?)
418
+ assert_equal(true, Pathname.new("a" ).taint .tainted?)
419
+ assert_equal(true, Pathname.new("a" ).taint.to_s.tainted?)
420
+ assert_equal(true, Pathname.new("a".taint) .tainted?)
421
+ assert_equal(true, Pathname.new("a".taint) .to_s.tainted?)
422
+ assert_equal(true, Pathname.new("a".taint).taint .tainted?)
423
+ assert_equal(true, Pathname.new("a".taint).taint.to_s.tainted?)
424
+
425
+ str = "a"
426
+ path = Pathname.new(str)
427
+ str.taint
428
+ assert_equal(false, path .tainted?)
429
+ assert_equal(false, path.to_s.tainted?)
430
+ end
431
+
432
+ def test_untaint
433
+ obj = Pathname.new("a"); assert_same(obj, obj.untaint)
434
+
435
+ assert_equal(false, Pathname.new("a").taint.untaint .tainted?)
436
+ assert_equal(false, Pathname.new("a").taint.untaint.to_s.tainted?)
437
+
438
+ str = "a".taint
439
+ path = Pathname.new(str)
440
+ str.untaint
441
+ assert_equal(true, path .tainted?)
442
+ assert_equal(true, path.to_s.tainted?)
443
+ end
444
+
445
+ def test_freeze
446
+ obj = Pathname.new("a"); assert_same(obj, obj.freeze)
447
+
448
+ assert_equal(false, Pathname.new("a" ) .frozen?)
449
+ assert_equal(false, Pathname.new("a".freeze) .frozen?)
450
+ assert_equal(true, Pathname.new("a" ).freeze .frozen?)
451
+ assert_equal(true, Pathname.new("a".freeze).freeze .frozen?)
452
+ assert_equal(false, Pathname.new("a" ) .to_s.frozen?)
453
+ assert_equal(false, Pathname.new("a".freeze) .to_s.frozen?)
454
+ assert_equal(false, Pathname.new("a" ).freeze.to_s.frozen?)
455
+ assert_equal(false, Pathname.new("a".freeze).freeze.to_s.frozen?)
456
+ end
457
+
458
+ def test_to_s
459
+ str = "a"
460
+ obj = Pathname.new(str)
461
+ assert_equal(str, obj.to_s)
462
+ assert_not_same(str, obj.to_s)
463
+ assert_not_same(obj.to_s, obj.to_s)
464
+ end
465
+
466
+ def test_kernel_open
467
+ count = 0
468
+ result = Kernel.open(Pathname.new(__FILE__)) {|f|
469
+ assert(File.identical?(__FILE__, f))
470
+ count += 1
471
+ 2
472
+ }
473
+ assert_equal(1, count)
474
+ assert_equal(2, result)
475
+ end
476
+
477
+ def test_each_filename
478
+ result = []
479
+ Pathname.new("/usr/bin/ruby").each_filename {|f| result << f }
480
+ assert_equal(%w[usr bin ruby], result)
481
+ end
482
+
483
+ def test_kernel_pathname
484
+ assert_equal(Pathname.new("a"), Pathname("a"))
485
+ end
486
+ end