pathname 0.1.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: c42dd90f7949c457dd8225ff9f04ab3919a972ec4c4f41781784ee22e3957c47
4
+ data.tar.gz: 67971cc891a87efcadb29c62dea3bf429d74c5261711a83b8ccd45bbb2322028
5
+ SHA512:
6
+ metadata.gz: 0bc313f05de84a963277fb5814202b52c73dec674b195944f4255f80fc35b72a9dd00baea026826438524393baac08da00a723dcdc1fcf24bb49dff24a323be2
7
+ data.tar.gz: 1f4f1ec1ca815d7e857929ba474378d80b6e36ce5071fac4b3c1c12799a5a38eb951fc68361395274957c02ee67864dca30b8a2310d4521b65c51d97354b4788
@@ -0,0 +1,24 @@
1
+ name: build
2
+
3
+ on: [push, pull_request]
4
+
5
+ jobs:
6
+ build:
7
+ name: build (${{ matrix.ruby }} / ${{ matrix.os }})
8
+ strategy:
9
+ matrix:
10
+ ruby: [ 2.7, 2.6, 2.5, head ]
11
+ os: [ ubuntu-latest, macos-latest ]
12
+ runs-on: ${{ matrix.os }}
13
+ steps:
14
+ - uses: actions/checkout@master
15
+ - name: Set up Ruby
16
+ uses: ruby/setup-ruby@v1
17
+ with:
18
+ ruby-version: ${{ matrix.ruby }}
19
+ - name: Install dependencies
20
+ run: |
21
+ gem install bundler --no-document
22
+ bundle install
23
+ - name: Run test
24
+ run: rake compile test
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ gem "rake"
6
+ gem "rake-compiler"
@@ -0,0 +1,22 @@
1
+ Copyright (C) 1993-2013 Yukihiro Matsumoto. All rights reserved.
2
+
3
+ Redistribution and use in source and binary forms, with or without
4
+ modification, are permitted provided that the following conditions
5
+ are met:
6
+ 1. Redistributions of source code must retain the above copyright
7
+ notice, this list of conditions and the following disclaimer.
8
+ 2. Redistributions in binary form must reproduce the above copyright
9
+ notice, this list of conditions and the following disclaimer in the
10
+ documentation and/or other materials provided with the distribution.
11
+
12
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
13
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
15
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
16
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
18
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
19
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
20
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
21
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
22
+ SUCH DAMAGE.
@@ -0,0 +1,36 @@
1
+ # Pathname
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/pathname`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'pathname'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle install
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install pathname
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/hsbpathname.
36
+
@@ -0,0 +1,12 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test/lib"
6
+ t.test_files = FileList["test/**/test_*.rb"]
7
+ end
8
+
9
+ require 'rake/extensiontask'
10
+ Rake::ExtensionTask.new("pathname")
11
+
12
+ task :default => [:compile, :test]
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "pathname"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: false
2
+ require 'mkmf'
3
+ have_func("rb_file_s_birthtime")
4
+ create_makefile('pathname')
@@ -0,0 +1,599 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # = pathname.rb
4
+ #
5
+ # Object-Oriented Pathname Class
6
+ #
7
+ # Author:: Tanaka Akira <akr@m17n.org>
8
+ # Documentation:: Author and Gavin Sinclair
9
+ #
10
+ # For documentation, see class Pathname.
11
+ #
12
+
13
+ require 'pathname.so'
14
+
15
+ class Pathname
16
+
17
+ # :stopdoc:
18
+
19
+ # to_path is implemented so Pathname objects are usable with File.open, etc.
20
+ TO_PATH = :to_path
21
+
22
+ SAME_PATHS = if File::FNM_SYSCASE.nonzero?
23
+ # Avoid #zero? here because #casecmp can return nil.
24
+ proc {|a, b| a.casecmp(b) == 0}
25
+ else
26
+ proc {|a, b| a == b}
27
+ end
28
+
29
+
30
+ if File::ALT_SEPARATOR
31
+ SEPARATOR_LIST = "#{Regexp.quote File::ALT_SEPARATOR}#{Regexp.quote File::SEPARATOR}"
32
+ SEPARATOR_PAT = /[#{SEPARATOR_LIST}]/
33
+ else
34
+ SEPARATOR_LIST = "#{Regexp.quote File::SEPARATOR}"
35
+ SEPARATOR_PAT = /#{Regexp.quote File::SEPARATOR}/
36
+ end
37
+
38
+ if File.dirname('A:') == 'A:.' # DOSish drive letter
39
+ ABSOLUTE_PATH = /\A(?:[A-Za-z]:|#{SEPARATOR_PAT})/o
40
+ else
41
+ ABSOLUTE_PATH = /\A#{SEPARATOR_PAT}/o
42
+ end
43
+ private_constant :ABSOLUTE_PATH
44
+
45
+ # :startdoc:
46
+
47
+ # chop_basename(path) -> [pre-basename, basename] or nil
48
+ def chop_basename(path) # :nodoc:
49
+ base = File.basename(path)
50
+ if /\A#{SEPARATOR_PAT}?\z/o.match?(base)
51
+ return nil
52
+ else
53
+ return path[0, path.rindex(base)], base
54
+ end
55
+ end
56
+ private :chop_basename
57
+
58
+ # split_names(path) -> prefix, [name, ...]
59
+ def split_names(path) # :nodoc:
60
+ names = []
61
+ while r = chop_basename(path)
62
+ path, basename = r
63
+ names.unshift basename
64
+ end
65
+ return path, names
66
+ end
67
+ private :split_names
68
+
69
+ def prepend_prefix(prefix, relpath) # :nodoc:
70
+ if relpath.empty?
71
+ File.dirname(prefix)
72
+ elsif /#{SEPARATOR_PAT}/o.match?(prefix)
73
+ prefix = File.dirname(prefix)
74
+ prefix = File.join(prefix, "") if File.basename(prefix + 'a') != 'a'
75
+ prefix + relpath
76
+ else
77
+ prefix + relpath
78
+ end
79
+ end
80
+ private :prepend_prefix
81
+
82
+ # Returns clean pathname of +self+ with consecutive slashes and useless dots
83
+ # removed. The filesystem is not accessed.
84
+ #
85
+ # If +consider_symlink+ is +true+, then a more conservative algorithm is used
86
+ # to avoid breaking symbolic linkages. This may retain more +..+
87
+ # entries than absolutely necessary, but without accessing the filesystem,
88
+ # this can't be avoided.
89
+ #
90
+ # See Pathname#realpath.
91
+ #
92
+ def cleanpath(consider_symlink=false)
93
+ if consider_symlink
94
+ cleanpath_conservative
95
+ else
96
+ cleanpath_aggressive
97
+ end
98
+ end
99
+
100
+ #
101
+ # Clean the path simply by resolving and removing excess +.+ and +..+ entries.
102
+ # Nothing more, nothing less.
103
+ #
104
+ def cleanpath_aggressive # :nodoc:
105
+ path = @path
106
+ names = []
107
+ pre = path
108
+ while r = chop_basename(pre)
109
+ pre, base = r
110
+ case base
111
+ when '.'
112
+ when '..'
113
+ names.unshift base
114
+ else
115
+ if names[0] == '..'
116
+ names.shift
117
+ else
118
+ names.unshift base
119
+ end
120
+ end
121
+ end
122
+ pre.tr!(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR
123
+ if /#{SEPARATOR_PAT}/o.match?(File.basename(pre))
124
+ names.shift while names[0] == '..'
125
+ end
126
+ self.class.new(prepend_prefix(pre, File.join(*names)))
127
+ end
128
+ private :cleanpath_aggressive
129
+
130
+ # has_trailing_separator?(path) -> bool
131
+ def has_trailing_separator?(path) # :nodoc:
132
+ if r = chop_basename(path)
133
+ pre, basename = r
134
+ pre.length + basename.length < path.length
135
+ else
136
+ false
137
+ end
138
+ end
139
+ private :has_trailing_separator?
140
+
141
+ # add_trailing_separator(path) -> path
142
+ def add_trailing_separator(path) # :nodoc:
143
+ if File.basename(path + 'a') == 'a'
144
+ path
145
+ else
146
+ File.join(path, "") # xxx: Is File.join is appropriate to add separator?
147
+ end
148
+ end
149
+ private :add_trailing_separator
150
+
151
+ def del_trailing_separator(path) # :nodoc:
152
+ if r = chop_basename(path)
153
+ pre, basename = r
154
+ pre + basename
155
+ elsif /#{SEPARATOR_PAT}+\z/o =~ path
156
+ $` + File.dirname(path)[/#{SEPARATOR_PAT}*\z/o]
157
+ else
158
+ path
159
+ end
160
+ end
161
+ private :del_trailing_separator
162
+
163
+ def cleanpath_conservative # :nodoc:
164
+ path = @path
165
+ names = []
166
+ pre = path
167
+ while r = chop_basename(pre)
168
+ pre, base = r
169
+ names.unshift base if base != '.'
170
+ end
171
+ pre.tr!(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR
172
+ if /#{SEPARATOR_PAT}/o.match?(File.basename(pre))
173
+ names.shift while names[0] == '..'
174
+ end
175
+ if names.empty?
176
+ self.class.new(File.dirname(pre))
177
+ else
178
+ if names.last != '..' && File.basename(path) == '.'
179
+ names << '.'
180
+ end
181
+ result = prepend_prefix(pre, File.join(*names))
182
+ if /\A(?:\.|\.\.)\z/ !~ names.last && has_trailing_separator?(path)
183
+ self.class.new(add_trailing_separator(result))
184
+ else
185
+ self.class.new(result)
186
+ end
187
+ end
188
+ end
189
+ private :cleanpath_conservative
190
+
191
+ # Returns the parent directory.
192
+ #
193
+ # This is same as <code>self + '..'</code>.
194
+ def parent
195
+ self + '..'
196
+ end
197
+
198
+ # Returns +true+ if +self+ points to a mountpoint.
199
+ def mountpoint?
200
+ begin
201
+ stat1 = self.lstat
202
+ stat2 = self.parent.lstat
203
+ stat1.dev != stat2.dev || stat1.ino == stat2.ino
204
+ rescue Errno::ENOENT
205
+ false
206
+ end
207
+ end
208
+
209
+ #
210
+ # Predicate method for root directories. Returns +true+ if the
211
+ # pathname consists of consecutive slashes.
212
+ #
213
+ # It doesn't access the filesystem. So it may return +false+ for some
214
+ # pathnames which points to roots such as <tt>/usr/..</tt>.
215
+ #
216
+ def root?
217
+ chop_basename(@path) == nil && /#{SEPARATOR_PAT}/o.match?(@path)
218
+ end
219
+
220
+ # Predicate method for testing whether a path is absolute.
221
+ #
222
+ # It returns +true+ if the pathname begins with a slash.
223
+ #
224
+ # p = Pathname.new('/im/sure')
225
+ # p.absolute?
226
+ # #=> true
227
+ #
228
+ # p = Pathname.new('not/so/sure')
229
+ # p.absolute?
230
+ # #=> false
231
+ def absolute?
232
+ ABSOLUTE_PATH.match? @path
233
+ end
234
+
235
+ # The opposite of Pathname#absolute?
236
+ #
237
+ # It returns +false+ if the pathname begins with a slash.
238
+ #
239
+ # p = Pathname.new('/im/sure')
240
+ # p.relative?
241
+ # #=> false
242
+ #
243
+ # p = Pathname.new('not/so/sure')
244
+ # p.relative?
245
+ # #=> true
246
+ def relative?
247
+ !absolute?
248
+ end
249
+
250
+ #
251
+ # Iterates over each component of the path.
252
+ #
253
+ # Pathname.new("/usr/bin/ruby").each_filename {|filename| ... }
254
+ # # yields "usr", "bin", and "ruby".
255
+ #
256
+ # Returns an Enumerator if no block was given.
257
+ #
258
+ # enum = Pathname.new("/usr/bin/ruby").each_filename
259
+ # # ... do stuff ...
260
+ # enum.each { |e| ... }
261
+ # # yields "usr", "bin", and "ruby".
262
+ #
263
+ def each_filename # :yield: filename
264
+ return to_enum(__method__) unless block_given?
265
+ _, names = split_names(@path)
266
+ names.each {|filename| yield filename }
267
+ nil
268
+ end
269
+
270
+ # Iterates over and yields a new Pathname object
271
+ # for each element in the given path in descending order.
272
+ #
273
+ # Pathname.new('/path/to/some/file.rb').descend {|v| p v}
274
+ # #<Pathname:/>
275
+ # #<Pathname:/path>
276
+ # #<Pathname:/path/to>
277
+ # #<Pathname:/path/to/some>
278
+ # #<Pathname:/path/to/some/file.rb>
279
+ #
280
+ # Pathname.new('path/to/some/file.rb').descend {|v| p v}
281
+ # #<Pathname:path>
282
+ # #<Pathname:path/to>
283
+ # #<Pathname:path/to/some>
284
+ # #<Pathname:path/to/some/file.rb>
285
+ #
286
+ # Returns an Enumerator if no block was given.
287
+ #
288
+ # enum = Pathname.new("/usr/bin/ruby").descend
289
+ # # ... do stuff ...
290
+ # enum.each { |e| ... }
291
+ # # yields Pathnames /, /usr, /usr/bin, and /usr/bin/ruby.
292
+ #
293
+ # It doesn't access the filesystem.
294
+ #
295
+ def descend
296
+ return to_enum(__method__) unless block_given?
297
+ vs = []
298
+ ascend {|v| vs << v }
299
+ vs.reverse_each {|v| yield v }
300
+ nil
301
+ end
302
+
303
+ # Iterates over and yields a new Pathname object
304
+ # for each element in the given path in ascending order.
305
+ #
306
+ # Pathname.new('/path/to/some/file.rb').ascend {|v| p v}
307
+ # #<Pathname:/path/to/some/file.rb>
308
+ # #<Pathname:/path/to/some>
309
+ # #<Pathname:/path/to>
310
+ # #<Pathname:/path>
311
+ # #<Pathname:/>
312
+ #
313
+ # Pathname.new('path/to/some/file.rb').ascend {|v| p v}
314
+ # #<Pathname:path/to/some/file.rb>
315
+ # #<Pathname:path/to/some>
316
+ # #<Pathname:path/to>
317
+ # #<Pathname:path>
318
+ #
319
+ # Returns an Enumerator if no block was given.
320
+ #
321
+ # enum = Pathname.new("/usr/bin/ruby").ascend
322
+ # # ... do stuff ...
323
+ # enum.each { |e| ... }
324
+ # # yields Pathnames /usr/bin/ruby, /usr/bin, /usr, and /.
325
+ #
326
+ # It doesn't access the filesystem.
327
+ #
328
+ def ascend
329
+ return to_enum(__method__) unless block_given?
330
+ path = @path
331
+ yield self
332
+ while r = chop_basename(path)
333
+ path, = r
334
+ break if path.empty?
335
+ yield self.class.new(del_trailing_separator(path))
336
+ end
337
+ end
338
+
339
+ #
340
+ # Appends a pathname fragment to +self+ to produce a new Pathname object.
341
+ #
342
+ # p1 = Pathname.new("/usr") # Pathname:/usr
343
+ # p2 = p1 + "bin/ruby" # Pathname:/usr/bin/ruby
344
+ # p3 = p1 + "/etc/passwd" # Pathname:/etc/passwd
345
+ #
346
+ # # / is aliased to +.
347
+ # p4 = p1 / "bin/ruby" # Pathname:/usr/bin/ruby
348
+ # p5 = p1 / "/etc/passwd" # Pathname:/etc/passwd
349
+ #
350
+ # This method doesn't access the file system; it is pure string manipulation.
351
+ #
352
+ def +(other)
353
+ other = Pathname.new(other) unless Pathname === other
354
+ Pathname.new(plus(@path, other.to_s))
355
+ end
356
+ alias / +
357
+
358
+ def plus(path1, path2) # -> path # :nodoc:
359
+ prefix2 = path2
360
+ index_list2 = []
361
+ basename_list2 = []
362
+ while r2 = chop_basename(prefix2)
363
+ prefix2, basename2 = r2
364
+ index_list2.unshift prefix2.length
365
+ basename_list2.unshift basename2
366
+ end
367
+ return path2 if prefix2 != ''
368
+ prefix1 = path1
369
+ while true
370
+ while !basename_list2.empty? && basename_list2.first == '.'
371
+ index_list2.shift
372
+ basename_list2.shift
373
+ end
374
+ break unless r1 = chop_basename(prefix1)
375
+ prefix1, basename1 = r1
376
+ next if basename1 == '.'
377
+ if basename1 == '..' || basename_list2.empty? || basename_list2.first != '..'
378
+ prefix1 = prefix1 + basename1
379
+ break
380
+ end
381
+ index_list2.shift
382
+ basename_list2.shift
383
+ end
384
+ r1 = chop_basename(prefix1)
385
+ if !r1 && (r1 = /#{SEPARATOR_PAT}/o.match?(File.basename(prefix1)))
386
+ while !basename_list2.empty? && basename_list2.first == '..'
387
+ index_list2.shift
388
+ basename_list2.shift
389
+ end
390
+ end
391
+ if !basename_list2.empty?
392
+ suffix2 = path2[index_list2.first..-1]
393
+ r1 ? File.join(prefix1, suffix2) : prefix1 + suffix2
394
+ else
395
+ r1 ? prefix1 : File.dirname(prefix1)
396
+ end
397
+ end
398
+ private :plus
399
+
400
+ #
401
+ # Joins the given pathnames onto +self+ to create a new Pathname object.
402
+ #
403
+ # path0 = Pathname.new("/usr") # Pathname:/usr
404
+ # path0 = path0.join("bin/ruby") # Pathname:/usr/bin/ruby
405
+ # # is the same as
406
+ # path1 = Pathname.new("/usr") + "bin/ruby" # Pathname:/usr/bin/ruby
407
+ # path0 == path1
408
+ # #=> true
409
+ #
410
+ def join(*args)
411
+ return self if args.empty?
412
+ result = args.pop
413
+ result = Pathname.new(result) unless Pathname === result
414
+ return result if result.absolute?
415
+ args.reverse_each {|arg|
416
+ arg = Pathname.new(arg) unless Pathname === arg
417
+ result = arg + result
418
+ return result if result.absolute?
419
+ }
420
+ self + result
421
+ end
422
+
423
+ #
424
+ # Returns the children of the directory (files and subdirectories, not
425
+ # recursive) as an array of Pathname objects.
426
+ #
427
+ # By default, the returned pathnames will have enough information to access
428
+ # the files. If you set +with_directory+ to +false+, then the returned
429
+ # pathnames will contain the filename only.
430
+ #
431
+ # For example:
432
+ # pn = Pathname("/usr/lib/ruby/1.8")
433
+ # pn.children
434
+ # # -> [ Pathname:/usr/lib/ruby/1.8/English.rb,
435
+ # Pathname:/usr/lib/ruby/1.8/Env.rb,
436
+ # Pathname:/usr/lib/ruby/1.8/abbrev.rb, ... ]
437
+ # pn.children(false)
438
+ # # -> [ Pathname:English.rb, Pathname:Env.rb, Pathname:abbrev.rb, ... ]
439
+ #
440
+ # Note that the results never contain the entries +.+ and +..+ in
441
+ # the directory because they are not children.
442
+ #
443
+ def children(with_directory=true)
444
+ with_directory = false if @path == '.'
445
+ result = []
446
+ Dir.foreach(@path) {|e|
447
+ next if e == '.' || e == '..'
448
+ if with_directory
449
+ result << self.class.new(File.join(@path, e))
450
+ else
451
+ result << self.class.new(e)
452
+ end
453
+ }
454
+ result
455
+ end
456
+
457
+ # Iterates over the children of the directory
458
+ # (files and subdirectories, not recursive).
459
+ #
460
+ # It yields Pathname object for each child.
461
+ #
462
+ # By default, the yielded pathnames will have enough information to access
463
+ # the files.
464
+ #
465
+ # If you set +with_directory+ to +false+, then the returned pathnames will
466
+ # contain the filename only.
467
+ #
468
+ # Pathname("/usr/local").each_child {|f| p f }
469
+ # #=> #<Pathname:/usr/local/share>
470
+ # # #<Pathname:/usr/local/bin>
471
+ # # #<Pathname:/usr/local/games>
472
+ # # #<Pathname:/usr/local/lib>
473
+ # # #<Pathname:/usr/local/include>
474
+ # # #<Pathname:/usr/local/sbin>
475
+ # # #<Pathname:/usr/local/src>
476
+ # # #<Pathname:/usr/local/man>
477
+ #
478
+ # Pathname("/usr/local").each_child(false) {|f| p f }
479
+ # #=> #<Pathname:share>
480
+ # # #<Pathname:bin>
481
+ # # #<Pathname:games>
482
+ # # #<Pathname:lib>
483
+ # # #<Pathname:include>
484
+ # # #<Pathname:sbin>
485
+ # # #<Pathname:src>
486
+ # # #<Pathname:man>
487
+ #
488
+ # Note that the results never contain the entries +.+ and +..+ in
489
+ # the directory because they are not children.
490
+ #
491
+ # See Pathname#children
492
+ #
493
+ def each_child(with_directory=true, &b)
494
+ children(with_directory).each(&b)
495
+ end
496
+
497
+ #
498
+ # Returns a relative path from the given +base_directory+ to the receiver.
499
+ #
500
+ # If +self+ is absolute, then +base_directory+ must be absolute too.
501
+ #
502
+ # If +self+ is relative, then +base_directory+ must be relative too.
503
+ #
504
+ # This method doesn't access the filesystem. It assumes no symlinks.
505
+ #
506
+ # ArgumentError is raised when it cannot find a relative path.
507
+ #
508
+ # Note that this method does not handle situations where the case sensitivity
509
+ # of the filesystem in use differs from the operating system default.
510
+ #
511
+ def relative_path_from(base_directory)
512
+ base_directory = Pathname.new(base_directory) unless base_directory.is_a? Pathname
513
+ dest_directory = self.cleanpath.to_s
514
+ base_directory = base_directory.cleanpath.to_s
515
+ dest_prefix = dest_directory
516
+ dest_names = []
517
+ while r = chop_basename(dest_prefix)
518
+ dest_prefix, basename = r
519
+ dest_names.unshift basename if basename != '.'
520
+ end
521
+ base_prefix = base_directory
522
+ base_names = []
523
+ while r = chop_basename(base_prefix)
524
+ base_prefix, basename = r
525
+ base_names.unshift basename if basename != '.'
526
+ end
527
+ unless SAME_PATHS[dest_prefix, base_prefix]
528
+ raise ArgumentError, "different prefix: #{dest_prefix.inspect} and #{base_directory.inspect}"
529
+ end
530
+ while !dest_names.empty? &&
531
+ !base_names.empty? &&
532
+ SAME_PATHS[dest_names.first, base_names.first]
533
+ dest_names.shift
534
+ base_names.shift
535
+ end
536
+ if base_names.include? '..'
537
+ raise ArgumentError, "base_directory has ..: #{base_directory.inspect}"
538
+ end
539
+ base_names.fill('..')
540
+ relpath_names = base_names + dest_names
541
+ if relpath_names.empty?
542
+ Pathname.new('.')
543
+ else
544
+ Pathname.new(File.join(*relpath_names))
545
+ end
546
+ end
547
+ end
548
+
549
+
550
+ class Pathname # * Find *
551
+ #
552
+ # Iterates over the directory tree in a depth first manner, yielding a
553
+ # Pathname for each file under "this" directory.
554
+ #
555
+ # Returns an Enumerator if no block is given.
556
+ #
557
+ # Since it is implemented by the standard library module Find, Find.prune can
558
+ # be used to control the traversal.
559
+ #
560
+ # If +self+ is +.+, yielded pathnames begin with a filename in the
561
+ # current directory, not +./+.
562
+ #
563
+ # See Find.find
564
+ #
565
+ def find(ignore_error: true) # :yield: pathname
566
+ return to_enum(__method__, ignore_error: ignore_error) unless block_given?
567
+ require 'find'
568
+ if @path == '.'
569
+ Find.find(@path, ignore_error: ignore_error) {|f| yield self.class.new(f.sub(%r{\A\./}, '')) }
570
+ else
571
+ Find.find(@path, ignore_error: ignore_error) {|f| yield self.class.new(f) }
572
+ end
573
+ end
574
+ end
575
+
576
+
577
+ class Pathname # * FileUtils *
578
+ # Creates a full path, including any intermediate directories that don't yet
579
+ # exist.
580
+ #
581
+ # See FileUtils.mkpath and FileUtils.mkdir_p
582
+ def mkpath
583
+ require 'fileutils'
584
+ FileUtils.mkpath(@path)
585
+ nil
586
+ end
587
+
588
+ # Recursively deletes a directory, including all directories beneath it.
589
+ #
590
+ # See FileUtils.rm_r
591
+ def rmtree
592
+ # The name "rmtree" is borrowed from File::Path of Perl.
593
+ # File::Path provides "mkpath" and "rmtree".
594
+ require 'fileutils'
595
+ FileUtils.rm_r(@path)
596
+ nil
597
+ end
598
+ end
599
+