diff options
Diffstat (limited to 'lib/pathname.rb')
| -rw-r--r-- | lib/pathname.rb | 849 |
1 files changed, 125 insertions, 724 deletions
diff --git a/lib/pathname.rb b/lib/pathname.rb index 0cd0c9a377..0e51e1fdf6 100644 --- a/lib/pathname.rb +++ b/lib/pathname.rb @@ -1,750 +1,151 @@ +# frozen_string_literal: true +# +# = pathname.rb +# # Object-Oriented Pathname Class # # Author:: Tanaka Akira <akr@m17n.org> - -# Pathname represents a pathname which locates a file in a filesystem. -# It supports only Unix style pathnames. +# Documentation:: Author and Gavin Sinclair # -# Pathname is immutable. It has no method for destructive update. +# For documentation, see class Pathname. # -# pathname.rb is distributed with Ruby since 1.8.0. class Pathname - def initialize(path) - @path = path.to_str.dup - @path.freeze - if /\0/ =~ @path - raise ArgumentError, "pathname contains \\0: #{@path.inspect}" - end - end - - def ==(other) - return false unless Pathname === other - other.to_s == @path - end - alias === == - alias eql? == - - def <=>(other) - return nil unless Pathname === other - @path.tr('/', "\0") <=> other.to_s.tr('/', "\0") - end - - def hash - @path.hash - end - - def to_s - @path.dup - end - - # to_str is implemented for Pathname object usable with File.open, etc. - alias to_str to_s - - def inspect - "#<#{self.class}:#{@path}>" - end - - # cleanpath returns clean pathname of self which is without consecutive - # slashes and useless dots. - # - # If true is given as the optional argument consider_symlink, - # symbolic links are considered. It makes more dots are retained. - # - # cleanpath doesn't access actual filesystem. - def cleanpath(consider_symlink=false) - if consider_symlink - cleanpath_conservative - else - cleanpath_aggressive - end - end - - def cleanpath_aggressive # :nodoc: - # cleanpath_aggressive assumes: - # * no symlink - # * all pathname prefix contained in the pathname is existing directory - return Pathname.new('') if @path == '' - absolute = absolute? - names = [] - @path.scan(%r{[^/]+}) {|name| - next if name == '.' - if name == '..' - if names.empty? - next if absolute - else - if names.last != '..' - names.pop - next - end - end - end - names << name - } - return Pathname.new(absolute ? '/' : '.') if names.empty? - path = absolute ? '/' : '' - path << names.join('/') - Pathname.new(path) - end - - def cleanpath_conservative # :nodoc: - return Pathname.new('') if @path == '' - names = @path.scan(%r{[^/]+}) - last_dot = names.last == '.' - names.delete('.') - names.shift while names.first == '..' if absolute? - return Pathname.new(absolute? ? '/' : '.') if names.empty? - path = absolute? ? '/' : '' - path << names.join('/') - if names.last != '..' - if last_dot - path << '/.' - elsif %r{/\z} =~ @path - path << '/' - end - end - Pathname.new(path) - end - - # realpath returns a real pathname of self in actual filesystem. - # The real pathname doesn't contain a symlink and useless dots. - # - # It returns absolute pathname. - def realpath(*args) - unless args.empty? - warn "The argument for Pathname#realpath is obsoleted." - end - force_absolute = args.fetch(0, true) - - if %r{\A/} =~ @path - top = '/' - unresolved = @path.scan(%r{[^/]+}) - elsif force_absolute - # Although POSIX getcwd returns a pathname which contains no symlink, - # 4.4BSD-Lite2 derived getcwd may return the environment variable $PWD - # which may contain a symlink. - # So the return value of Dir.pwd should be examined. - top = '/' - unresolved = Dir.pwd.scan(%r{[^/]+}) + @path.scan(%r{[^/]+}) - else - top = '' - unresolved = @path.scan(%r{[^/]+}) - end - resolved = [] - - until unresolved.empty? - case unresolved.last - when '.' - unresolved.pop - when '..' - resolved.unshift unresolved.pop - else - loop_check = {} - while (stat = File.lstat(path = top + unresolved.join('/'))).symlink? - symlink_id = "#{stat.dev}:#{stat.ino}" - raise Errno::ELOOP.new(path) if loop_check[symlink_id] - loop_check[symlink_id] = true - if %r{\A/} =~ (link = File.readlink(path)) - top = '/' - unresolved = link.scan(%r{[^/]+}) - else - unresolved[-1,1] = link.scan(%r{[^/]+}) - end - end - next if (filename = unresolved.pop) == '.' - if filename != '..' && resolved.first == '..' - resolved.shift - else - resolved.unshift filename - end - end - end - - if top == '/' - resolved.shift while resolved[0] == '..' - end - - if resolved.empty? - Pathname.new(top.empty? ? '.' : '/') - else - Pathname.new(top + resolved.join('/')) - end - end - - # parent method returns parent directory. - # - # If self is `.', `..' is returned. - # Otherwise, `..' is joined to self. - def parent + # :markup: markdown + # + # call-seq: + # Pathname.find(ignore_error: true) -> nil + # + # With a block given, performs a depth-first traversal of the path in `self`; + # calls the block with each found path: + # + # ```ruby + # paths = [] + # Pathname('lib').find {|path| paths << path } + # paths.size # => 909 + # paths.take(3) + # # => + # # [#<Pathname:lib>, + # # #<Pathname:lib/English.gemspec>, + # # #<Pathname:lib/English.rb>] + # ``` + # + # When `self` contains `'.'`, the found paths omit the leading `'./'`: + # + # ```ruby + # paths = [] + # Dir.chdir('lib') do + # Pathname('.').find {|path| paths << path } + # end + # paths.take(3) + # # # => + # # [#<Pathname:.>, + # # #<Pathname:English.gemspec>, + # # #<Pathname:English.rb>] + # ``` + # + # This method calls method Find.find; + # therefore method Find.prune may be used in the block: + # + # ```ruby + # files = [] + # Pathname('.').find do |path| + # Find.prune if File.basename(path) == 'test' + # next unless File.file?(path) && File.extname(path) == '.rb' + # files << path + # end + # files.size # => 6690 + # files.take(3) + # # # => + # # [#<Pathname:KNOWNBUGS.rb>, + # # #<Pathname:array.rb>, + # # #<Pathname:ast.rb>] + # ``` + # + # Raises an exception if the path in `self` cannot be read. + # + # When keyword argument `ignore_error` is given as `true` (the default), + # certain exceptions during traversal are ignored (i.e., silently rescued): + # Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR, Errno::ELOOP, Errno::ENAMETOOLONG, Errno::EINVAL; + # when given as `false`, no exceptions are rescued. + # + # Note that these exceptions may be ignored only in `Pathname#find` traversal code; + # an exception raised before traversal begins, + # or raised while in the block is not ignored. + # Each of the calls below raises an Errno::ENOENT exception that is not ignored: + # + # ```ruby + # Pathname('nosuch').find { } + # Pathname('lib').find {|entry| raise Errno::ENOENT } + # ``` + # + # With no block given, returns a new Enumerator. + def find(ignore_error: true) # :yield: pathname + return to_enum(__method__, ignore_error: ignore_error) unless block_given? + require 'find' if @path == '.' - Pathname.new('..') + Find.find(@path, ignore_error: ignore_error) {|f| yield self.class.new(f.delete_prefix('./')) } else - self.join('..') - end - end - - # mountpoint? method returns true if self points a mountpoint. - def mountpoint? - begin - stat1 = self.lstat - stat2 = self.parent.lstat - stat1.dev == stat2.dev && stat1.ino == stat2.ino || - stat1.dev != stat2.dev - rescue Errno::ENOENT - false + Find.find(@path, ignore_error: ignore_error) {|f| yield self.class.new(f) } end end - - # root? method is a predicate for root directory. - # 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 root such as "/usr/..". - def root? - %r{\A/+\z} =~ @path ? true : false - end - - # absolute? method is a predicate for absolute pathname. - # It returns true if self is beginning with a slash. - def absolute? - %r{\A/} =~ @path ? true : false - end - - # relative? method is a predicate for relative pathname. - # It returns true unless self is beginning with a slash. - def relative? - !absolute? - end - - # each_filename iterates over self for each filename components. - def each_filename - @path.scan(%r{[^/]+}) { yield $& } - end - - # Pathname#+ return new pathname which is concatenated with self and - # an argument. - # If self is the current working directory `.' or - # the argument is absolute pathname, - # the argument is just returned. - # If the argument is `.', self is returned. - def +(other) - other = Pathname.new(other) unless Pathname === other - if @path == '.' || other.absolute? - other - elsif other.to_s == '.' - self - elsif %r{/\z} =~ @path - Pathname.new(@path + other.to_s) - else - Pathname.new(@path + '/' + other.to_s) - end - end - - # Pathname#join joins pathnames. - # - # path0.join(path1, ... pathN) is same as path0 + path1 + ... + pathN. - def join(*args) - args.map! {|arg| Pathname === arg ? arg : Pathname.new(arg) } - args.inject(self) {|pathname, arg| pathname + arg } - end - - # Pathname#children returns the children of the directory as an array of - # pathnames. - # - # By default, the returned pathname can be used to access the corresponding - # file in the directory. - # This is because the pathname contains self as a prefix unless self is `.'. - # - # If false is given for the optional argument `with_directory', - # just filenames of children is returned. - # In this case, the returned pathname cannot be used directly to access the - # corresponding file when self doesn't point working directory. - # - # Note that the result never contain the entry `.' and `..' in the directory - # because they are not child. - # - # This method is exist since 1.8.1. - def children(with_directory=true) - with_directory = false if @path == '.' - result = [] - Dir.foreach(@path) {|e| - next if e == '.' || e == '..' - if with_directory - result << Pathname.new(File.join(@path, e)) - else - result << Pathname.new(e) - end - } - result - end - - # Pathname#relative_path_from returns a relative path from the argument to - # self. - # 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 actual filesystem. - # It assumes no symlinks. - # - # ArgumentError is raised when it cannot find a relative path. - # - # This method is exist since 1.8.1. - def relative_path_from(base_directory) - if self.absolute? != base_directory.absolute? - raise ArgumentError, - "relative path between absolute and relative path: #{self.inspect}, #{base_directory.inspect}" - end - - dest = [] - self.cleanpath.each_filename {|f| - next if f == '.' - dest << f - } - - base = [] - base_directory.cleanpath.each_filename {|f| - next if f == '.' - base << f - } - - while !base.empty? && !dest.empty? && base[0] == dest[0] - base.shift - dest.shift - end - - if base.include? '..' - raise ArgumentError, "base_directory has ..: #{base_directory.inspect}" - end - - base.fill '..' - relpath = base + dest - if relpath.empty? - Pathname.new(".") - else - Pathname.new(relpath.join('/')) - end - end - -end - -# IO -class Pathname - # Pathname#each_line iterates over lines of the file. - # It's yields a String object for each line. - # - # This method is exist since 1.8.1. - def each_line(*args, &block) IO.foreach(@path, *args, &block) end - - # Pathname#foreachline is obsoleted at 1.8.1. - # - def foreachline(*args, &block) # compatibility to 1.8.0. obsoleted. - warn "Pathname#foreachline is obsoleted. Use Pathname#each_line." - each_line(*args, &block) - end - - def read(*args) IO.read(@path, *args) end - def readlines(*args) IO.readlines(@path, *args) end - def sysopen(*args) IO.sysopen(@path, *args) end -end - -# File -class Pathname - def atime() File.atime(@path) end - def ctime() File.ctime(@path) end - def mtime() File.mtime(@path) end - def chmod(mode) File.chmod(mode, @path) end - def lchmod(mode) File.chmod(mode, @path) end - def chown(owner, group) File.chown(owner, group, @path) end - def lchown(owner, group) File.lchown(owner, group, @path) end - def fnmatch(pattern, *args) File.fnmatch(pattern, @path, *args) end - def fnmatch?(pattern, *args) File.fnmatch?(pattern, @path, *args) end - def ftype() File.ftype(@path) end - def make_link(old) File.link(old, @path) end - def open(*args, &block) File.open(@path, *args, &block) end - def readlink() Pathname.new(File.readlink(@path)) end - def rename(to) File.rename(@path, to) end - def stat() File.stat(@path) end - def lstat() File.lstat(@path) end - def make_symlink(old) File.symlink(old, @path) end - def truncate(length) File.truncate(@path, length) end - def utime(atime, mtime) File.utime(atime, mtime, @path) end - def basename(*args) Pathname.new(File.basename(@path, *args)) end - def dirname() Pathname.new(File.dirname(@path)) end - def extname() File.extname(@path) end - def expand_path(*args) Pathname.new(File.expand_path(@path, *args)) end - def split() File.split(@path).map {|f| Pathname.new(f) } end - - # Pathname#link is confusing and obsoleted because the receiver/argument - # order is inverted to corresponding system call. - def link(old) - warn 'Pathname#link is obsoleted. Use Pathname#make_link.' - File.link(old, @path) - end - - # Pathname#symlink is confusing and obsoleted because the receiver/argument - # order is inverted to corresponding system call. - def symlink(old) - warn 'Pathname#symlink is obsoleted. Use Pathname#make_symlink.' - File.symlink(old, @path) - end end -# FileTest -class Pathname - def blockdev?() FileTest.blockdev?(@path) end - def chardev?() FileTest.chardev?(@path) end - def executable?() FileTest.executable?(@path) end - def executable_real?() FileTest.executable_real?(@path) end - def exist?() FileTest.exist?(@path) end - def grpowned?() FileTest.grpowned?(@path) end - def directory?() FileTest.directory?(@path) end - def file?() FileTest.file?(@path) end - def pipe?() FileTest.pipe?(@path) end - def socket?() FileTest.socket?(@path) end - def owned?() FileTest.owned?(@path) end - def readable?() FileTest.readable?(@path) end - def readable_real?() FileTest.readable_real?(@path) end - def setuid?() FileTest.setuid?(@path) end - def setgid?() FileTest.setgid?(@path) end - def size() FileTest.size(@path) end - def size?() FileTest.size?(@path) end - def sticky?() FileTest.sticky?(@path) end - def symlink?() FileTest.symlink?(@path) end - def writable?() FileTest.writable?(@path) end - def writable_real?() FileTest.writable_real?(@path) end - def zero?() FileTest.zero?(@path) end -end -# Dir -class Pathname - def Pathname.glob(*args) - if block_given? - Dir.glob(*args) {|f| yield Pathname.new(f) } - else - Dir.glob(*args).map {|f| Pathname.new(f) } - end - end - - def Pathname.getwd() Pathname.new(Dir.getwd) end - class << self; alias pwd getwd end - - # Pathname#chdir is obsoleted at 1.8.1. - # - def chdir(&block) # compatibility to 1.8.0. - warn "Pathname#chdir is obsoleted. Use Dir.chdir." - Dir.chdir(@path, &block) - end - - # Pathname#chroot is obsoleted at 1.8.1. - # - def chroot # compatibility to 1.8.0. - warn "Pathname#chroot is obsoleted. Use Dir.chroot." - Dir.chroot(@path) - end - - def rmdir() Dir.rmdir(@path) end - def entries() Dir.entries(@path).map {|f| Pathname.new(f) } end - - # Pathname#each_entry iterates over entries of the directory. - # It's yields Pathname objects for each entry. +class Pathname # * FileUtils * + # Recursively deletes a directory, including all directories beneath it. # - # This method is exist since 1.8.1. - def each_entry(&block) Dir.foreach(@path) {|f| yield Pathname.new(f) } end - - # Pathname#dir_foreach is obsoleted at 1.8.1. + # Note that you need to require 'pathname' to use this method. # - def dir_foreach(*args, &block) # compatibility to 1.8.0. obsoleted. - warn "Pathname#dir_foreach is obsoleted. Use Pathname#each_entry." - each_entry(*args, &block) - end - - def mkdir(*args) Dir.mkdir(@path, *args) end - def opendir(&block) Dir.open(@path, &block) end -end - -# Find -class Pathname - # Pathname#find is a iterator to traverse directory tree in depth first - # manner. It yields a pathname for each file under the directory which - # is pointed by self. - # - # 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 `./'. - def find(&block) - require 'find' - if @path == '.' - Find.find(@path) {|f| yield Pathname.new(f.sub(%r{\A\./}, '')) } - else - Find.find(@path) {|f| yield Pathname.new(f) } - end - end -end - -# FileUtils -class Pathname - def mkpath - require 'fileutils' - FileUtils.mkpath(@path) - nil - end - - def rmtree + # See FileUtils.rm_rf + def rmtree(noop: nil, verbose: nil, secure: nil) # The name "rmtree" is borrowed from File::Path of Perl. # File::Path provides "mkpath" and "rmtree". require 'fileutils' - FileUtils.rm_r(@path) - nil + FileUtils.rm_rf(@path, noop: noop, verbose: verbose, secure: secure) + self end end -# mixed -class Pathname - def unlink() - if FileTest.directory? @path - Dir.unlink @path - else - File.unlink @path - end - end - alias delete unlink - - # This method is obsoleted at 1.8.1. - # - def foreach(*args, &block) # compatibility to 1.8.0. obsoleted. - warn "Pathname#foreach is obsoleted. Use each_line or each_entry." - if FileTest.directory? @path - # For polymorphism between Dir.foreach and IO.foreach, - # Pathname#foreach doesn't yield Pathname object. - Dir.foreach(@path, *args, &block) +class Pathname # * tmpdir * + # call-seq: + # Pathname.mktmpdir -> new_pathname + # Pathname.mktmpdir {|pathname| ... } -> object + # + # Creates: + # + # - A temporary directory via Dir.mktmpdir. + # - A \Pathname object that contains the path to that directory. + # + # With no block given, returns the created pathname; + # the caller should delete the created directory when it is no longer needed + # (FileUtils.rm_r is a convenient method for the deletion): + # + # pathname = Pathname.mktmpdir + # dirpath = pathname.to_s + # Dir.exist?(dirpath) # => true + # # Do something with the directory. + # require 'fileutils' + # FileUtils.rm_r(dirpath) + # + # With a block given, calls the block with the created pathname; + # on block exit, automatically deletes the created directory and all its contents; + # returns the block's exit value: + # + # pathname = Pathname.mktmpdir do |p| + # # Do something with the directory. + # p + # end + # Dir.exist?(pathname.to_s) # => false + def self.mktmpdir + require 'tmpdir' unless defined?(Dir.mktmpdir) + if block_given? + Dir.mktmpdir do |dir| + dir = self.new(dir) + yield dir + end else - IO.foreach(@path, *args, &block) - end - end -end - -if $0 == __FILE__ - require 'test/unit' - - class PathnameTest < Test::Unit::TestCase # :nodoc: - class AnotherStringLike # :nodoc: - def initialize(s) @s = s end - def to_str() @s end - def ==(other) @s == other end - end - - def test_equality - obj = Pathname.new("a") - str = "a" - sym = :a - ano = AnotherStringLike.new("a") - assert_equal(false, obj == str) - assert_equal(false, str == obj) - assert_equal(false, obj == ano) - assert_equal(false, ano == obj) - assert_equal(false, obj == sym) - assert_equal(false, sym == obj) - - obj2 = Pathname.new("a") - assert_equal(true, obj == obj2) - assert_equal(true, obj === obj2) - assert_equal(true, obj.eql?(obj2)) - end - - def test_hashkey - h = {} - h[Pathname.new("a")] = 1 - h[Pathname.new("a")] = 2 - assert_equal(1, h.size) - end - - def assert_pathname_cmp(e, s1, s2) - p1 = Pathname.new(s1) - p2 = Pathname.new(s2) - r = p1 <=> p2 - assert(e == r, - "#{p1.inspect} <=> #{p2.inspect}: <#{e}> expected but was <#{r}>") - end - def test_comparison - assert_pathname_cmp( 0, "a", "a") - assert_pathname_cmp( 1, "b", "a") - assert_pathname_cmp(-1, "a", "b") - ss = %w( - a - a/ - a/b - a. - a0 - ) - s1 = ss.shift - ss.each {|s2| - assert_pathname_cmp(-1, s1, s2) - s1 = s2 - } - end - - def test_comparison_string - assert_equal(nil, Pathname.new("a") <=> "a") - assert_equal(nil, "a" <=> Pathname.new("a")) - end - - def test_syntactical - assert_equal(true, Pathname.new("/").root?) - assert_equal(true, Pathname.new("//").root?) - assert_equal(true, Pathname.new("///").root?) - assert_equal(false, Pathname.new("").root?) - assert_equal(false, Pathname.new("a").root?) - end - - def test_cleanpath - assert_equal('/', Pathname.new('/').cleanpath(true).to_s) - assert_equal('/', Pathname.new('//').cleanpath(true).to_s) - assert_equal('', Pathname.new('').cleanpath(true).to_s) - - assert_equal('.', Pathname.new('.').cleanpath(true).to_s) - assert_equal('..', Pathname.new('..').cleanpath(true).to_s) - assert_equal('a', Pathname.new('a').cleanpath(true).to_s) - assert_equal('/', Pathname.new('/.').cleanpath(true).to_s) - assert_equal('/', Pathname.new('/..').cleanpath(true).to_s) - assert_equal('/a', Pathname.new('/a').cleanpath(true).to_s) - assert_equal('.', Pathname.new('./').cleanpath(true).to_s) - assert_equal('..', Pathname.new('../').cleanpath(true).to_s) - assert_equal('a/', Pathname.new('a/').cleanpath(true).to_s) - - assert_equal('a/b', Pathname.new('a//b').cleanpath(true).to_s) - assert_equal('a/.', Pathname.new('a/.').cleanpath(true).to_s) - assert_equal('a/.', Pathname.new('a/./').cleanpath(true).to_s) - assert_equal('a/..', Pathname.new('a/../').cleanpath(true).to_s) - assert_equal('/a/.', Pathname.new('/a/.').cleanpath(true).to_s) - assert_equal('..', Pathname.new('./..').cleanpath(true).to_s) - assert_equal('..', Pathname.new('../.').cleanpath(true).to_s) - assert_equal('..', Pathname.new('./../').cleanpath(true).to_s) - assert_equal('..', Pathname.new('.././').cleanpath(true).to_s) - assert_equal('/', Pathname.new('/./..').cleanpath(true).to_s) - assert_equal('/', Pathname.new('/../.').cleanpath(true).to_s) - assert_equal('/', Pathname.new('/./../').cleanpath(true).to_s) - assert_equal('/', Pathname.new('/.././').cleanpath(true).to_s) - - assert_equal('a/b/c', Pathname.new('a/b/c').cleanpath(true).to_s) - assert_equal('b/c', Pathname.new('./b/c').cleanpath(true).to_s) - assert_equal('a/c', Pathname.new('a/./c').cleanpath(true).to_s) - assert_equal('a/b/.', Pathname.new('a/b/.').cleanpath(true).to_s) - assert_equal('a/..', Pathname.new('a/../.').cleanpath(true).to_s) - - assert_equal('/a', Pathname.new('/../.././../a').cleanpath(true).to_s) - assert_equal('a/b/../../../../c/../d', - Pathname.new('a/b/../../../../c/../d').cleanpath(true).to_s) - end - - def test_cleanpath_no_symlink - assert_equal('/', Pathname.new('/').cleanpath.to_s) - assert_equal('/', Pathname.new('//').cleanpath.to_s) - assert_equal('', Pathname.new('').cleanpath.to_s) - - assert_equal('.', Pathname.new('.').cleanpath.to_s) - assert_equal('..', Pathname.new('..').cleanpath.to_s) - assert_equal('a', Pathname.new('a').cleanpath.to_s) - assert_equal('/', Pathname.new('/.').cleanpath.to_s) - assert_equal('/', Pathname.new('/..').cleanpath.to_s) - assert_equal('/a', Pathname.new('/a').cleanpath.to_s) - assert_equal('.', Pathname.new('./').cleanpath.to_s) - assert_equal('..', Pathname.new('../').cleanpath.to_s) - assert_equal('a', Pathname.new('a/').cleanpath.to_s) - - assert_equal('a/b', Pathname.new('a//b').cleanpath.to_s) - assert_equal('a', Pathname.new('a/.').cleanpath.to_s) - assert_equal('a', Pathname.new('a/./').cleanpath.to_s) - assert_equal('.', Pathname.new('a/../').cleanpath.to_s) - assert_equal('/a', Pathname.new('/a/.').cleanpath.to_s) - assert_equal('..', Pathname.new('./..').cleanpath.to_s) - assert_equal('..', Pathname.new('../.').cleanpath.to_s) - assert_equal('..', Pathname.new('./../').cleanpath.to_s) - assert_equal('..', Pathname.new('.././').cleanpath.to_s) - assert_equal('/', Pathname.new('/./..').cleanpath.to_s) - assert_equal('/', Pathname.new('/../.').cleanpath.to_s) - assert_equal('/', Pathname.new('/./../').cleanpath.to_s) - assert_equal('/', Pathname.new('/.././').cleanpath.to_s) - - assert_equal('a/b/c', Pathname.new('a/b/c').cleanpath.to_s) - assert_equal('b/c', Pathname.new('./b/c').cleanpath.to_s) - assert_equal('a/c', Pathname.new('a/./c').cleanpath.to_s) - assert_equal('a/b', Pathname.new('a/b/.').cleanpath.to_s) - assert_equal('.', Pathname.new('a/../.').cleanpath.to_s) - - assert_equal('/a', Pathname.new('/../.././../a').cleanpath.to_s) - assert_equal('../../d', Pathname.new('a/b/../../../../c/../d').cleanpath.to_s) - end - - def test_destructive_update - path = Pathname.new("a") - path.to_s.replace "b" - assert_equal(Pathname.new("a"), path) - end - - def test_null_character - assert_raises(ArgumentError) { Pathname.new("\0") } - end - - def assert_relpath(result, dest, base) - assert_equal(Pathname.new(result), - Pathname.new(dest).relative_path_from(Pathname.new(base))) - end - - def assert_relpath_err(dest, base) - assert_raises(ArgumentError) { - Pathname.new(dest).relative_path_from(Pathname.new(base)) - } - end - - def test_relative_path_from - assert_relpath("../a", "a", "b") - assert_relpath("../a", "a", "b/") - assert_relpath("../a", "a/", "b") - assert_relpath("../a", "a/", "b/") - assert_relpath("../a", "/a", "/b") - assert_relpath("../a", "/a", "/b/") - assert_relpath("../a", "/a/", "/b") - assert_relpath("../a", "/a/", "/b/") - - assert_relpath("../b", "a/b", "a/c") - assert_relpath("../a", "../a", "../b") - - assert_relpath("a", "a", ".") - assert_relpath("..", ".", "a") - - assert_relpath(".", ".", ".") - assert_relpath(".", "..", "..") - assert_relpath("..", "..", ".") - - assert_relpath("c/d", "/a/b/c/d", "/a/b") - assert_relpath("../..", "/a/b", "/a/b/c/d") - assert_relpath("../../../../e", "/e", "/a/b/c/d") - assert_relpath("../b/c", "a/b/c", "a/d") - - assert_relpath("../a", "/../a", "/b") - assert_relpath("../../a", "../a", "b") - assert_relpath(".", "/a/../../b", "/b") - assert_relpath("..", "a/..", "a") - assert_relpath(".", "a/../b", "b") - - assert_relpath("a", "a", "b/..") - assert_relpath("b/c", "b/c", "b/..") - - assert_relpath_err("/", ".") - assert_relpath_err(".", "/") - assert_relpath_err("a", "..") - assert_relpath_err(".", "..") - end - - def test_plus - assert_equal(Pathname.new('a/b'), Pathname.new('a') + Pathname.new('b')) - assert_equal(Pathname.new('a'), Pathname.new('a') + Pathname.new('.')) - assert_equal(Pathname.new('b'), Pathname.new('.') + Pathname.new('b')) - assert_equal(Pathname.new('.'), Pathname.new('.') + Pathname.new('.')) - assert_equal(Pathname.new('/b'), Pathname.new('a') + Pathname.new('/b')) + self.new(Dir.mktmpdir) end end end |
