Class: Build::Files::Path

Inherits:
Object
  • Object
show all
Includes:
Comparable
Defined in:
lib/build/files/path.rb,
lib/build/files/glob.rb,
lib/build/files/paths.rb,
lib/build/files/system.rb

Overview

Represents a file path with an absolute root and a relative offset:

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(full_path, root = nil, relative_path = nil) ⇒ Path

Both paths must be full absolute paths, and path must have root as an prefix.



96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/build/files/path.rb', line 96

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
end

Instance Attribute Details

#full_pathObject (readonly)

Returns the value of attribute full_path.



110
111
112
# File 'lib/build/files/path.rb', line 110

def full_path
  @full_path
end

#rootObject (readonly)

Returns the value of attribute root.



109
110
111
# File 'lib/build/files/path.rb', line 109

def root
  @root
end

Class Method Details

.[](path) ⇒ Object

Convert a path-like object to a Path instance.



91
92
93
# File 'lib/build/files/path.rb', line 91

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

.components(path) ⇒ Object

Returns a list of components for a path, either represented as a Path instance or a String.



33
34
35
36
37
38
39
# File 'lib/build/files/path.rb', line 33

def self.components(path)
	if Path === path
		path.components
	else
		path.split(File::SEPARATOR)
	end
end

.currentObject

Get the current working directory as a path.



12
13
14
# File 'lib/build/files/path.rb', line 12

def self.current
	self.new(::Dir.pwd)
end

.expand(path, root = Dir.getwd) ⇒ Object

Expand a path within a given root.



239
240
241
242
243
244
245
# File 'lib/build/files/path.rb', line 239

def self.expand(path, root = Dir.getwd)
	if path.start_with?(File::SEPARATOR)
		self.new(path)
	else
		self.join(root, path)
	end
end

.join(root, relative_path) ⇒ Object

Join a root and relative path to create a new Path.



234
235
236
# File 'lib/build/files/path.rb', line 234

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

.prefix_length(a, b) ⇒ Object

Returns the length of the prefix which is shared by two strings.



28
29
30
# File 'lib/build/files/path.rb', line 28

def self.prefix_length(a, b)
	[a.size, b.size].min.times{|i| return i if a[i] != b[i]}
end

.relative_path(root, full_path) ⇒ Object

Compute the relative path from root to full path.



79
80
81
82
83
84
85
86
# File 'lib/build/files/path.rb', line 79

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

.root(path) ⇒ Object

Get the root directory of a path.



44
45
46
47
48
49
50
# File 'lib/build/files/path.rb', line 44

def self.root(path)
	if Path === path
		path.root
	else
		File.dirname(path)
	end
end

.shortest_path(path, root) ⇒ Object

Compute the shortest relative path from root to path.



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/build/files/path.rb', line 56

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
	
	components = [".."] * up + path_components[i..-1]
	
	if components.empty?
		return "."
	else
		return File.join(components)
	end
end

.split(path) ⇒ Object

Split a path into directory, filename, and extension components.



19
20
21
22
23
24
25
# File 'lib/build/files/path.rb', line 19

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

Instance Method Details

#+(path) ⇒ Object

Add a path component to the current path.

Parameters:

  • path (String, nil)

    (Optionally) the path to append.



181
182
183
184
185
186
187
# File 'lib/build/files/path.rb', line 181

def +(path)
	if path
		self.class.new(File.join(@full_path, path), @root)
	else
		self
	end
end

#/(path) ⇒ Object

Use the current path to define a new root, with an optional sub-path.

Parameters:

  • path (String, nil)

    (Optionally) the path to append.



191
192
193
194
195
196
197
# File 'lib/build/files/path.rb', line 191

def /(path)
	if path
		self.class.new(File.join(self, path), self)
	else
		self.class.new(self, self)
	end
end

#<=>(other) ⇒ Object

Compare this path with another for sorting.



297
298
299
# File 'lib/build/files/path.rb', line 297

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

#append(extension) ⇒ Object

Append an extension to the path.



175
176
177
# File 'lib/build/files/path.rb', line 175

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

#basenameObject

Get the basename of the path.



128
129
130
# File 'lib/build/files/path.rb', line 128

def basename
	self.parts.last
end

#componentsObject Also known as: parts

Get the path components as an array.



122
123
124
# File 'lib/build/files/path.rb', line 122

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

#copy(destination) ⇒ Object

Copy the path to a destination.



35
36
37
38
39
40
41
# File 'lib/build/files/system.rb', line 35

def copy(destination)
	if directory?
		destination.create
	else
		FileUtils.cp(self.to_s, destination.to_s)
	end
end

#directory?Boolean

Checks if the path refers to a directory.

Returns:

  • (Boolean)


60
61
62
# File 'lib/build/files/system.rb', line 60

def directory?
	File.directory?(self.to_s)
end

#eql?(other) ⇒ Boolean

Check equality with another path.

Returns:

  • (Boolean)


288
289
290
# File 'lib/build/files/path.rb', line 288

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

#exist?Boolean

Checks if the file exists in the local file system.

Returns:

  • (Boolean)


55
56
57
# File 'lib/build/files/system.rb', line 55

def exist?
	File.exist?(self.to_s)
end

#file?Boolean

Check if the path refers to a regular file.

Returns:

  • (Boolean)


66
67
68
# File 'lib/build/files/system.rb', line 66

def file?
	File.file?(self.to_s)
end

#for_appendingObject

Get file opening arguments for appending.



322
323
324
# File 'lib/build/files/path.rb', line 322

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

#for_readingObject

Get file opening arguments for reading.



310
311
312
# File 'lib/build/files/path.rb', line 310

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

#for_writingObject

Get file opening arguments for writing.



316
317
318
# File 'lib/build/files/path.rb', line 316

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

#glob(pattern) ⇒ Object

Create a glob pattern matcher for files under this path.



14
15
16
# File 'lib/build/files/glob.rb', line 14

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

#hashObject

Compute the hash value for this path.



281
282
283
# File 'lib/build/files/path.rb', line 281

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

#inspectObject

Generate a string representation for debugging.



275
276
277
# File 'lib/build/files/path.rb', line 275

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

#lengthObject Also known as: size

Get the length of the full path.



114
115
116
# File 'lib/build/files/path.rb', line 114

def length
	@full_path.length
end

#list(*relative_paths) ⇒ Object

Create a paths list from relative paths under this path.



84
85
86
# File 'lib/build/files/paths.rb', line 84

def list(*relative_paths)
	Paths.directory(self, relative_paths)
end

#match(pattern, flags = 0) ⇒ Object

Match a path with a given pattern, using ‘File#fnmatch`.



302
303
304
305
306
# File 'lib/build/files/path.rb', line 302

def match(pattern, flags = 0)
	path = pattern.start_with?("/") ? full_path : relative_path
	
	return File.fnmatch(pattern, path, flags)
end

#mkpathObject Also known as: create

Recursively create a directory hierarchy for the given path.



88
89
90
# File 'lib/build/files/system.rb', line 88

def mkpath
	FileUtils.mkpath(self.to_s)
end

#modified_timeObject

The time the file was last modified.



83
84
85
# File 'lib/build/files/system.rb', line 83

def modified_time
	File.mtime(self.to_s)
end

#open(mode, &block) ⇒ Object

Open a file with the specified mode.



15
16
17
# File 'lib/build/files/system.rb', line 15

def open(mode, &block)
	File.open(self.to_s, mode, &block)
end

#parentObject

Get the parent directory path.



134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/build/files/path.rb', line 134

def parent
	root = @root
	full_path = File.dirname(@full_path)
	
	while root.size > full_path.size
		root = Path.root(root)
	end
	
	if root.size == full_path.size
		root = Path.root(root)
	end
	
	self.class.new(full_path, root)
end

#read(mode = File::RDONLY) ⇒ Object

Read the entire contents of the file.



20
21
22
23
24
# File 'lib/build/files/system.rb', line 20

def read(mode = File::RDONLY)
	open(mode) do |file|
		file.read
	end
end

#readable?Boolean

Check if the file is readable.

Returns:

  • (Boolean)


78
79
80
# File 'lib/build/files/system.rb', line 78

def readable?
	File.readable?(self.to_s)
end

#rebase(root) ⇒ Object

Rebase the path to a new root directory.



202
203
204
# File 'lib/build/files/path.rb', line 202

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

#relative_partsObject

Split the relative path into directory and basename components.



166
167
168
169
170
# File 'lib/build/files/path.rb', line 166

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

#relative_pathObject

Get the relative path from the root.



160
161
162
# File 'lib/build/files/path.rb', line 160

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

#rmObject Also known as: delete

Recursively delete the given path and all contents.



95
96
97
# File 'lib/build/files/system.rb', line 95

def rm
	FileUtils.rm_rf(self.to_s)
end

#shortest_path(root) ⇒ Object

Compute the shortest path from this path to a root.



250
251
252
# File 'lib/build/files/path.rb', line 250

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

#start_with?(*args) ⇒ Boolean

Check if the path starts with the given prefix.

Returns:

  • (Boolean)


152
153
154
# File 'lib/build/files/path.rb', line 152

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

#statObject

Get file statistics.



50
51
52
# File 'lib/build/files/system.rb', line 50

def stat
	File.stat(self.to_s)
end

#symlink?Boolean

Check if the path is a symbolic link.

Returns:

  • (Boolean)


72
73
74
# File 'lib/build/files/system.rb', line 72

def symlink?
	File.symlink?(self.to_s)
end

#to_pathObject

Convert the path to a path string.



262
263
264
# File 'lib/build/files/path.rb', line 262

def to_path
	@full_path
end

#to_sObject

Convert the path to a string representation.



268
269
270
271
# File 'lib/build/files/path.rb', line 268

def to_s
	# It's not guaranteed to be string.
	@full_path.to_s
end

#to_strObject

Convert the path to a string.



256
257
258
# File 'lib/build/files/path.rb', line 256

def to_str
	@full_path.to_str
end

#touchObject

Touch the file, changing it’s last modified time.



44
45
46
# File 'lib/build/files/system.rb', line 44

def touch
	FileUtils.touch(self.to_s)
end

#with(root: @root, extension: nil, basename: false) ⇒ Object

Create a modified path with new root, extension, or basename.



211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/build/files/path.rb', line 211

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

#write(buffer, mode = File::CREAT|File::TRUNC|File::WRONLY) ⇒ Object

Write a buffer to the file, creating it if it doesn’t exist.



27
28
29
30
31
# File 'lib/build/files/system.rb', line 27

def write(buffer, mode = File::CREAT|File::TRUNC|File::WRONLY)
	open(mode) do |file|
		file.write(buffer)
	end
end