class Path
All methods from FileTest and all predicates from File are included
Path's low-level implementation based on Pathname
Constants
- LOADERS
The list of loaders. See {Path.register_loader}.
- SAME_PATHS
@private
- VERSION
The version of the gem. Set here to avoid duplication and allow introspection.
Attributes
Returns the path
as a String. {#path} is implemented for better readability (file.path
instead of file.to_s
) and as an accessor. {#to_path} is implemented so Path
objects are usable with open
, etc. {#to_str} is implemented so Path
objects are usable with open
, etc with Ruby 1.8 (it is not defined in Ruby 1.9).
Returns the path
as a String. {#path} is implemented for better readability (file.path
instead of file.to_s
) and as an accessor. {#to_path} is implemented so Path
objects are usable with open
, etc. {#to_str} is implemented so Path
objects are usable with open
, etc with Ruby 1.8 (it is not defined in Ruby 1.9).
Returns the path
as a String. {#path} is implemented for better readability (file.path
instead of file.to_s
) and as an accessor. {#to_path} is implemented so Path
objects are usable with open
, etc. {#to_str} is implemented so Path
objects are usable with open
, etc with Ruby 1.8 (it is not defined in Ruby 1.9).
Returns the path
as a String. {#path} is implemented for better readability (file.path
instead of file.to_s
) and as an accessor. {#to_path} is implemented so Path
objects are usable with open
, etc. {#to_str} is implemented so Path
objects are usable with open
, etc with Ruby 1.8 (it is not defined in Ruby 1.9).
Public Class Methods
Marshal loading.
# File lib/path/identity.rb, line 149 def self._load path self.new(path) end
Same as +Path.file.backfind(path)+. See {#backfind}.
# File lib/path.rb, line 48 def backfind(path) file(caller).backfind(path) end
Configures the behavior of {Path#+}. The default is :warning
.
Path.configure(:+ => :defined) # aliased to Path#/ Path.configure(:+ => :warning) # calls Path#/ but warns Path.configure(:+ => :error) # not defined Path.configure(:+ => :string) # like String#+. Warns if $VERBOSE (-w)
@option config [:defined, :warning, :error, :string] :+ the configuration value
# File lib/path/implementation.rb, line 48 def Path.configure(config) config = config[:+] unless [:defined, :warning, :error, :string].include? config raise ArgumentError, "Invalid configuration: #{config.inspect}" end if @plus_configured raise "Path.configure(:+ => ...) has already been called: #{@plus_configured}" end remove_method :+ if method_defined? :+ case config when :defined alias :+ :/ when :warning def +(other) warn 'Warning: use of deprecated Path#+ as Path#/: ' << "#{inspect} + #{other.inspect}\n#{caller.first}" self / other end when :error # nothing to do, the method has been removed when :string def +(other) warn 'Warning: use of deprecated Path#+ as String#+: ' << "#{inspect} + #{other.inspect}\n#{caller.first}" if $VERBOSE Path.new(to_s + other.to_s) end end @plus_configured = caller.first end
{Path} to the directory of this file: +Path(__FILE__).dir+.
# File lib/path.rb, line 19 def dir(from = nil) from ||= caller # this can not be moved as a default argument, JRuby optimizes it file(from).dir end
add a leading . to ext
if missing. Returns '' if ext
is empty.
# File lib/path/parts.rb, line 10 def self.dotted_ext(ext) ext = ext.to_s and (ext.empty? or ext.start_with?('.')) ? ext : ".#{ext}" end
{Path} to the current file +Path(__FILE__)+.
# File lib/path.rb, line 12 def file(from = nil) from ||= caller # this can not be moved as a default argument, JRuby optimizes it # v This : is there to define a group without capturing new(from.first.rpartition(/:\d+(?:$|:in )/).first).expand end
Returns the current working directory as a Path
. See Dir.getwd
.
# File lib/path/dir.rb, line 22 def Path.getwd new Dir.getwd end
Returns or yields Path
objects. See Dir.glob
. @yieldparam [Path] path
# File lib/path/dir.rb, line 13 def glob(pattern, flags = 0) if block_given? Dir.glob(pattern, flags) { |f| yield new(f) } else Dir.glob(pattern, flags).map(&Path) end end
Escape the path to be suitable for globbing (so it contains no globbing special characters)
# File lib/path/dir.rb, line 7 def glob_escape(path) path.gsub(/\[|\]|\*|\?|\{|\}/, '\\\\' + '\0') end
JSON loading.
# File lib/path/identity.rb, line 139 def self.json_create json new json['data'] end
A matcher responding to ===. Useful for case clauses, grep, etc. See {Path.like?}.
case obj when Path.like then Path(obj) # ... end
# File lib/path/identity.rb, line 36 def like @like ||= begin matcher = Object.new def matcher.===(object) Path.like?(object) end matcher end end
Creates a new Path
. See {#initialize}.
# File lib/path/identity.rb, line 6 def new(*args) if args.size == 1 and Path === args.first args.first else super(*args) end end
Creates a new Path
. If multiple arguments are given, they are joined with File.join. The path will have File::ALT_SEPARATOR replaced with '/' and if it begins with a '~', it will be expanded (using File.expand_path). Accepts an Array of Strings, a Tempfile, anything that respond to path
, to_path
or to_str
with a String and defaults to calling to_s
.
@param parts [Array<String>, Tempfile, to_path
, path
, to_str
, to_s
] the path-like object(s)
# File lib/path/identity.rb, line 57 def initialize(*parts) path = parts.size > 1 ? File.join(*parts) : parts.first @path = case path when Tempfile @_tmpfile = path # We would not want it to be GC'd path.path.dup when String path.dup else if path.respond_to? :to_path and String === path.to_path path.to_path.dup elsif path.respond_to? :path and String === path.path path.path.dup elsif path.respond_to? :to_str and String === path.to_str path.to_str.dup else path.to_s.dup end end init end
A {Path} to the null device on the current platform.
# File lib/path.rb, line 38 def null new case RbConfig::CONFIG['host_os'] when /mswin|mingw/ then 'NUL' when /amiga/i then 'NIL:' when /openvms/i then 'NL:' else '/dev/null' end end
remove the leading . of ext
if present.
# File lib/path/parts.rb, line 5 def self.pure_ext(ext) ext = ext.to_s and ext.start_with?('.') ? ext[1..-1] : ext end
Registers a new loader (a block which will be called with the Path
to load) for the given extensions (either with the leading dot or not)
Path.register_loader('.marshal') { |file| Marshal.load file.read }
@yieldparam [Path] path
# File lib/path/load.rb, line 13 def self.register_loader(*extensions, &loader) extensions.each { |ext| LOADERS[Path.dotted_ext(ext)] = loader } end
{Path} relative to the directory of this file.
# File lib/path.rb, line 25 def relative(path, from = nil) from ||= caller # this can not be moved as a default argument, JRuby optimizes it new(path).expand dir(from) end
Requires all .rb files recursively under directory
(or the current file's directory if not given).
The order of requires is alphabetical, but files having the same basename as a directory are required before files in this directory.
# in bar.rb Path.require_tree # require in this order: # foo.rb # foo/ext.rb # foo/sub.rb
@param directory [String] the directory to search,
or the current file's directory.
@option options [Array<String>] :except ([])
a list of prefixes to ignore, relative to +directory+.
# File lib/path/require_tree.rb, line 22 def self.require_tree(directory = nil, options = {}) directory, options = nil, directory if Hash === directory source = Path.file(caller) directory = Path.relative(directory || source.dir, caller) except = options[:except] || [] directory.glob('**/*.rb').reject { |path| except.any? { |prefix| (path % directory).path.start_with?(prefix) } }.sort! { |a,b| if b.inside?(a.rm_ext) -1 elsif a.inside?(b.rm_ext) +1 else a <=> b end }.each { |file| require file.path unless source == file } end
@yieldparam [Path] tmpdir
# File lib/path.rb, line 82 def tmpchdir(prefix_suffix = nil, *rest) tmpdir do |dir| dir.chdir do yield dir end end end
@yieldparam [Path] tmpdir
# File lib/path.rb, line 68 def tmpdir(prefix_suffix = nil, *rest) require 'tmpdir' dir = new Dir.mktmpdir(prefix_suffix, *rest) if block_given? begin yield dir ensure FileUtils.remove_entry_secure(dir) rescue nil end end dir end
@yieldparam [Path] tmpfile
# File lib/path.rb, line 53 def tmpfile(basename = '', tmpdir = nil, options = nil) tempfile = Tempfile.new(basename, *[tmpdir, options].compact) file = new tempfile if block_given? begin yield file ensure tempfile.close! end end file end
A class constructor.
%w[foo bar].map(&Path) # == [Path('foo'), Path('bar')]
# File lib/path/identity.rb, line 18 def to_proc lambda { |path| new(path) } end
A {Path} to the home directory of user
(defaults to the current user). The form with an argument (user
) is not supported on Windows.
# File lib/path.rb, line 32 def ~(user = '') new("~#{user}") end
Public Instance Methods
# File lib/path/implementation.rb, line 61 def +(other) warn 'Warning: use of deprecated Path#+ as Path#/: ' << "#{inspect} + #{other.inspect}\n#{caller.first}" self / other end
Path#/ appends a path fragment to self
to produce a new Path
.
p = Path.new("/usr") # => #<Path /usr> p / "bin/ruby" # => #<Path /usr/bin/ruby> p / "/etc/passwd" # => #<Path /etc/passwd>
This method doesn't access the file system, it is pure string manipulation.
# File lib/path/implementation.rb, line 36 def /(other) Path.new(plus(@path, other.to_s)) end
Provides a case-sensitive comparison operator for paths.
# File lib/path/identity.rb, line 98 def <=>(other) return nil unless Path === other @path.tr('/', "\0") <=> other.path.tr('/', "\0") end
Compare this path 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/path/identity.rb, line 92 def == other Path === other and @path == other.path end
Marshal dumping.
# File lib/path/identity.rb, line 144 def _dump level @path end
Whether a path is absolute.
# File lib/path/predicates.rb, line 5 def absolute? is_absolute?(@path) end
Adds ext
as an extension to path
. Handle both extensions with or without leading dot. No-op if ext
is empty?
.
Path('file').add_extension('txt') # => #<Path file.txt>
# File lib/path/parts.rb, line 57 def add_extension(ext) return self if ext.to_s.empty? Path.new @path + Path.dotted_ext(ext) end
Appends contents
to self
. See IO.write
or +IO#write+.
# File lib/path/io.rb, line 69 def append(contents, open_args = {}) open_args[:mode] = 'a' IO.write(@path, contents, open_args) end
Iterates over each element in the given path in ascending order.
Path.new('/path/to/some/file.rb').ascend { |v| p v } #<Path /path/to/some/file.rb> #<Path /path/to/some> #<Path /path/to> #<Path /path> #<Path /> Path.new('path/to/some/file.rb').ascend { |v| p v } #<Path path/to/some/file.rb> #<Path path/to/some> #<Path path/to> #<Path path>
It doesn't access the filesystem. @yieldparam [Path] path
# File lib/path/parts.rb, line 137 def ascend return to_enum(:ascend) unless block_given? path = @path yield self while r = chop_basename(path) path, = r break if path.empty? yield Path.new(del_trailing_separator(path)) end end
Returns the last access time. See File.atime
.
# File lib/path/file.rb, line 5 def atime File.atime(@path) end
Ascends the parents until it finds the given path
.
Path.backfind('lib') # => the lib folder
It accepts an XPath-like context:
Path.backfind('.[.git]') # => the root of the repository
# File lib/path.rb, line 110 def backfind(path) condition = path[/\[(.*)\]$/, 1] || '' path = $` unless condition.empty? result = ancestors.find { |ancestor| (ancestor/path/condition).exist? } result/path if result end
Returns the last component of the path. See File.basename
.
# File lib/path/parts.rb, line 15 def base(*args) Path.new(File.basename(@path, *args)) end
Returns all the bytes from the file, or the first N
if specified. See IO.binread
.
# File lib/path/io.rb, line 26 def binread(*args) IO.binread(@path, *args) end
Writes contents
to self
. See IO.binwrite
.
# File lib/path/io.rb, line 58 def binwrite(contents, *open_args) IO.binwrite(@path, contents, *open_args) end
See File.blockdev?
.
# File lib/path/file_predicates.rb, line 7 def blockdev? File.blockdev?(@path) end
See File.chardev?
.
# File lib/path/file_predicates.rb, line 12 def chardev? File.chardev?(@path) end
Changes the current working directory of the process to self. See Dir.chdir. The recommended way to use it is to use the block form, or not use it all!
# File lib/path/dir.rb, line 94 def chdir(&block) Dir.chdir(@path, &block) end
Returns the children of the directory (files and subdirectories, not recursive) as an array of Path
objects. By default, the returned paths will have enough information to access the files. If you set with_directory
to false
, then the returned paths will contain the filename only.
For example:
pn = Path("/usr/lib/ruby/1.8") pn.children # -> [ #<Path /usr/lib/ruby/1.8/English.rb>, #<Path /usr/lib/ruby/1.8/Env.rb>, #<Path /usr/lib/ruby/1.8/abbrev.rb>, ... ] pn.children(false) # -> [ #<Path English.rb>, #<Path Env.rb>, #<Path abbrev.rb>, ... ]
Note that the results never contain the entries .
and ..
in the directory because they are not children.
# File lib/path/dir.rb, line 115 def children(with_directory=true) with_directory = false if @path == '.' result = [] Dir.foreach(@path) { |e| next if e == '.' || e == '..' if with_directory result << Path.new(@path, e) else result << Path.new(e) end } result end
Changes permissions of path
. See File.chmod
.
# File lib/path/file.rb, line 21 def chmod(mode) File.chmod(mode, @path) end
Recusively changes permissions. See FileUtils.chmod_R
and File.chmod
.
# File lib/path/fileutils.rb, line 88 def chmod_r(mode) FileUtils.chmod_R(mode, @path) end
Changes the owner and group of path
. See File.chown
.
# File lib/path/file.rb, line 31 def chown(owner, group) File.chown(owner, group, @path) end
Recusively changes owner and group. See FileUtils.chown_R
and File.chown
.
# File lib/path/fileutils.rb, line 93 def chown_r(owner, group) FileUtils.chown_R(owner, group, @path) end
Returns a cleaned version 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/path/implementation.rb, line 18 def clean(consider_symlink = false) consider_symlink ? cleanpath_conservative : cleanpath_aggressive end
Copies the file to to
. See FileUtils.cp
.
# File lib/path/fileutils.rb, line 44 def cp(to) # TODO: remove :preserve when all implement it correctly (r31123) FileUtils.cp(@path, to, :preserve => true) end
Copies the file or directory recursively to the directory to
. See FileUtils.cp_r
.
# File lib/path/fileutils.rb, line 52 def cp_r(to) FileUtils.cp_r(@path, to) end
Returns the last change time (of the directory entry, not the file itself). See File.ctime
.
# File lib/path/file.rb, line 11 def ctime File.ctime(@path) end
Iterates over each element in the given path in descending order.
Path.new('/path/to/some/file.rb').descend { |v| p v } #<Path /> #<Path /path> #<Path /path/to> #<Path /path/to/some> #<Path /path/to/some/file.rb> Path.new('path/to/some/file.rb').descend { |v| p v } #<Path path> #<Path path/to> #<Path path/to/some> #<Path path/to/some/file.rb>
It doesn't access the filesystem. @yieldparam [Path] path
# File lib/path/parts.rb, line 114 def descend return to_enum(:descend) unless block_given? ascend.reverse_each { |v| yield v } nil end
Returns all but the last component of the path.
Don't chain this when the path is relative:
Path('.').dir # => #<Path .>
Use parent
instead. See File.dirname
.
# File lib/path/parts.rb, line 31 def dir Path.new(File.dirname(@path)) end
See File.directory?
.
# File lib/path/file_predicates.rb, line 38 def directory? File.directory?(@path) end
Iterates over the children of the directory (files and subdirectories, not recursive). By default, the yielded paths will have enough information to access the files. If you set with_directory
to false
, then the returned paths will contain the filename only.
Path("/usr/local").each_child { |f| p f } # => #<Path /usr/local/share> #<Path /usr/local/bin> #<Path /usr/local/games> #<Path /usr/local/lib> #<Path /usr/local/include> #<Path /usr/local/sbin> #<Path /usr/local/src> #<Path /usr/local/man> Path("/usr/local").each_child(false) { |f| p f } # => #<Path share> #<Path bin> #<Path games> #<Path lib> #<Path include> #<Path sbin> #<Path src> #<Path man>
@yieldparam [Path] child
# File lib/path/dir.rb, line 154 def each_child(with_directory=true, &b) children(with_directory).each(&b) end
Iterates over the entries (files and subdirectories) in the directory.
Path("/usr/local").each_entry { |entry| p entry } # => #<Path .> #<Path ..> #<Path lib> #<Path share> # ...
@deprecated Use {#each_child} instead.
This method is deprecated since it is too low level and likely useless in Ruby. But it is there for the sake of compatibility with Dir.foreach and Pathname#each_entry.
@yieldparam [Path] entry
# File lib/path/dir.rb, line 44 def each_entry(&block) Dir.foreach(@path) { |f| yield Path.new(f) } end
Iterates over each component of the path.
Path.new("/usr/bin/ruby").each_filename { |filename| ... } # yields "usr", "bin", and "ruby".
Returns an Enumerator if no block was given. @yieldparam [String] filename
# File lib/path/parts.rb, line 90 def each_filename return to_enum(__method__) unless block_given? _, names = split_names(@path) names.each { |filename| yield filename } nil end
Iterates over the lines in the file. See IO.foreach
. @yieldparam [String] line
# File lib/path/io.rb, line 12 def each_line(*args, &block) IO.foreach(@path, *args, &block) end
Return the entries (files and subdirectories) in the directory. Each Path
only contains the filename. The result may contain the current directory #<Path .> and the parent directory #<Path ..>.
Path('/usr/local').entries # => [#<Path share>, #<Path lib>, #<Path .>, #<Path ..>, <Path bin>, ...]
@deprecated Use {#children} instead.
This method is deprecated since it is too low level and likely useless in Ruby. But it is there for the sake of compatibility with Dir.entries (and Pathname#entries).
# File lib/path/dir.rb, line 88 def entries Dir.entries(@path).map(&Path) end
See File.executable?
.
# File lib/path/file_predicates.rb, line 17 def executable? File.executable?(@path) end
See File.executable_real?
.
# File lib/path/file_predicates.rb, line 22 def executable_real? File.executable_real?(@path) end
See File.exist?
.
# File lib/path/file_predicates.rb, line 27 def exist? File.exist?(@path) end
Expands path
, making it absolute. If the path is relative, it is expanded with the current working directory, unless dir
is given as an argument. See File.expand_path
.
# File lib/path/file.rb, line 112 def expand(*args) Path.new(File.expand_path(@path, *args)) end
Returns the extension, with a leading dot. See File.extname
.
# File lib/path/parts.rb, line 37 def ext File.extname(@path) end
See File.file?
.
# File lib/path/file_predicates.rb, line 44 def file? File.file?(@path) end
Iterates over the directory tree in a depth first manner, yielding a Path
for each file under “this” directory.
Returns an Enumerator if no block is given.
Since it is implemented by the standard library module Find, Find.prune
can be used to control the traversal.
If self
is .
, yielded paths begin with a filename in the current directory, not ./
.
See Find.find
.
@yieldparam [Path] path
# File lib/path/find.rb, line 16 def find return to_enum(__method__) unless block_given? require 'find' if @path == '.' Find.find(@path) { |f| yield Path.new(f.sub(%r{\A\./}, '')) } else Find.find(@path) { |f| yield Path.new(f) } end end
Return true
if the receiver matches the given pattern. See File.fnmatch?
.
# File lib/path/file_predicates.rb, line 148 def fnmatch?(pattern, *args) File.fnmatch?(pattern, @path, *args) end
Returns “type” of file (“file”, “directory”, etc). See File.ftype
.
# File lib/path/file.rb, line 42 def ftype File.ftype(@path) end
Returns or yields Path
objects. Prepends the (escaped for globbing) path
to the pattern. See Dir.glob
. @yieldparam [Path] path
# File lib/path/dir.rb, line 69 def glob(pattern, flags = 0) pattern = "#{Path.glob_escape(@path)}/#{pattern}" if block_given? Dir.glob(pattern, flags) { |f| yield Path.new(f) } else Dir.glob(pattern, flags).map(&Path) end end
See File.grpowned?
.
# File lib/path/file_predicates.rb, line 33 def grpowned? File.grpowned?(@path) end
Whether the contents of path
and file
are identical. See FileUtils.compare_file
.
# File lib/path/fileutils.rb, line 99 def has_same_contents?(file) FileUtils.compare_file(@path, file) end
The hash value of the path
.
# File lib/path/identity.rb, line 104 def hash @path.hash end
Returns the first bytes
bytes of the file. If the file size is smaller than bytes
, return the whole contents.
# File lib/path/io.rb, line 91 def head(bytes) read(bytes) end
See File.identical?
.
# File lib/path/file_predicates.rb, line 140 def identical?(path) File.identical?(@path, path) end
Psych loading.
# File lib/path/identity.rb, line 125 def init_with(coder) @path = coder['path'] init end
Whether self
is inside ancestor
, such that ancestor
is an ancestor of self
. This is pure String manipulation. Paths should be absolute. self
is considered to be inside itself.
# File lib/path.rb, line 94 def inside? ancestor @path == ancestor.to_s or @path.start_with?("#{ancestor}/") end
A representation of the path
.
# File lib/path/identity.rb, line 114 def inspect "#<Path #{@path}>" end
Install file
into path
(the “prefix”, which should be a directory). If file
is not same as path/file
, replaces it. See FileUtils.install
(arguments are swapped).
# File lib/path/fileutils.rb, line 83 def install(file, options = {}) FileUtils.install(file, @path, options) end
Joins paths.
path0.join(path1, ..., pathN) # is the same as path0 / path1 / ... / pathN
# File lib/path/implementation.rb, line 92 def join(*paths) result = nil paths.reverse_each { |path| result = Path.new(path) / result return result if result.absolute? } self / result end
Changes permissions of path
, not following symlink. See File.lchmod
.
# File lib/path/file.rb, line 26 def lchmod(mode) File.lchmod(mode, @path) end
Changes the owner and group of path
, not following symlink. See File.lchown
.
# File lib/path/file.rb, line 37 def lchown(owner, group) File.lchown(owner, group, @path) end
Path#load
helps loading data from various files. JSON and YAML loaders are provided by default. See {Path.register_loader}.
# File lib/path/load.rb, line 22 def load if LOADERS.key? ext LOADERS[ext].call(self) else raise "Unable to load #{self} (unrecognized extension)" end end
Returns the stat of path
as a File::Stat
object, not following symlink. See File.lstat
.
# File lib/path/file.rb, line 72 def lstat File.lstat(@path) end
Creates a hard link to target
and returns self.
Raises Errno::EEXIST if self already exist. See File.link
(arguments are swapped).
# File lib/path/file.rb, line 50 def make_link(target) File.link(target, @path) self end
Creates a symbolic link to target
and returns self.
Raises Errno::EEXIST if self already exist. See File.symlink
(arguments are swapped).
# File lib/path/file.rb, line 85 def make_symlink(target) File.symlink(target, @path) self end
Create the referenced directory and returns self. See Dir.mkdir
.
# File lib/path/dir.rb, line 49 def mkdir(*args) Dir.mkdir(@path, *args) self end
Creates a full path, including any intermediate directories that don't yet exist. See FileUtils.mkpath
.
# File lib/path/fileutils.rb, line 8 def mkpath FileUtils.mkpath(@path) self end
Returns true
if self
points to a mountpoint.
# File lib/path/predicates.rb, line 24 def mountpoint? begin stat1 = lstat stat2 = parent.lstat stat1.dev != stat2.dev or stat1.ino == stat2.ino rescue Errno::ENOENT false end end
Returns last modification time. See File.mtime
.
# File lib/path/file.rb, line 16 def mtime File.mtime(@path) end
Moves self
to the to
directory.
# File lib/path/fileutils.rb, line 74 def mv(to) FileUtils.mv(@path, to) to end
Opens the file for reading or writing. See File.open
. @yieldparam [File] file
# File lib/path/io.rb, line 6 def open(*args, &block) File.open(@path, *args, &block) end
See Dir.open
. @yieldparam [Dir] dir
# File lib/path/dir.rb, line 61 def opendir(&block) Dir.open(@path, &block) end
The opposite of {#inside?}.
# File lib/path.rb, line 99 def outside? ancestor !inside?(ancestor) end
See File.owned?
.
# File lib/path/file_predicates.rb, line 59 def owned? File.owned?(@path) end
Returns the parent directory. This can be chained.
# File lib/path/implementation.rb, line 25 def parent self / '..' end
See File.pipe?
.
# File lib/path/file_predicates.rb, line 49 def pipe? File.pipe?(@path) end
{#ext} without leading dot.
# File lib/path/parts.rb, line 43 def pure_ext Path.pure_ext(extname) end
Returns all data from the file, or the first bytes
bytes if specified. See IO.read
.
# File lib/path/io.rb, line 19 def read(*args) IO.read(@path, *args) end
See File.readable?
.
# File lib/path/file_predicates.rb, line 64 def readable? File.readable?(@path) end
See File.readable_real?
.
# File lib/path/file_predicates.rb, line 81 def readable_real? File.readable_real?(@path) end
Returns all the lines from the file. See IO.readlines
.
# File lib/path/io.rb, line 36 def readlines(*args) IO.readlines(@path, *args) end
Reads the symbolic link. See File.readlink
.
# File lib/path/file.rb, line 56 def readlink Path.new(File.readlink(@path)) end
Returns the real (absolute) path of self
in the actual filesystem. The real path doesn't contain symlinks or useless dots.
The last component of the real path can be nonexistent.
# File lib/path/file.rb, line 130 def realdirpath(basedir=nil) Path.new(real_path_internal(false, basedir)) end
Returns the real (absolute) path for self
in the actual filesystem not containing symlinks or useless dots.
All components of the path must exist when this method is called.
# File lib/path/file.rb, line 121 def realpath(basedir=nil) Path.new(real_path_internal(true, basedir)) end
Whether a path is relative.
# File lib/path/predicates.rb, line 10 def relative? not absolute? end
relative_path_from
returns a relative path from the given base_directory
to the receiver. They must be both relative or both absolute.
It doesn't access the filesystem and assumes no symlinks.
@raise [ArgumentError] if it cannot find a relative path:
Either the base is relative and contains '..' (in that case you can expand both paths) or the paths are absolutes and on different drives (Windows).
# File lib/path/implementation.rb, line 109 def relative_path_from(base_directory) dest = clean.path base = Path.new(base_directory).clean.path dest_prefix, dest_names = split_names(dest) base_prefix, base_names = split_names(base) unless SAME_PATHS[dest_prefix, base_prefix] raise ArgumentError, "different prefix: #{self.inspect} and #{base_directory.inspect}" end while d = dest_names.first and b = base_names.first and SAME_PATHS[d, b] dest_names.shift base_names.shift end raise ArgumentError, "base_directory has ..: #{base_directory.inspect}" if base_names.include? '..' # the number of names left in base is the ones we have to climb names = base_names.fill('..').concat(dest_names) return Path.new('.') if names.empty? Path.new(*names) end
Relocates this path somewhere else.
Without a block, this method is a simple shorcut for a longer expression that proves difficult to remember in practice:
to / (self.sub_ext(new_ext) % from)
That is, it relocates the original path to a target folder to
appended with the relative path from a source folder from
. An optional new extension can also be specified, as it is a common use case.
With a block, the relative path is passed to the block for user update, without the last extension. new_ext
is added after (or the original extension if not provided).
from = Path('pictures') to = Path('output/public/thumbnails') earth = from / 'nature/earth.jpg' earth.relocate(from, to) # => #<Path output/public/thumbnails/nature/earth.jpg> earth.relocate(from, to, '.png') { |rel| "#{rel}-200" } # => #<Path output/public/thumbnails/nature/earth-200.png>
# File lib/path.rb, line 145 def relocate(from, to, new_ext = ext, &updater) updater ||= lambda { |path| path } renamer = lambda { |rel| Path.new(updater.call(rel.rm_ext)).add_ext(new_ext) } Path.new(to) / renamer.call(self % from) end
Renames the file and returns the new Path
. See File.rename
.
# File lib/path/file.rb, line 61 def rename(to) File.rename(@path, to) Path.new(to) end
Replaces the last extension of path
with ext
. Handle both extensions with or without leading dot. Removes last extension if ext
is empty?
.
Path('main.c++').replace_extension('cc') # => #<Path main.cc>
# File lib/path/parts.rb, line 77 def replace_extension(ext) return without_extension if ext.to_s.empty? Path.new(@path[0..-extname.size-1] << Path.dotted_ext(ext)) end
Rewrites contents of self
.
Path('file').rewrite { |contents| contents.reverse }
@yieldparam [String] contents @yieldreturn [String] contents to write
# File lib/path/io.rb, line 85 def rewrite write yield read end
Removes the file using FileUtils.rm
.
# File lib/path/fileutils.rb, line 24 def rm FileUtils.rm(@path) self end
Removes the file, ignoring errors, using FileUtils.rm_f
.
# File lib/path/fileutils.rb, line 30 def rm_f FileUtils.rm_f(@path) self end
Removes the file or directory recursively, ignoring errors, using FileUtils.rm_f
.
# File lib/path/fileutils.rb, line 38 def rm_rf FileUtils.rm_rf(@path) self end
Remove the referenced directory. See Dir.rmdir
.
# File lib/path/dir.rb, line 55 def rmdir Dir.rmdir(@path) end
Deletes a directory and all beneath it. See FileUtils.rm_r
.
# File lib/path/fileutils.rb, line 15 def rmtree # The name "rmtree" is borrowed from File::Path of Perl. # File::Path provides "mkpath" and "rmtree". FileUtils.rm_r(@path) self end
Predicate for root directories. Returns true
if the path consists of consecutive slashes.
It doesn't access the filesystem. So it may return false
for some paths which points to roots such as /usr/..
.
# File lib/path/predicates.rb, line 19 def root? is_root?(@path) end
See File.setgid?
.
# File lib/path/file_predicates.rb, line 91 def setgid? File.setgid?(@path) end
See File.setuid?
.
# File lib/path/file_predicates.rb, line 86 def setuid? File.setuid?(@path) end
Equivalent of +parent.children - [self]+. Returns the siblings, the files in the same directory as the current path
. Returns only the root if path
is the root.
# File lib/path/dir.rb, line 161 def siblings(with_directory = true) if root? [self] else parent.children(with_directory) - [(with_directory ? self : basename)] end end
Returns the file size in bytes. See File.size
.
# File lib/path/file.rb, line 77 def size File.size(@path) end
See File.size?
.
# File lib/path/file_predicates.rb, line 96 def size? File.size?(@path) end
See File.socket?
.
# File lib/path/file_predicates.rb, line 54 def socket? File.socket?(@path) end
Returns the stat of path
as a File::Stat
object. See File.stat
.
# File lib/path/file.rb, line 67 def stat File.stat(@path) end
Returns the last component of the path, without the extension: base(ext)
# File lib/path/parts.rb, line 21 def stem base(ext) end
See File.sticky?
.
# File lib/path/file_predicates.rb, line 101 def sticky? File.sticky?(@path) end
See File.symlink?
.
# File lib/path/file_predicates.rb, line 106 def symlink? File.symlink?(@path) end
See IO.sysopen
.
# File lib/path/io.rb, line 41 def sysopen(*args) IO.sysopen(@path, *args) end
Returns the last bytes
bytes of the file. If the file size is smaller than bytes
, return the whole contents.
# File lib/path/io.rb, line 97 def tail(bytes) return read if size < bytes open { |f| f.seek(-bytes, IO::SEEK_END) f.read } end
JSON dumping.
# File lib/path/identity.rb, line 131 def to_json(*args) { 'json_class' => 'Path', 'data' => @path }.to_json(*args) end
Returns the path
as a Symbol.
# File lib/path/identity.rb, line 109 def to_sym @path.to_sym end
Updates access and modification time or create an empty file.
# File lib/path/fileutils.rb, line 57 def touch if exist? now = Time.now File.utime(now, now, @path) else open('w') {} end self end
{#touch} preceded by dir.
{#mkpath}.
# File lib/path/fileutils.rb, line 68 def touch! dir.mkpath touch end
Truncates the file to length
bytes. See File.truncate
.
# File lib/path/file.rb, line 91 def truncate(length) File.truncate(@path, length) end
Removes a file using File.unlink
. This is incompatible with Pathname#unlink, which can also remove directories. Use {#rmdir} or {#rm_r} for directories.
# File lib/path/file.rb, line 99 def unlink File.unlink @path end
Returns whether self
is newer than all others
. Non-existent files are older than any file. See FileUtils.uptodate?
.
# File lib/path/fileutils.rb, line 106 def uptodate?(*others) FileUtils.uptodate?(@path, others) end
Updates the access and modification times. See File.utime
.
# File lib/path/file.rb, line 105 def utime(atime, mtime) File.utime(atime, mtime, @path) end
Removes the last extension of path
.
Path('script.rb').without_extension # => #<Path script> Path('archive.tar.gz').without_extension # => #<Path archive.tar>
# File lib/path/parts.rb, line 67 def without_extension Path.new @path[0..-extname.size-1] end
See File.world_readable?
.
# File lib/path/file_predicates.rb, line 70 def world_readable? File.world_readable?(@path) end
See File.world_writable?
.
# File lib/path/file_predicates.rb, line 117 def world_writable? File.world_writable?(@path) end
See File.writable?
.
# File lib/path/file_predicates.rb, line 111 def writable? File.writable?(@path) end
See File.writable_real?
.
# File lib/path/file_predicates.rb, line 128 def writable_real? File.writable_real?(@path) end
Writes contents
to self
. See IO.write
or +IO#write+.
# File lib/path/io.rb, line 47 def write(contents, *open_args) IO.write(@path, contents, *open_args) end
YAML loading.
# File lib/path/identity.rb, line 119 def yaml_initialize(tag, ivars) @path = ivars['path'] init end
See File.zero?
. empty? is not defined in File/FileTest, but is is clearer.
# File lib/path/file_predicates.rb, line 134 def zero? File.zero?(@path) end
Private Instance Methods
# File lib/path/implementation.rb, line 193 def add_trailing_separator(path) # mutates path path << '/' unless path.end_with? '/' path end
chop_basename
(path) -> [pre-basename, basename] or nil
# File lib/path/implementation.rb, line 150 def chop_basename(path) base = File.basename(path) if base.empty? or base == '/' return nil else return path[0, path.rindex(base)], base end end
Clean the path simply by resolving and removing excess .
and ..
entries. Nothing more, nothing less.
# File lib/path/implementation.rb, line 216 def cleanpath_aggressive pre = @path names = [] while r = chop_basename(pre) pre, base = r if base == '.' # do nothing, it can be ignored elsif names.first == '..' and base != '..' # base can be ignored as we go back to its parent names.shift else names.unshift base end end remove_root_parents(pre, names) Path.new(prepend_prefix(pre, names)) end
# File lib/path/implementation.rb, line 234 def cleanpath_conservative path = @path pre, names = split_names(path) remove_root_parents(pre, names) if names.empty? Path.new(File.dirname(pre)) else names << '.' if names.last != '..' and File.basename(path) == '.' result = prepend_prefix(pre, names) if names.last != '.' and names.last != '..' and has_trailing_separator?(path) Path.new(add_trailing_separator(result)) else Path.new(result) end end end
# File lib/path/implementation.rb, line 198 def del_trailing_separator(path) if r = chop_basename(path) pre, basename = r pre + basename elsif %r{/+\z} =~ path $` + File.dirname(path)[%r{/*\z}] else path end end
# File lib/path/implementation.rb, line 189 def has_trailing_separator?(path) !is_root?(path) and path.end_with?('/') end
# File lib/path/implementation.rb, line 133 def init @path = validate(@path) taint if @path.tainted? @path.freeze freeze end
# File lib/path/implementation.rb, line 159 def is_absolute?(path) path.start_with?('/') or (path =~ /\A[a-zA-Z]:\// and is_root?($&)) end
# File lib/path/implementation.rb, line 163 def is_root?(path) chop_basename(path) == nil and path.include?('/') end
# File lib/path/implementation.rb, line 251 def plus(prefix, rel) return rel if is_absolute?(rel) _, names = split_names(rel) loop do # break if that was the last segment break unless r = chop_basename(prefix) prefix, name = r next if name == '.' # break if we can't resolve anymore if name == '..' or names.first != '..' prefix << name break end names.shift end remove_root_parents(prefix, names) has_prefix = chop_basename(prefix) if names.empty? has_prefix ? prefix : File.dirname(prefix) else suffix = File.join(*names) has_prefix ? File.join(prefix, suffix) : prefix + suffix end end
# File lib/path/implementation.rb, line 177 def prepend_prefix(prefix, relnames) relpath = File.join(*relnames) if relpath.empty? File.dirname(prefix) elsif prefix.include? '/' # safe because File.dirname returns a new String add_trailing_separator(File.dirname(prefix)) << relpath else prefix + relpath end end
# File lib/path/compatibility.rb, line 5 def real_path_internal(strict = false, basedir = nil) strict ? File.realpath(@path, basedir) : File.realdirpath(@path, basedir) end
# File lib/path/compatibility.rb, line 9 def realpath_rec(prefix, unresolved, h, strict, last = true) resolved = [] until unresolved.empty? n = unresolved.shift if n == '..' resolved.pop else path = prepend_prefix(prefix, resolved + [n]) if h.include? path if h[path] == :resolving raise Errno::ELOOP.new(path) else prefix, *resolved = h[path] end else begin s = File.lstat(path) rescue Errno::ENOENT => e raise e if strict || !last || !unresolved.empty? resolved << n break end if s.symlink? h[path] = :resolving link_prefix, link_names = split_names(File.readlink(path)) if link_prefix == '' # if link is relative link_prefix, link_names = prefix, resolved.concat(link_names) end prefix, *resolved = h[path] = realpath_rec(link_prefix, link_names, h, strict, unresolved.empty?) else resolved << n h[path] = [prefix, *resolved] end end end end return prefix, *resolved end
remove '..' segments since root's parent is root
# File lib/path/implementation.rb, line 210 def remove_root_parents(prefix, names) names.shift while names.first == '..' if is_root?(prefix) end
split_names
(path) -> prefix, [name, …]
# File lib/path/implementation.rb, line 168 def split_names(path) names = [] while r = chop_basename(path) path, basename = r names.unshift basename if basename != '.' end return path, names end
# File lib/path/implementation.rb, line 141 def validate(path) raise ArgumentError, "path contains a null byte: #{path.inspect}" if path.include? "\0" path.gsub!(File::ALT_SEPARATOR, '/') if File::ALT_SEPARATOR path = del_trailing_separator(path) path = File.expand_path(path) if path.start_with? '~' path end