| Class | Pathname |
| In: |
lib/pathname.rb
|
| Parent: | Object |
Pathname represents a pathname which locates a file in a filesystem. The pathname depends on OS: Unix, Windows, etc. Pathname library works with pathnames of local OS. However non-Unix pathnames are supported experimentally.
It does not represent the file itself. A Pathname can be relative or absolute. It‘s not until you try to reference the file that it even matters whether the file exists or not.
Pathname is immutable. It has no method for destructive update.
The value of this class is to manipulate file path information in a neater way than standard Ruby provides. The examples below demonstrate the difference. All functionality from File, FileTest, and some from Dir and FileUtils is included, in an unsurprising way. It is essentially a facade for all of these, and more.
require 'pathname'
p = Pathname.new("/usr/bin/ruby")
size = p.size # 27662
isdir = p.directory? # false
dir = p.dirname # Pathname:/usr/bin
base = p.basename # Pathname:ruby
dir, base = p.split # [Pathname:/usr/bin, Pathname:ruby]
data = p.read
p.open { |f| _ }
p.each_line { |line| _ }
p = "/usr/bin/ruby"
size = File.size(p) # 27662
isdir = File.directory?(p) # false
dir = File.dirname(p) # "/usr/bin"
base = File.basename(p) # "ruby"
dir, base = File.split(p) # ["/usr/bin", "ruby"]
data = File.read(p)
File.open(p) { |f| _ }
File.foreach(p) { |line| _ }
p1 = Pathname.new("/usr/lib") # Pathname:/usr/lib
p2 = p1 + "ruby/1.8" # Pathname:/usr/lib/ruby/1.8
p3 = p1.parent # Pathname:/usr
p4 = p2.relative_path_from(p3) # Pathname:lib/ruby/1.8
pwd = Pathname.pwd # Pathname:/home/gavin
pwd.absolute? # true
p5 = Pathname.new "." # Pathname:.
p5 = p5 + "music/../articles" # Pathname:music/../articles
p5.cleanpath # Pathname:articles
p5.realpath # Pathname:/home/gavin/articles
p5.children # [Pathname:/home/gavin/articles/linux, ...]
These methods are effectively manipulating a String, because that‘s all a path is. Except for mountpoint?, children, and realpath, they don‘t access the filesystem.
These methods are a facade for FileTest:
These methods are a facade for File:
These methods are a facade for Dir:
These methods are a facade for IO:
These methods are a mixture of Find, FileUtils, and others:
As the above section shows, most of the methods in Pathname are facades. The documentation for these methods generally just says, for instance, "See FileTest.writable?", as you should be familiar with the original method anyway, and its documentation (e.g. through ri) will contain more information. In some cases, a brief description will follow.
| SEPARATOR_PAT | = | /[#{Regexp.quote File::ALT_SEPARATOR}#{Regexp.quote File::SEPARATOR}]/ |
| SEPARATOR_PAT | = | /#{Regexp.quote File::SEPARATOR}/ |
| getwd | -> | pwd |
Create a Pathname object from the given String (or String-like object). If path contains a NUL character (\0), an ArgumentError is raised.
# File lib/pathname.rb, line 203
203: def initialize(path)
204: path = path.__send__(TO_PATH) if path.respond_to? TO_PATH
205: @path = path.dup
206:
207: if /\0/ =~ @path
208: raise ArgumentError, "pathname contains \\0: #{@path.inspect}"
209: end
210:
211: self.taint if @path.tainted?
212: end
Pathname#+ appends a pathname fragment to this one to produce a new Pathname object.
p1 = Pathname.new("/usr") # Pathname:/usr
p2 = p1 + "bin/ruby" # Pathname:/usr/bin/ruby
p3 = p1 + "/etc/passwd" # Pathname:/etc/passwd
This method doesn‘t access the file system; it is pure string manipulation.
# File lib/pathname.rb, line 595
595: def +(other)
596: other = Pathname.new(other) unless Pathname === other
597: Pathname.new(plus(@path, other.to_s))
598: end
Provides for comparing pathnames, case-sensitively.
# File lib/pathname.rb, line 231
231: def <=>(other)
232: return nil unless Pathname === other
233: @path.tr('/', "\0") <=> other.to_s.tr('/', "\0")
234: end
Compare this pathname with other. The comparison is string-based. Be aware that two different paths (foo.txt and ./foo.txt) can refer to the same file.
# File lib/pathname.rb, line 223
223: def ==(other)
224: return false unless Pathname === other
225: other.to_s == @path
226: end
Predicate method for testing whether a path is absolute. It returns true if the pathname begins with a slash.
# File lib/pathname.rb, line 503
503: def absolute?
504: !relative?
505: end
Iterates over and yields a new Pathname object for each element in the given path in ascending order.
Pathname.new('/path/to/some/file.rb').ascend {|v| p v}
#<Pathname:/path/to/some/file.rb>
#<Pathname:/path/to/some>
#<Pathname:/path/to>
#<Pathname:/path>
#<Pathname:/>
Pathname.new('path/to/some/file.rb').ascend {|v| p v}
#<Pathname:path/to/some/file.rb>
#<Pathname:path/to/some>
#<Pathname:path/to>
#<Pathname:path>
It doesn‘t access actual filesystem.
This method is available since 1.8.5.
# File lib/pathname.rb, line 575
575: def ascend
576: path = @path
577: yield self
578: while r = chop_basename(path)
579: path, name = r
580: break if path.empty?
581: yield self.class.new(del_trailing_separator(path))
582: end
583: end
See File.atime. Returns last access time.
# File lib/pathname.rb, line 776
776: def atime() File.atime(@path) end
See File.basename. Returns the last component of the path.
# File lib/pathname.rb, line 837
837: def basename(*args) self.class.new(File.basename(@path, *args)) end
See FileTest.blockdev?.
# File lib/pathname.rb, line 871
871: def blockdev?() FileTest.blockdev?(@path) end
See FileTest.chardev?.
# File lib/pathname.rb, line 874
874: def chardev?() FileTest.chardev?(@path) end
Pathname#chdir is obsoleted at 1.8.1.
# File lib/pathname.rb, line 959
959: def chdir(&block)
960: warn "Pathname#chdir is obsoleted. Use Dir.chdir."
961: Dir.chdir(@path, &block)
962: end
Returns the children of the directory (files and subdirectories, not recursive) as an array of Pathname objects. By default, the returned pathnames will have enough information to access the files. If you set with_directory to false, then the returned pathnames will contain the filename only.
For example:
p = Pathname("/usr/lib/ruby/1.8")
p.children
# -> [ Pathname:/usr/lib/ruby/1.8/English.rb,
Pathname:/usr/lib/ruby/1.8/Env.rb,
Pathname:/usr/lib/ruby/1.8/abbrev.rb, ... ]
p.children(false)
# -> [ Pathname:English.rb, Pathname:Env.rb, Pathname:abbrev.rb, ... ]
Note that the result never contain the entries . and .. in the directory because they are not children.
This method has existed since 1.8.1.
# File lib/pathname.rb, line 682
682: def children(with_directory=true)
683: with_directory = false if @path == '.'
684: result = []
685: Dir.foreach(@path) {|e|
686: next if e == '.' || e == '..'
687: if with_directory
688: result << self.class.new(File.join(@path, e))
689: else
690: result << self.class.new(e)
691: end
692: }
693: result
694: end
See File.chmod. Changes permissions.
# File lib/pathname.rb, line 785
785: def chmod(mode) File.chmod(mode, @path) end
See File.chown. Change owner and group of file.
# File lib/pathname.rb, line 791
791: def chown(owner, group) File.chown(owner, group, @path) end
Pathname#chroot is obsoleted at 1.8.1.
# File lib/pathname.rb, line 965
965: def chroot
966: warn "Pathname#chroot is obsoleted. Use Dir.chroot."
967: Dir.chroot(@path)
968: end
Returns clean pathname of self with consecutive slashes and useless dots removed. The filesystem is not accessed.
If consider_symlink is true, then a more conservative algorithm is used to avoid breaking symbolic linkages. This may retain more .. entries than absolutely necessary, but without accessing the filesystem, this can‘t be avoided. See realpath.
# File lib/pathname.rb, line 320
320: def cleanpath(consider_symlink=false)
321: if consider_symlink
322: cleanpath_conservative
323: else
324: cleanpath_aggressive
325: end
326: end
See File.ctime. Returns last (directory entry, not file) change time.
# File lib/pathname.rb, line 779
779: def ctime() File.ctime(@path) end
Iterates over and yields a new Pathname object for each element in the given path in descending order.
Pathname.new('/path/to/some/file.rb').descend {|v| p v}
#<Pathname:/>
#<Pathname:/path>
#<Pathname:/path/to>
#<Pathname:/path/to/some>
#<Pathname:/path/to/some/file.rb>
Pathname.new('path/to/some/file.rb').descend {|v| p v}
#<Pathname:path>
#<Pathname:path/to>
#<Pathname:path/to/some>
#<Pathname:path/to/some/file.rb>
It doesn‘t access actual filesystem.
This method is available since 1.8.5.
# File lib/pathname.rb, line 548
548: def descend
549: vs = []
550: ascend {|v| vs << v }
551: vs.reverse_each {|v| yield v }
552: nil
553: end
Pathname#dir_foreach is obsoleted at 1.8.1.
# File lib/pathname.rb, line 983
983: def dir_foreach(*args, &block)
984: warn "Pathname#dir_foreach is obsoleted. Use Pathname#each_entry."
985: each_entry(*args, &block)
986: end
See FileTest.directory?.
# File lib/pathname.rb, line 889
889: def directory?() FileTest.directory?(@path) end
See File.dirname. Returns all but the last component of the path.
# File lib/pathname.rb, line 840
840: def dirname() self.class.new(File.dirname(@path)) end
Iterates over each component of the path.
Pathname.new("/usr/bin/ruby").each_filename {|filename| ... }
# yields "usr", "bin", and "ruby".
# File lib/pathname.rb, line 522
522: def each_filename # :yield: filename
523: prefix, names = split_names(@path)
524: names.each {|filename| yield filename }
525: nil
526: end
See FileTest.executable?.
# File lib/pathname.rb, line 877
877: def executable?() FileTest.executable?(@path) end
See FileTest.executable_real?.
# File lib/pathname.rb, line 880
880: def executable_real?() FileTest.executable_real?(@path) end
See File.expand_path.
# File lib/pathname.rb, line 846
846: def expand_path(*args) self.class.new(File.expand_path(@path, *args)) end
See File.extname. Returns the file‘s extension.
# File lib/pathname.rb, line 843
843: def extname() File.extname(@path) end
Pathname#find is an iterator to traverse a directory tree in a depth first manner. It yields a Pathname for each file under "this" directory.
Since it is implemented by find.rb, Find.prune can be used to control the traverse.
If self is ., yielded pathnames begin with a filename in the current directory, not ./.
# File lib/pathname.rb, line 1012
1012: def find(&block) # :yield: p
1013: require 'find'
1014: if @path == '.'
1015: Find.find(@path) {|f| yield self.class.new(f.sub(%r{\A\./}, '')) }
1016: else
1017: Find.find(@path) {|f| yield self.class.new(f) }
1018: end
1019: end
See File.fnmatch. Return true if the receiver matches the given pattern.
# File lib/pathname.rb, line 798
798: def fnmatch(pattern, *args) File.fnmatch(pattern, @path, *args) end
See File.fnmatch? (same as fnmatch).
# File lib/pathname.rb, line 801
801: def fnmatch?(pattern, *args) File.fnmatch?(pattern, @path, *args) end
This method is obsoleted at 1.8.1. Use each_line or each_entry.
# File lib/pathname.rb, line 1056
1056: def foreach(*args, &block)
1057: warn "Pathname#foreach is obsoleted. Use each_line or each_entry."
1058: if FileTest.directory? @path
1059: # For polymorphism between Dir.foreach and IO.foreach,
1060: # Pathname#foreach doesn't yield Pathname object.
1061: Dir.foreach(@path, *args, &block)
1062: else
1063: IO.foreach(@path, *args, &block)
1064: end
1065: end
Pathname#foreachline is obsoleted at 1.8.1. Use each_line.
# File lib/pathname.rb, line 756
756: def foreachline(*args, &block)
757: warn "Pathname#foreachline is obsoleted. Use Pathname#each_line."
758: each_line(*args, &block)
759: end
See File.ftype. Returns "type" of file ("file", "directory", etc).
# File lib/pathname.rb, line 805
805: def ftype() File.ftype(@path) end
See FileTest.grpowned?.
# File lib/pathname.rb, line 886
886: def grpowned?() FileTest.grpowned?(@path) end
Pathname#join joins pathnames.
path0.join(path1, …, pathN) is the same as path0 + path1 + … + pathN.
# File lib/pathname.rb, line 648
648: def join(*args)
649: args.unshift self
650: result = args.pop
651: result = Pathname.new(result) unless Pathname === result
652: return result if result.absolute?
653: args.reverse_each {|arg|
654: arg = Pathname.new(arg) unless Pathname === arg
655: result = arg + result
656: return result if result.absolute?
657: }
658: result
659: end
See File.lchmod.
# File lib/pathname.rb, line 788
788: def lchmod(mode) File.lchmod(mode, @path) end
See File.lchown.
# File lib/pathname.rb, line 794
794: def lchown(owner, group) File.lchown(owner, group, @path) end
Pathname#link is confusing and obsoleted because the receiver/argument order is inverted to corresponding system call.
# File lib/pathname.rb, line 854
854: def link(old)
855: warn 'Pathname#link is obsoleted. Use Pathname#make_link.'
856: File.link(old, @path)
857: end
See File.symlink. Creates a symbolic link.
# File lib/pathname.rb, line 828
828: def make_symlink(old) File.symlink(old, @path) end
See FileUtils.mkpath. Creates a full path, including any intermediate directories that don‘t yet exist.
# File lib/pathname.rb, line 1026
1026: def mkpath
1027: require 'fileutils'
1028: FileUtils.mkpath(@path)
1029: nil
1030: end
mountpoint? returns true if self points to a mountpoint.
# File lib/pathname.rb, line 479
479: def mountpoint?
480: begin
481: stat1 = self.lstat
482: stat2 = self.parent.lstat
483: stat1.dev == stat2.dev && stat1.ino == stat2.ino ||
484: stat1.dev != stat2.dev
485: rescue Errno::ENOENT
486: false
487: end
488: end
See File.mtime. Returns last modification time.
# File lib/pathname.rb, line 782
782: def mtime() File.mtime(@path) end
See File.open. Opens the file for reading or writing.
# File lib/pathname.rb, line 811
811: def open(*args, &block) # :yield: file
812: File.open(@path, *args, &block)
813: end
See FileTest.readable?.
# File lib/pathname.rb, line 904
904: def readable?() FileTest.readable?(@path) end
See FileTest.readable_real?.
# File lib/pathname.rb, line 910
910: def readable_real?() FileTest.readable_real?(@path) end
See IO.readlines. Returns all the lines from the file.
# File lib/pathname.rb, line 766
766: def readlines(*args) IO.readlines(@path, *args) end
See File.readlink. Read symbolic link.
# File lib/pathname.rb, line 816
816: def readlink() self.class.new(File.readlink(@path)) end
Returns a real (absolute) pathname of self in the actual filesystem. The real pathname doesn‘t contain symlinks or useless dots.
No arguments should be given; the old behaviour is obsoleted.
# File lib/pathname.rb, line 460
460: def realpath
461: path = @path
462: prefix, names = split_names(path)
463: if prefix == ''
464: prefix, names2 = split_names(Dir.pwd)
465: names = names2 + names
466: end
467: prefix, *names = realpath_rec(prefix, names, {})
468: self.class.new(prepend_prefix(prefix, File.join(*names)))
469: end
The opposite of absolute?
# File lib/pathname.rb, line 508
508: def relative?
509: path = @path
510: while r = chop_basename(path)
511: path, basename = r
512: end
513: path == ''
514: end
relative_path_from returns a relative path from the argument to the receiver. If self is absolute, the argument must be absolute too. If self is relative, the argument must be relative too.
relative_path_from doesn‘t access the filesystem. It assumes no symlinks.
ArgumentError is raised when it cannot find a relative path.
This method has existed since 1.8.1.
# File lib/pathname.rb, line 707
707: def relative_path_from(base_directory)
708: dest_directory = self.cleanpath.to_s
709: base_directory = base_directory.cleanpath.to_s
710: dest_prefix = dest_directory
711: dest_names = []
712: while r = chop_basename(dest_prefix)
713: dest_prefix, basename = r
714: dest_names.unshift basename if basename != '.'
715: end
716: base_prefix = base_directory
717: base_names = []
718: while r = chop_basename(base_prefix)
719: base_prefix, basename = r
720: base_names.unshift basename if basename != '.'
721: end
722: if dest_prefix != base_prefix
723: raise ArgumentError, "different prefix: #{dest_prefix.inspect} and #{base_directory.inspect}"
724: end
725: while !dest_names.empty? &&
726: !base_names.empty? &&
727: dest_names.first == base_names.first
728: dest_names.shift
729: base_names.shift
730: end
731: if base_names.include? '..'
732: raise ArgumentError, "base_directory has ..: #{base_directory.inspect}"
733: end
734: base_names.fill('..')
735: relpath_names = base_names + dest_names
736: if relpath_names.empty?
737: Pathname.new('.')
738: else
739: Pathname.new(File.join(*relpath_names))
740: end
741: end
See File.rename. Rename the file.
# File lib/pathname.rb, line 819
819: def rename(to) File.rename(@path, to) end
See FileUtils.rm_r. Deletes a directory and all beneath it.
# File lib/pathname.rb, line 1033
1033: def rmtree
1034: # The name "rmtree" is borrowed from File::Path of Perl.
1035: # File::Path provides "mkpath" and "rmtree".
1036: require 'fileutils'
1037: FileUtils.rm_r(@path)
1038: nil
1039: end
root? is a predicate for root directories. I.e. it returns true if the pathname consists of consecutive slashes.
It doesn‘t access actual filesystem. So it may return false for some pathnames which points to roots such as /usr/...
# File lib/pathname.rb, line 497
497: def root?
498: !!(chop_basename(@path) == nil && /#{SEPARATOR_PAT}/o =~ @path)
499: end
See FileTest.setgid?.
# File lib/pathname.rb, line 916
916: def setgid?() FileTest.setgid?(@path) end
See FileTest.setuid?.
# File lib/pathname.rb, line 913
913: def setuid?() FileTest.setuid?(@path) end
See FileTest.size.
# File lib/pathname.rb, line 919
919: def size() FileTest.size(@path) end
See FileTest.size?.
# File lib/pathname.rb, line 922
922: def size?() FileTest.size?(@path) end
See FileTest.socket?.
# File lib/pathname.rb, line 898
898: def socket?() FileTest.socket?(@path) end
See File.split. Returns the dirname and the basename in an Array.
# File lib/pathname.rb, line 850
850: def split() File.split(@path).map {|f| self.class.new(f) } end
See File.stat. Returns a File::Stat object.
# File lib/pathname.rb, line 822
822: def stat() File.stat(@path) end
See FileTest.sticky?.
# File lib/pathname.rb, line 925
925: def sticky?() FileTest.sticky?(@path) end
Return a pathname which is substituted by String#sub.
# File lib/pathname.rb, line 253
253: def sub(pattern, *rest, &block)
254: if block
255: path = @path.sub(pattern, *rest) {|*args|
256: begin
257: old = Thread.current[:pathname_sub_matchdata]
258: Thread.current[:pathname_sub_matchdata] = $~
259: eval("$~ = Thread.current[:pathname_sub_matchdata]", block.binding)
260: ensure
261: Thread.current[:pathname_sub_matchdata] = old
262: end
263: yield *args
264: }
265: else
266: path = @path.sub(pattern, *rest)
267: end
268: self.class.new(path)
269: end
Pathname#symlink is confusing and obsoleted because the receiver/argument order is inverted to corresponding system call.
# File lib/pathname.rb, line 861
861: def symlink(old)
862: warn 'Pathname#symlink is obsoleted. Use Pathname#make_symlink.'
863: File.symlink(old, @path)
864: end
See FileTest.symlink?.
# File lib/pathname.rb, line 928
928: def symlink?() FileTest.symlink?(@path) end
See IO.sysopen.
# File lib/pathname.rb, line 769
769: def sysopen(*args) IO.sysopen(@path, *args) end
See File.truncate. Truncate the file to length bytes.
# File lib/pathname.rb, line 831
831: def truncate(length) File.truncate(@path, length) end
Removes a file or directory, using File.unlink or Dir.unlink as necessary.
# File lib/pathname.rb, line 1046
1046: def unlink()
1047: begin
1048: Dir.unlink @path
1049: rescue Errno::ENOTDIR
1050: File.unlink @path
1051: end
1052: end
See File.utime. Update the access and modification times.
# File lib/pathname.rb, line 834
834: def utime(atime, mtime) File.utime(atime, mtime, @path) end
See FileTest.world_readable?.
# File lib/pathname.rb, line 907
907: def world_readable?() FileTest.world_readable?(@path) end
See FileTest.world_writable?.
# File lib/pathname.rb, line 934
934: def world_writable?() FileTest.world_writable?(@path) end
See FileTest.writable?.
# File lib/pathname.rb, line 931
931: def writable?() FileTest.writable?(@path) end
See FileTest.writable_real?.
# File lib/pathname.rb, line 937
937: def writable_real?() FileTest.writable_real?(@path) end
add_trailing_separator(path) -> path
# File lib/pathname.rb, line 369
369: def add_trailing_separator(path)
370: if File.basename(path + 'a') == 'a'
371: path
372: else
373: File.join(path, "") # xxx: Is File.join is appropriate to add separator?
374: end
375: end
chop_basename(path) -> [pre-basename, basename] or nil
# File lib/pathname.rb, line 278
278: def chop_basename(path)
279: base = File.basename(path)
280: if /\A#{SEPARATOR_PAT}?\z/ =~ base
281: return nil
282: else
283: return path[0, path.rindex(base)], base
284: end
285: end
Clean the path simply by resolving and removing excess "." and ".." entries. Nothing more, nothing less.
# File lib/pathname.rb, line 332
332: def cleanpath_aggressive
333: path = @path
334: names = []
335: pre = path
336: while r = chop_basename(pre)
337: pre, base = r
338: case base
339: when '.'
340: when '..'
341: names.unshift base
342: else
343: if names[0] == '..'
344: names.shift
345: else
346: names.unshift base
347: end
348: end
349: end
350: if /#{SEPARATOR_PAT}/o =~ File.basename(pre)
351: names.shift while names[0] == '..'
352: end
353: self.class.new(prepend_prefix(pre, File.join(*names)))
354: end
# File lib/pathname.rb, line 390
390: def cleanpath_conservative
391: path = @path
392: names = []
393: pre = path
394: while r = chop_basename(pre)
395: pre, base = r
396: names.unshift base if base != '.'
397: end
398: if /#{SEPARATOR_PAT}/o =~ File.basename(pre)
399: names.shift while names[0] == '..'
400: end
401: if names.empty?
402: self.class.new(File.dirname(pre))
403: else
404: if names.last != '..' && File.basename(path) == '.'
405: names << '.'
406: end
407: result = prepend_prefix(pre, File.join(*names))
408: if /\A(?:\.|\.\.)\z/ !~ names.last && has_trailing_separator?(path)
409: self.class.new(add_trailing_separator(result))
410: else
411: self.class.new(result)
412: end
413: end
414: end
# File lib/pathname.rb, line 378
378: def del_trailing_separator(path)
379: if r = chop_basename(path)
380: pre, basename = r
381: pre + basename
382: elsif /#{SEPARATOR_PAT}+\z/o =~ path
383: $` + File.dirname(path)[/#{SEPARATOR_PAT}*\z/o]
384: else
385: path
386: end
387: end
has_trailing_separator?(path) -> bool
# File lib/pathname.rb, line 358
358: def has_trailing_separator?(path)
359: if r = chop_basename(path)
360: pre, basename = r
361: pre.length + basename.length < path.length
362: else
363: false
364: end
365: end
# File lib/pathname.rb, line 600
600: def plus(path1, path2) # -> path
601: prefix2 = path2
602: index_list2 = []
603: basename_list2 = []
604: while r2 = chop_basename(prefix2)
605: prefix2, basename2 = r2
606: index_list2.unshift prefix2.length
607: basename_list2.unshift basename2
608: end
609: return path2 if prefix2 != ''
610: prefix1 = path1
611: while true
612: while !basename_list2.empty? && basename_list2.first == '.'
613: index_list2.shift
614: basename_list2.shift
615: end
616: break unless r1 = chop_basename(prefix1)
617: prefix1, basename1 = r1
618: next if basename1 == '.'
619: if basename1 == '..' || basename_list2.empty? || basename_list2.first != '..'
620: prefix1 = prefix1 + basename1
621: break
622: end
623: index_list2.shift
624: basename_list2.shift
625: end
626: r1 = chop_basename(prefix1)
627: if !r1 && /#{SEPARATOR_PAT}/o =~ File.basename(prefix1)
628: while !basename_list2.empty? && basename_list2.first == '..'
629: index_list2.shift
630: basename_list2.shift
631: end
632: end
633: if !basename_list2.empty?
634: suffix2 = path2[index_list2.first..-1]
635: r1 ? File.join(prefix1, suffix2) : prefix1 + suffix2
636: else
637: r1 ? prefix1 : File.dirname(prefix1)
638: end
639: end
# File lib/pathname.rb, line 299
299: def prepend_prefix(prefix, relpath)
300: if relpath.empty?
301: File.dirname(prefix)
302: elsif /#{SEPARATOR_PAT}/ =~ prefix
303: prefix = File.dirname(prefix)
304: prefix = File.join(prefix, "") if File.basename(prefix + 'a') != 'a'
305: prefix + relpath
306: else
307: prefix + relpath
308: end
309: end
# File lib/pathname.rb, line 417
417: def realpath_rec(prefix, unresolved, h)
418: resolved = []
419: until unresolved.empty?
420: n = unresolved.shift
421: if n == '.'
422: next
423: elsif n == '..'
424: resolved.pop
425: else
426: path = prepend_prefix(prefix, File.join(*(resolved + [n])))
427: if h.include? path
428: if h[path] == :resolving
429: raise Errno::ELOOP.new(path)
430: else
431: prefix, *resolved = h[path]
432: end
433: else
434: s = File.lstat(path)
435: if s.symlink?
436: h[path] = :resolving
437: link_prefix, link_names = split_names(File.readlink(path))
438: if link_prefix == ''
439: prefix, *resolved = h[path] = realpath_rec(prefix, resolved + link_names, h)
440: else
441: prefix, *resolved = h[path] = realpath_rec(link_prefix, link_names, h)
442: end
443: else
444: resolved << n
445: h[path] = [prefix, *resolved]
446: end
447: end
448: end
449: end
450: return prefix, *resolved
451: end
split_names(path) -> prefix, [name, …]
# File lib/pathname.rb, line 289
289: def split_names(path)
290: names = []
291: while r = chop_basename(path)
292: path, basename = r
293: names.unshift basename
294: end
295: return path, names
296: end