class Pathname
def self.mktmpdir
Creates a tmp directory and wraps the returned path in a Pathname object.
* tmpdir *
def self.mktmpdir require 'tmpdir' unless defined?(Dir.mktmpdir) if block_given? Dir.mktmpdir do |dir| dir = self.new(dir) yield dir end else self.new(Dir.mktmpdir) end end
def +(other)
This method doesn't access the file system; it is pure string manipulation.
p5 = p1 / "/etc/passwd" # Pathname:/etc/passwd
p4 = p1 / "bin/ruby" # Pathname:/usr/bin/ruby
# / is aliased to +.
p3 = p1 + "/etc/passwd" # Pathname:/etc/passwd
p2 = p1 + "bin/ruby" # Pathname:/usr/bin/ruby
p1 = Pathname.new("/usr") # Pathname:/usr
an absolute path, the new Pathname object is created from just +other+.
Since +other+ is considered as a path relative to +self+, if +other+ is
Appends a pathname fragment to +self+ to produce a new Pathname object.
def +(other) other = Pathname.new(other) unless Pathname === other Pathname.new(plus(@path, other.to_s)) end
def absolute?
p.absolute?
p = Pathname.new('not/so/sure')
#=> true
p.absolute?
p = Pathname.new('/im/sure')
It returns +true+ if the pathname begins with a slash.
Predicate method for testing whether a path is absolute.
def absolute? ABSOLUTE_PATH.match? @path end
def add_trailing_separator(path) # :nodoc:
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
def ascend
It doesn't access the filesystem.
# yields Pathnames /usr/bin/ruby, /usr/bin, /usr, and /.
enum.each { |e| ... }
# ... do stuff ...
enum = Pathname.new("/usr/bin/ruby").ascend
Returns an Enumerator if no block was given.
#
#
#
#
Pathname.new('path/to/some/file.rb').ascend {|v| p v}
#
#
#
#
#
Pathname.new('/path/to/some/file.rb').ascend {|v| p v}
for each element in the given path in ascending order.
Iterates over and yields a new Pathname object
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
def children(with_directory=true)
the directory because they are not children.
Note that the results never contain the entries +.+ and +..+ in
# -> [ Pathname:English.rb, Pathname:Env.rb, Pathname:abbrev.rb, ... ]
pn.children(false)
Pathname:/usr/lib/ruby/1.8/abbrev.rb, ... ]
Pathname:/usr/lib/ruby/1.8/Env.rb,
# -> [ Pathname:/usr/lib/ruby/1.8/English.rb,
pn.children
pn = Pathname("/usr/lib/ruby/1.8")
For example:
pathnames will contain the filename only.
the files. If you set +with_directory+ to +false+, then the returned
By default, the returned pathnames will have enough information to access
recursive) as an array of Pathname objects.
Returns the children of the directory (files and subdirectories, not
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
def chop_basename(path) # :nodoc:
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
def cleanpath(consider_symlink=false)
See Pathname#realpath.
this can't be avoided.
entries than absolutely necessary, but without accessing the filesystem,
to avoid breaking symbolic linkages. This may retain more +..+
If +consider_symlink+ is +true+, then a more conservative algorithm is used
removed. The filesystem is not accessed.
Returns clean pathname of +self+ with consecutive slashes and useless dots
def cleanpath(consider_symlink=false) if consider_symlink cleanpath_conservative else cleanpath_aggressive end end
def cleanpath_aggressive # :nodoc:
Nothing more, nothing less.
Clean the path simply by resolving and removing excess +.+ and +..+ entries.
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
def cleanpath_conservative # :nodoc:
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
def del_trailing_separator(path) # :nodoc:
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
def descend
It doesn't access the filesystem.
# yields Pathnames /, /usr, /usr/bin, and /usr/bin/ruby.
enum.each { |e| ... }
# ... do stuff ...
enum = Pathname.new("/usr/bin/ruby").descend
Returns an Enumerator if no block was given.
#
#
#
#
Pathname.new('path/to/some/file.rb').descend {|v| p v}
#
#
#
#
#
Pathname.new('/path/to/some/file.rb').descend {|v| p v}
for each element in the given path in descending order.
Iterates over and yields a new Pathname object
def descend return to_enum(__method__) unless block_given? vs = [] ascend {|v| vs << v } vs.reverse_each {|v| yield v } nil end
def each_child(with_directory=true, &b)
See Pathname#children
the directory because they are not children.
Note that the results never contain the entries +.+ and +..+ in
# #
# #
# #
# #
# #
# #
# #
#=> #
Pathname("/usr/local").each_child(false) {|f| p f }
# #
# #
# #
# #
# #
# #
# #
#=> #
Pathname("/usr/local").each_child {|f| p f }
contain the filename only.
If you set +with_directory+ to +false+, then the returned pathnames will
the files.
By default, the yielded pathnames will have enough information to access
It yields Pathname object for each child.
(files and subdirectories, not recursive).
Iterates over the children of the directory
def each_child(with_directory=true, &b) children(with_directory).each(&b) end
def each_filename # :yield: filename
# yields "usr", "bin", and "ruby".
enum.each { |e| ... }
# ... do stuff ...
enum = Pathname.new("/usr/bin/ruby").each_filename
Returns an Enumerator if no block was given.
# yields "usr", "bin", and "ruby".
Pathname.new("/usr/bin/ruby").each_filename {|filename| ... }
Iterates over each component of the path.
def each_filename # :yield: filename return to_enum(__method__) unless block_given? _, names = split_names(@path) names.each {|filename| yield filename } nil end
def find(ignore_error: true) # :yield: pathname
See Find.find
current directory, not +./+.
If +self+ is +.+, yielded pathnames begin with a filename in the
be used to control the traversal.
Since it is implemented by the standard library module Find, Find.prune can
Returns an Enumerator if no block is given.
Pathname for each file under "this" directory.
Iterates over the directory tree in a depth first manner, yielding a
* Find *
def find(ignore_error: true) # :yield: pathname return to_enum(__method__, ignore_error: ignore_error) unless block_given? require 'find' if @path == '.' Find.find(@path, ignore_error: ignore_error) {|f| yield self.class.new(f.delete_prefix('./')) } else Find.find(@path, ignore_error: ignore_error) {|f| yield self.class.new(f) } end end
def has_trailing_separator?(path) # :nodoc:
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
def join(*args)
#=> true
path0 == path1
path1 = Pathname.new("/usr") + "bin/ruby" # Pathname:/usr/bin/ruby
# is the same as
path0 = path0.join("bin/ruby") # Pathname:/usr/bin/ruby
path0 = Pathname.new("/usr") # Pathname:/usr
all arguments sequentially.
This is effectively the same as using Pathname#+ to append +self+ and
Joins the given pathnames onto +self+ to create a new Pathname object.
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
def mkpath(mode: nil)
exist.
Creates a full path, including any intermediate directories that don't yet
* FileUtils *
def mkpath(mode: nil) require 'fileutils' FileUtils.mkpath(@path, mode: mode) self end
def 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
def parent
self + '..'.Returns the parent directory.
def parent self + '..' end
def plus(path1, path2) # -> path # :nodoc:
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
def prepend_prefix(prefix, relpath) # :nodoc:
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
def relative?
p.relative?
p = Pathname.new('not/so/sure')
#=> false
p.relative?
p = Pathname.new('/im/sure')
It returns +false+ if the pathname begins with a slash.
The opposite of Pathname#absolute?
def relative? !absolute? end
def relative_path_from(base_directory)
of the filesystem in use differs from the operating system default.
Note that this method does not handle situations where the case sensitivity
ArgumentError is raised when it cannot find a relative path.
This method doesn't access the filesystem. It assumes no symlinks.
If +self+ is relative, then +base_directory+ must be relative too.
If +self+ is absolute, then +base_directory+ must be absolute too.
Returns a relative path from the given +base_directory+ to the receiver.
def relative_path_from(base_directory) base_directory = Pathname.new(base_directory) unless base_directory.is_a? Pathname dest_directory = self.cleanpath.to_s base_directory = base_directory.cleanpath.to_s 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
def rmtree(noop: nil, verbose: nil, secure: nil)
Recursively deletes a directory, including all directories beneath it.
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_rf(@path, noop: noop, verbose: verbose, secure: secure) self end
def root?
pathnames which points to roots such as /usr/...
It doesn't access the filesystem. So it may return +false+ for some
pathname consists of consecutive slashes.
Predicate method for root directories. Returns +true+ if the
def root? chop_basename(@path) == nil && /#{SEPARATOR_PAT}/o.match?(@path) end
def split_names(path) # :nodoc:
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