class Build::Files::Path

def self.[] path

def self.[] path
	self === path ? path : self.new(path.to_s)
end

def self.components(path)

Returns a list of components for a path, either represented as a Path instance or a String.
def self.components(path)
	if Path === path
		path.components
	else
		path.split(File::SEPARATOR)
	end
end

def self.expand(subpath, root = Dir.getwd)

Expand a subpath within a given root, similar to `File.expand_path`
def self.expand(subpath, root = Dir.getwd)
	if subpath.start_with? File::SEPARATOR
		self.new(subpath)
	else
		self.join(root, subpath)
	end
end

def self.join(root, relative_path)

def self.join(root, relative_path)
	self.new(File.join(root, relative_path), root)
end

def self.prefix_length(a, b)

Returns the length of the prefix which is shared by two strings.
def self.prefix_length(a, b)
	[a.size, b.size].min.times{|i| return i if a[i] != b[i]}
end

def self.relative_path(root, full_path)

def self.relative_path(root, full_path)
	relative_offset = root.length
	
	# Deal with the case where the root may or may not end with the path separator:
	relative_offset += 1 unless root.end_with?(File::SEPARATOR)
	
	return full_path.slice(relative_offset..-1)
end

def self.shortest_path(path, root)

Return the shortest relative path to get to path from root. Root should be a directory with which you are computing the relative path.
def self.shortest_path(path, root)
	path_components = Path.components(path)
	root_components = Path.components(root)
	
	# Find the common prefix:
	i = prefix_length(path_components, root_components) || 0
	
	# The difference between the root path and the required path, taking into account the common prefix:
	up = root_components.size - i
	
	return File.join([".."] * up + path_components[i..-1])
end

def self.split(path)

def self.split(path)
	# Effectively dirname and basename:
	dirname, separator, filename = path.rpartition(File::SEPARATOR)
	filename, dot, extension = filename.rpartition('.')
	
	return dirname + separator, filename, dot + extension
end

def +(path)

Parameters:
  • path (String, nil) -- (Optionally) the path to append.
def +(path)
	if path
		self.class.new(File.join(@full_path, path), @root)
	else
		self
	end
end

def /(path)

Parameters:
  • path (String, nil) -- (Optionally) the path to append.
def /(path)
	if path
		self.class.new(File.join(self, path), self)
	else
		self.class.new(self, self)
	end
end

def ==(other)

def ==(other)
	self.to_s == other.to_s
end

def append(extension)

def append(extension)
	self.class.new(@full_path + extension, @root)
end

def basename

def basename
	self.parts.last
end

def components

def components
	@components ||= @full_path.split(File::SEPARATOR).freeze
end

def directory?

Checks if the path refers to a directory.
def directory?
	File.directory? self
end

def eql?(other)

def eql?(other)
	self.class.eql?(other.class) and @root.eql?(other.root) and @full_path.eql?(other.full_path)
end

def exist?

Checks if the file exists in the local file system.
def exist?
	File.exist? self
end

def file?

def file?
	File.file? self
end

def for_appending

def for_appending
	[@full_path, File::CREAT|File::APPEND|File::WRONLY]
end

def for_reading

def for_reading
	[@full_path, File::RDONLY]
end

def for_writing

def for_writing
	[@full_path, File::CREAT|File::TRUNC|File::WRONLY]
end

def glob(pattern)

def glob(pattern)
	Glob.new(self, pattern)
end

def hash

def hash
	[@root, @full_path].hash
end

def initialize(full_path, root = nil, relative_path = nil)

Both paths must be full absolute paths, and path must have root as an prefix.
def initialize(full_path, root = nil, relative_path = nil)
	# This is the object identity:
	@full_path = full_path
	
	if root
		@root = root
		@relative_path = relative_path
	else
		# Effectively dirname and basename:
		@root, _, @relative_path = full_path.rpartition(File::SEPARATOR)
	end
	
	# This improves the cost of hash/eql? slightly but the root cannot be deconstructed if it was an instance of Path.
	# @root = @root.to_s
end

def inspect

def inspect
	"#{@root.inspect}/#{relative_path.inspect}"
end

def length

def length
	@full_path.length
end

def match(pattern, flags = 0)

Match a path with a given pattern, using `File#fnmatch`.
def match(pattern, flags = 0)
	path = pattern.start_with?('/') ? full_path : relative_path
	
	return File.fnmatch(pattern, path, flags)
end

def mkpath

Recursively create a directory hierarchy for the given path.
def mkpath
	FileUtils.mkpath self
end

def modified_time

The time the file was last modified.
def modified_time
	File.mtime self
end

def open(mode, &block)

Open a file with the specified mode.
def open(mode, &block)
	File.open(self, mode, &block)
end

def read(mode = File::RDONLY)

Read the entire contents of the file.
def read(mode = File::RDONLY)
	open(mode) do |file|
		file.read
	end
end

def readable?

def readable?
	File.readable? self
end

def rebase(root)

def rebase(root)
	self.class.new(File.join(root, relative_path), root)
end

def relative_parts

def relative_parts
	dirname, _, basename = self.relative_path.rpartition(File::SEPARATOR)
	
	return dirname, basename
end

def relative_path

def relative_path
	@relative_path ||= Path.relative_path(@root.to_s, @full_path).freeze
end

def rm

Recursively delete the given path and all contents.
def rm
	FileUtils.rm_rf self
end

def shortest_path(root)

def shortest_path(root)
	self.class.shortest_path(self, root)
end

def start_with?(*args)

def start_with?(*args)
	@full_path.start_with?(*args)
end

def stat

def stat
	File.stat self
end

def symlink?

def symlink?
	File.symlink? self
end

def to_path

def to_path
	@full_path
end

def to_s

def to_s
	@full_path
end

def to_str

def to_str
	@full_path
end

def touch

Touch the file, changing it's last modified time.
def touch
	FileUtils.touch self
end

def with(root: @root, extension: nil, basename: false)

def with(root: @root, extension: nil, basename: false)
	relative_path = self.relative_path
	
	if basename
		dirname, filename, _ = self.class.split(relative_path)
		
		# Replace the filename if the basename is supplied:
		filename = basename if basename.is_a? String
		
		relative_path = dirname + filename
	end
	
	if extension
		relative_path = relative_path + extension
	end
	
	self.class.new(File.join(root, relative_path), root, relative_path)
end

def write(buffer, mode = File::CREAT|File::TRUNC|File::WRONLY)

Write a buffer to the file, creating it if it doesn't exist.
def write(buffer, mode = File::CREAT|File::TRUNC|File::WRONLY)
	open(mode) do |file|
		file.write(buffer)
	end
end