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