# frozen_string_literal: true # # = pathname.rb # # Object-Oriented Pathname Class # # Author:: Tanaka Akira # Documentation:: Author and Gavin Sinclair # # For documentation, see class Pathname. # # # Pathname represents the name of a file or directory on the filesystem, # but not the file itself. # # The pathname depends on the Operating System: Unix, Windows, etc. # This library works with pathnames of local OS, however non-Unix pathnames # are supported experimentally. # # 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 goal 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. # # == Examples # # === Example 1: Using Pathname # # require 'pathname' # pn = Pathname.new("/usr/bin/ruby") # size = pn.size # 27662 # isdir = pn.directory? # false # dir = pn.dirname # Pathname:/usr/bin # base = pn.basename # Pathname:ruby # dir, base = pn.split # [Pathname:/usr/bin, Pathname:ruby] # data = pn.read # pn.open { |f| _ } # pn.each_line { |line| _ } # # === Example 2: Using standard Ruby # # pn = "/usr/bin/ruby" # size = File.size(pn) # 27662 # isdir = File.directory?(pn) # false # dir = File.dirname(pn) # "/usr/bin" # base = File.basename(pn) # "ruby" # dir, base = File.split(pn) # ["/usr/bin", "ruby"] # data = File.read(pn) # File.open(pn) { |f| _ } # File.foreach(pn) { |line| _ } # # === Example 3: Special features # # 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, ...] # # == Breakdown of functionality # # === Core methods # # These methods are effectively manipulating a String, because that's # all a path is. None of these access the file system except for # #mountpoint?, #children, #each_child, #realdirpath and #realpath. # # - + # - #join # - #parent # - #root? # - #absolute? # - #relative? # - #relative_path_from # - #each_filename # - #cleanpath # - #realpath # - #realdirpath # - #children # - #each_child # - #mountpoint? # # === File status predicate methods # # These methods are a facade for FileTest: # - #blockdev? # - #chardev? # - #directory? # - #executable? # - #executable_real? # - #exist? # - #file? # - #grpowned? # - #owned? # - #pipe? # - #readable? # - #world_readable? # - #readable_real? # - #setgid? # - #setuid? # - #size # - #size? # - #socket? # - #sticky? # - #symlink? # - #writable? # - #world_writable? # - #writable_real? # - #zero? # # === File property and manipulation methods # # These methods are a facade for File: # - #each_line(*args, &block) # - #read(*args) # - #binread(*args) # - #readlines(*args) # - #sysopen(*args) # - #write(*args) # - #binwrite(*args) # - #atime # - #birthtime # - #ctime # - #mtime # - #chmod(mode) # - #lchmod(mode) # - #chown(owner, group) # - #lchown(owner, group) # - #fnmatch(pattern, *args) # - #fnmatch?(pattern, *args) # - #ftype # - #make_link(old) # - #open(*args, &block) # - #readlink # - #rename(to) # - #stat # - #lstat # - #make_symlink(old) # - #truncate(length) # - #utime(atime, mtime) # - #lutime(atime, mtime) # - #basename(*args) # - #dirname # - #extname # - #expand_path(*args) # - #split # # === Directory methods # # These methods are a facade for Dir: # - Pathname.glob(*args) # - Pathname.getwd / Pathname.pwd # - #rmdir # - #entries # - #each_entry(&block) # - #mkdir(*args) # - #opendir(*args) # # === Utilities # # These methods are a mixture of Find, FileUtils, and others: # - #find(&block) # - #mkpath # - #rmtree # - #unlink / #delete # # # == Method documentation # # 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. # class Pathname # The version string. VERSION = "0.4.0" # :stopdoc: # to_path is implemented so Pathname objects are usable with File.open, etc. TO_PATH = :to_path SAME_PATHS = if File::FNM_SYSCASE.nonzero? # Avoid #zero? here because #casecmp can return nil. proc {|a, b| a.casecmp(b) == 0} else proc {|a, b| a == b} end attr_reader :path protected :path # :startdoc: # # Create a Pathname object from the given String (or String-like object). # If +path+ contains a NUL character (\0), an ArgumentError is raised. # def initialize(path) unless String === path path = path.to_path if path.respond_to? :to_path raise TypeError unless String === path end if path.include?("\0") raise ArgumentError, "pathname contains \\0: #{path.inspect}" end @path = path.dup end def freeze super @path.freeze self 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. # def ==(other) return false unless Pathname === other other.path == @path end alias === == alias eql? == unless method_defined?(:<=>, false) # Provides for comparing pathnames, case-sensitively. def <=>(other) return nil unless Pathname === other @path.tr('/', "\0") <=> other.path.tr('/', "\0") end end def hash # :nodoc: @path.hash end # Return the path as a String. def to_s @path.dup end # to_path is implemented so Pathname objects are usable with File.open, etc. alias_method TO_PATH, :to_s def inspect # :nodoc: "#<#{self.class}:#{@path}>" end unless method_defined?(:sub, false) # Return a pathname which is substituted by String#sub. def sub(pattern, *args, **kwargs, &block) if block path = @path.sub(pattern, *args, **kwargs) {|*sub_args| begin old = Thread.current[:pathname_sub_matchdata] Thread.current[:pathname_sub_matchdata] = $~ eval("$~ = Thread.current[:pathname_sub_matchdata]", block.binding) ensure Thread.current[:pathname_sub_matchdata] = old end yield(*sub_args) } else path = @path.sub(pattern, *args, **kwargs) end self.class.new(path) end end # Return a pathname with +repl+ added as a suffix to the basename. # # If self has no extension part, +repl+ is appended. # # Pathname.new('/usr/bin/shutdown').sub_ext('.rb') # #=> # def sub_ext(repl) ext = File.extname(@path) # File.extname("foo.bar:stream") returns ".bar" on NTFS and not ".bar:stream" # (see ruby_enc_find_extname()). # The behavior of Pathname#sub_ext is to replace everything # from the start of the extname until the end of the path with repl. unless @path.end_with?(ext) ext = @path[@path.rindex(ext)..] end self.class.new(@path.chomp(ext) + repl) end if File::ALT_SEPARATOR # Separator list string. SEPARATOR_LIST = "#{Regexp.quote File::ALT_SEPARATOR}#{Regexp.quote File::SEPARATOR}" # Regexp that matches a separator. SEPARATOR_PAT = /[#{SEPARATOR_LIST}]/ else SEPARATOR_LIST = "#{Regexp.quote File::SEPARATOR}" SEPARATOR_PAT = /#{Regexp.quote File::SEPARATOR}/ end if File.dirname('A:') == 'A:.' # DOSish drive letter # Regexp that matches an absoltute path. ABSOLUTE_PATH = /\A(?:[A-Za-z]:|#{SEPARATOR_PAT})/ else ABSOLUTE_PATH = /\A#{SEPARATOR_PAT}/ end private_constant :ABSOLUTE_PATH # :startdoc: # Creates a full path, including any intermediate directories that don't yet # exist. # # See FileUtils.mkpath and FileUtils.mkdir_p def mkpath(mode: nil) path = @path == '/' ? @path : @path.chomp('/') stack = [] until File.directory?(path) || File.dirname(path) == path stack.push path path = File.dirname(path) end stack.reverse_each do |dir| dir = dir == '/' ? dir : dir.chomp('/') if mode Dir.mkdir dir, mode File.chmod mode, dir else Dir.mkdir dir end rescue SystemCallError raise unless File.directory?(dir) end self end # chop_basename(path) -> [pre-basename, basename] or nil def chop_basename(path) # :nodoc: base = File.basename(path) if /\A#{SEPARATOR_PAT}?\z/o.match?(base) return nil else return path[0, path.rindex(base)], base end end private :chop_basename # split_names(path) -> prefix, [name, ...] def split_names(path) # :nodoc: names = [] while r = chop_basename(path) path, basename = r names.unshift basename end return path, names end private :split_names def prepend_prefix(prefix, relpath) # :nodoc: if relpath.empty? File.dirname(prefix) elsif /#{SEPARATOR_PAT}/o.match?(prefix) prefix = File.dirname(prefix) prefix = File.join(prefix, "") if File.basename(prefix + 'a') != 'a' prefix + relpath else prefix + relpath end end private :prepend_prefix # 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 Pathname#realpath. # def cleanpath(consider_symlink=false) if consider_symlink cleanpath_conservative else cleanpath_aggressive end end # # Clean the path simply by resolving and removing excess +.+ and +..+ entries. # Nothing more, nothing less. # def cleanpath_aggressive # :nodoc: path = @path names = [] pre = path while r = chop_basename(pre) pre, base = r case base when '.' when '..' names.unshift base else if names[0] == '..' names.shift else names.unshift base end end end pre.tr!(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR if /#{SEPARATOR_PAT}/o.match?(File.basename(pre)) names.shift while names[0] == '..' end self.class.new(prepend_prefix(pre, File.join(*names))) end private :cleanpath_aggressive # has_trailing_separator?(path) -> bool def has_trailing_separator?(path) # :nodoc: if r = chop_basename(path) pre, basename = r pre.length + basename.length < path.length else false end end private :has_trailing_separator? # add_trailing_separator(path) -> path def add_trailing_separator(path) # :nodoc: if File.basename(path + 'a') == 'a' path else File.join(path, "") # xxx: Is File.join is appropriate to add separator? end end private :add_trailing_separator def del_trailing_separator(path) # :nodoc: if r = chop_basename(path) pre, basename = r pre + basename elsif /#{SEPARATOR_PAT}+\z/o =~ path $` + File.dirname(path)[/#{SEPARATOR_PAT}*\z/o] else path end end private :del_trailing_separator def cleanpath_conservative # :nodoc: path = @path names = [] pre = path while r = chop_basename(pre) pre, base = r names.unshift base if base != '.' end pre.tr!(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR if /#{SEPARATOR_PAT}/o.match?(File.basename(pre)) names.shift while names[0] == '..' end if names.empty? self.class.new(File.dirname(pre)) else if names.last != '..' && File.basename(path) == '.' names << '.' end result = prepend_prefix(pre, File.join(*names)) if /\A(?:\.|\.\.)\z/ !~ names.last && has_trailing_separator?(path) self.class.new(add_trailing_separator(result)) else self.class.new(result) end end end private :cleanpath_conservative # Returns the parent directory. # # This is same as self + '..'. def parent self + '..' end # Returns +true+ if +self+ points to a mountpoint. def mountpoint? begin stat1 = self.lstat stat2 = self.parent.lstat stat1.dev != stat2.dev || stat1.ino == stat2.ino rescue Errno::ENOENT false end end # # Predicate method for root directories. Returns +true+ if the # pathname consists of consecutive slashes. # # It doesn't access the filesystem. So it may return +false+ for some # pathnames which points to roots such as /usr/... # def root? chop_basename(@path) == nil && /#{SEPARATOR_PAT}/o.match?(@path) end # Predicate method for testing whether a path is absolute. # # It returns +true+ if the pathname begins with a slash. # # p = Pathname.new('/im/sure') # p.absolute? # #=> true # # p = Pathname.new('not/so/sure') # p.absolute? # #=> false def absolute? ABSOLUTE_PATH.match? @path end # The opposite of Pathname#absolute? # # It returns +false+ if the pathname begins with a slash. # # p = Pathname.new('/im/sure') # p.relative? # #=> false # # p = Pathname.new('not/so/sure') # p.relative? # #=> true def relative? !absolute? end # # Iterates over each component of the path. # # Pathname.new("/usr/bin/ruby").each_filename {|filename| ... } # # yields "usr", "bin", and "ruby". # # Returns an Enumerator if no block was given. # # enum = Pathname.new("/usr/bin/ruby").each_filename # # ... do stuff ... # enum.each { |e| ... } # # yields "usr", "bin", and "ruby". # def each_filename # :yield: filename return to_enum(__method__) unless block_given? _, names = split_names(@path) names.each {|filename| yield filename } nil 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.new('path/to/some/file.rb').descend {|v| p v} # # # # # # # # # # Returns an Enumerator if no block was given. # # enum = Pathname.new("/usr/bin/ruby").descend # # ... do stuff ... # enum.each { |e| ... } # # yields Pathnames /, /usr, /usr/bin, and /usr/bin/ruby. # # It doesn't access the filesystem. # def descend return to_enum(__method__) unless block_given? vs = [] ascend {|v| vs << v } vs.reverse_each {|v| yield v } nil 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.new('path/to/some/file.rb').ascend {|v| p v} # # # # # # # # # # Returns an Enumerator if no block was given. # # enum = Pathname.new("/usr/bin/ruby").ascend # # ... do stuff ... # enum.each { |e| ... } # # yields Pathnames /usr/bin/ruby, /usr/bin, /usr, and /. # # It doesn't access the filesystem. # def ascend return to_enum(__method__) unless block_given? path = @path yield self while r = chop_basename(path) path, = r break if path.empty? yield self.class.new(del_trailing_separator(path)) end end # # Appends a pathname fragment to +self+ to produce a new Pathname object. # Since +other+ is considered as a path relative to +self+, if +other+ is # an absolute path, the new Pathname object is created from just +other+. # # p1 = Pathname.new("/usr") # Pathname:/usr # p2 = p1 + "bin/ruby" # Pathname:/usr/bin/ruby # p3 = p1 + "/etc/passwd" # Pathname:/etc/passwd # # # / is aliased to +. # p4 = p1 / "bin/ruby" # Pathname:/usr/bin/ruby # p5 = p1 / "/etc/passwd" # Pathname:/etc/passwd # # This method doesn't access the file system; it is pure string manipulation. # def +(other) other = Pathname.new(other) unless Pathname === other Pathname.new(plus(@path, other.path)) end alias / + def plus(path1, path2) # -> path # :nodoc: prefix2 = path2 index_list2 = [] basename_list2 = [] while r2 = chop_basename(prefix2) prefix2, basename2 = r2 index_list2.unshift prefix2.length basename_list2.unshift basename2 end return path2 if prefix2 != '' prefix1 = path1 while true while !basename_list2.empty? && basename_list2.first == '.' index_list2.shift basename_list2.shift end break unless r1 = chop_basename(prefix1) prefix1, basename1 = r1 next if basename1 == '.' if basename1 == '..' || basename_list2.empty? || basename_list2.first != '..' prefix1 = prefix1 + basename1 break end index_list2.shift basename_list2.shift end r1 = chop_basename(prefix1) if !r1 && (r1 = /#{SEPARATOR_PAT}/o.match?(File.basename(prefix1))) while !basename_list2.empty? && basename_list2.first == '..' index_list2.shift basename_list2.shift end end if !basename_list2.empty? suffix2 = path2[index_list2.first..-1] r1 ? File.join(prefix1, suffix2) : prefix1 + suffix2 else r1 ? prefix1 : File.dirname(prefix1) end end private :plus # # Joins the given pathnames onto +self+ to create a new Pathname object. # This is effectively the same as using Pathname#+ to append +self+ and # all arguments sequentially. # # path0 = Pathname.new("/usr") # Pathname:/usr # path0 = path0.join("bin/ruby") # Pathname:/usr/bin/ruby # # is the same as # path1 = Pathname.new("/usr") + "bin/ruby" # Pathname:/usr/bin/ruby # path0 == path1 # #=> true # def join(*args) return self if args.empty? result = args.pop result = Pathname.new(result) unless Pathname === result return result if result.absolute? args.reverse_each {|arg| arg = Pathname.new(arg) unless Pathname === arg result = arg + result return result if result.absolute? } self + result 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: # pn = Pathname("/usr/lib/ruby/1.8") # pn.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, ... ] # pn.children(false) # # -> [ Pathname:English.rb, Pathname:Env.rb, Pathname:abbrev.rb, ... ] # # Note that the results never contain the entries +.+ and +..+ in # the directory because they are not children. # def children(with_directory=true) with_directory = false if @path == '.' result = [] Dir.foreach(@path) {|e| next if e == '.' || e == '..' if with_directory result << self.class.new(File.join(@path, e)) else result << self.class.new(e) end } result end # Iterates over the children of the directory # (files and subdirectories, not recursive). # # It yields Pathname object for each child. # # By default, the yielded 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. # # Pathname("/usr/local").each_child {|f| p f } # #=> # # # # # # # # # # # # # # # # # # # # # # # # Pathname("/usr/local").each_child(false) {|f| p f } # #=> # # # # # # # # # # # # # # # # # # # # # # # # Note that the results never contain the entries +.+ and +..+ in # the directory because they are not children. # # See Pathname#children # def each_child(with_directory=true, &b) children(with_directory).each(&b) end # # Returns a relative path from the given +base_directory+ to the receiver. # # If +self+ is absolute, then +base_directory+ must be absolute too. # # If +self+ is relative, then +base_directory+ must be relative too. # # This method doesn't access the filesystem. It assumes no symlinks. # # ArgumentError is raised when it cannot find a relative path. # # Note that this method does not handle situations where the case sensitivity # of the filesystem in use differs from the operating system default. # def relative_path_from(base_directory) base_directory = Pathname.new(base_directory) unless base_directory.is_a? Pathname dest_directory = self.cleanpath.path base_directory = base_directory.cleanpath.path dest_prefix = dest_directory dest_names = [] while r = chop_basename(dest_prefix) dest_prefix, basename = r dest_names.unshift basename if basename != '.' end base_prefix = base_directory base_names = [] while r = chop_basename(base_prefix) base_prefix, basename = r base_names.unshift basename if basename != '.' end unless SAME_PATHS[dest_prefix, base_prefix] raise ArgumentError, "different prefix: #{dest_prefix.inspect} and #{base_directory.inspect}" end while !dest_names.empty? && !base_names.empty? && SAME_PATHS[dest_names.first, base_names.first] dest_names.shift base_names.shift end if base_names.include? '..' raise ArgumentError, "base_directory has ..: #{base_directory.inspect}" end base_names.fill('..') relpath_names = base_names + dest_names if relpath_names.empty? Pathname.new('.') else Pathname.new(File.join(*relpath_names)) end end end class Pathname # * File * # # #each_line iterates over the line in the file. It yields a String object # for each line. # # This method has existed since 1.8.1. # def each_line(...) # :yield: line File.foreach(@path, ...) end # See File.read. Returns all data from the file, or the first +N+ bytes # if specified. def read(...) File.read(@path, ...) end # See File.binread. Returns all the bytes from the file, or the first +N+ # if specified. def binread(...) File.binread(@path, ...) end # See File.readlines. Returns all the lines from the file. def readlines(...) File.readlines(@path, ...) end # See File.sysopen. def sysopen(...) File.sysopen(@path, ...) end # Writes +contents+ to the file. See File.write. def write(...) File.write(@path, ...) end # Writes +contents+ to the file, opening it in binary mode. # # See File.binwrite. def binwrite(...) File.binwrite(@path, ...) end # See File.atime. Returns last access time. def atime() File.atime(@path) end # Returns the birth time for the file. # If the platform doesn't have birthtime, raises NotImplementedError. # # See File.birthtime. def birthtime() File.birthtime(@path) end # See File.ctime. Returns last (directory entry, not file) change time. def ctime() File.ctime(@path) end # See File.mtime. Returns last modification time. def mtime() File.mtime(@path) end # See File.chmod. Changes permissions. def chmod(mode) File.chmod(mode, @path) end # See File.lchmod. def lchmod(mode) File.lchmod(mode, @path) end # See File.chown. Change owner and group of file. def chown(owner, group) File.chown(owner, group, @path) end # See File.lchown. def lchown(owner, group) File.lchown(owner, group, @path) end # See File.fnmatch. Return +true+ if the receiver matches the given # pattern. def fnmatch(pattern, ...) File.fnmatch(pattern, @path, ...) end # See File.fnmatch? (same as #fnmatch). def fnmatch?(pattern, ...) File.fnmatch?(pattern, @path, ...) end # See File.ftype. Returns "type" of file ("file", "directory", # etc). def ftype() File.ftype(@path) end # See File.link. Creates a hard link. def make_link(old) File.link(old, @path) end # See File.open. Opens the file for reading or writing. def open(...) # :yield: file File.open(@path, ...) end # See File.readlink. Read symbolic link. def readlink() self.class.new(File.readlink(@path)) end # See File.rename. Rename the file. def rename(to) File.rename(@path, to) end # See File.stat. Returns a File::Stat object. def stat() File.stat(@path) end # See File.lstat. def lstat() File.lstat(@path) end # See File.symlink. Creates a symbolic link. def make_symlink(old) File.symlink(old, @path) end # See File.truncate. Truncate the file to +length+ bytes. def truncate(length) File.truncate(@path, length) end # See File.utime. Update the access and modification times. def utime(atime, mtime) File.utime(atime, mtime, @path) end # Update the access and modification times of the file. # # Same as Pathname#utime, but does not follow symbolic links. # # See File.lutime. def lutime(atime, mtime) File.lutime(atime, mtime, @path) end # See File.basename. Returns the last component of the path. def basename(...) self.class.new(File.basename(@path, ...)) end # See File.dirname. Returns all but the last component of the path. def dirname() self.class.new(File.dirname(@path)) end # See File.extname. Returns the file's extension. def extname() File.extname(@path) end # See File.expand_path. def expand_path(...) self.class.new(File.expand_path(@path, ...)) end # See File.split. Returns the #dirname and the #basename in an # Array. def split() array = File.split(@path) raise TypeError, 'wrong argument type nil (expected Array)' unless Array === array array.map {|f| self.class.new(f) } end # Returns the real (absolute) pathname for +self+ in the actual filesystem. # # Does not contain symlinks or useless dots, +..+ and +.+. # # All components of the pathname must exist when this method is called. def realpath(...) self.class.new(File.realpath(@path, ...)) end # Returns the real (absolute) pathname of +self+ in the actual filesystem. # # Does not contain symlinks or useless dots, +..+ and +.+. # # The last component of the real pathname can be nonexistent. def realdirpath(...) self.class.new(File.realdirpath(@path, ...)) end end class Pathname # * FileTest * # See FileTest.blockdev?. def blockdev?() FileTest.blockdev?(@path) end # See FileTest.chardev?. def chardev?() FileTest.chardev?(@path) end # Tests the file is empty. # # See Dir#empty? and FileTest.empty?. def empty? if FileTest.directory?(@path) Dir.empty?(@path) else File.empty?(@path) end end # See FileTest.executable?. def executable?() FileTest.executable?(@path) end # See FileTest.executable_real?. def executable_real?() FileTest.executable_real?(@path) end # See FileTest.exist?. def exist?() FileTest.exist?(@path) end # See FileTest.grpowned?. def grpowned?() FileTest.grpowned?(@path) end # See FileTest.directory?. def directory?() FileTest.directory?(@path) end # See FileTest.file?. def file?() FileTest.file?(@path) end # See FileTest.pipe?. def pipe?() FileTest.pipe?(@path) end # See FileTest.socket?. def socket?() FileTest.socket?(@path) end # See FileTest.owned?. def owned?() FileTest.owned?(@path) end # See FileTest.readable?. def readable?() FileTest.readable?(@path) end # See FileTest.world_readable?. def world_readable?() File.world_readable?(@path) end # See FileTest.readable_real?. def readable_real?() FileTest.readable_real?(@path) end # See FileTest.setuid?. def setuid?() FileTest.setuid?(@path) end # See FileTest.setgid?. def setgid?() FileTest.setgid?(@path) end # See FileTest.size. def size() FileTest.size(@path) end # See FileTest.size?. def size?() FileTest.size?(@path) end # See FileTest.sticky?. def sticky?() FileTest.sticky?(@path) end # See FileTest.symlink?. def symlink?() FileTest.symlink?(@path) end # See FileTest.writable?. def writable?() FileTest.writable?(@path) end # See FileTest.world_writable?. def world_writable?() File.world_writable?(@path) end # See FileTest.writable_real?. def writable_real?() FileTest.writable_real?(@path) end # See FileTest.zero?. def zero?() FileTest.zero?(@path) end end class Pathname # * Dir * # See Dir.glob. Returns or yields Pathname objects. def Pathname.glob(*args, **kwargs) # :yield: pathname if block_given? Dir.glob(*args, **kwargs) {|f| yield self.new(f) } else Dir.glob(*args, **kwargs).map {|f| self.new(f) } end end # Returns or yields Pathname objects. # # Pathname("ruby-2.4.2").glob("R*.md") # #=> [#, #] # # See Dir.glob. # This method uses the +base+ keyword argument of Dir.glob. def glob(*args, **kwargs) # :yield: pathname if block_given? Dir.glob(*args, **kwargs, base: @path) {|f| yield self + f } else Dir.glob(*args, **kwargs, base: @path).map {|f| self + f } end end # See Dir.getwd. Returns the current working directory as a Pathname. def Pathname.getwd() self.new(Dir.getwd) end class << self alias pwd getwd end # Return the entries (files and subdirectories) in the directory, each as a # Pathname object. def entries() Dir.entries(@path).map {|f| self.class.new(f) } end # Iterates over the entries (files and subdirectories) in the directory. It # yields a Pathname object for each entry. # # This method has existed since 1.8.1. def each_entry(&block) # :yield: pathname return to_enum(__method__) unless block_given? Dir.foreach(@path) {|f| yield self.class.new(f) } end # See Dir.mkdir. Create the referenced directory. def mkdir(...) Dir.mkdir(@path, ...) end # See Dir.rmdir. Remove the referenced directory. def rmdir() Dir.rmdir(@path) end # See Dir.open. def opendir(&block) # :yield: dir Dir.open(@path, &block) end end class Pathname # * mixed * # Removes a file or directory, using File.unlink or # Dir.unlink as necessary. def unlink() Dir.unlink @path rescue Errno::ENOTDIR File.unlink @path end alias delete unlink end class Pathname undef =~ if Kernel.method_defined?(:=~) end module Kernel # Creates a Pathname object. def Pathname(path) # :doc: return path if Pathname === path Pathname.new(path) end module_function :Pathname end