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/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.



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/build/files/path.rb', line 75

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

Instance Attribute Details

#full_pathObject (readonly)

Returns the value of attribute full_path.



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

def full_path
  @full_path
end

#rootObject (readonly)

Returns the value of attribute root.



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

def root
  @root
end

Class Method Details

.[](path) ⇒ Object



70
71
72
# File 'lib/build/files/path.rb', line 70

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.



39
40
41
42
43
44
45
# File 'lib/build/files/path.rb', line 39

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

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

Expand a subpath within a given root, similar to ‘File.expand_path`



174
175
176
177
178
179
180
# File 'lib/build/files/path.rb', line 174

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

.join(root, relative_path) ⇒ Object



169
170
171
# File 'lib/build/files/path.rb', line 169

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.



34
35
36
# File 'lib/build/files/path.rb', line 34

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



61
62
63
64
65
66
67
68
# File 'lib/build/files/path.rb', line 61

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

.shortest_path(path, root) ⇒ Object

Return the shortest relative path to get to path from root. Root should be a directory with which you are computing the relative path.



48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/build/files/path.rb', line 48

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

.split(path) ⇒ Object



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

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.



128
129
130
131
132
133
134
# File 'lib/build/files/path.rb', line 128

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.



138
139
140
141
142
143
144
# File 'lib/build/files/path.rb', line 138

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

#<=>(other) ⇒ Object



212
213
214
# File 'lib/build/files/path.rb', line 212

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

#append(extension) ⇒ Object



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

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

#basenameObject



102
103
104
# File 'lib/build/files/path.rb', line 102

def basename
	self.parts.last
end

#componentsObject Also known as: parts



98
99
100
# File 'lib/build/files/path.rb', line 98

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

#directory?Boolean

Checks if the path refers to a directory.

Returns:

  • (Boolean)


63
64
65
# File 'lib/build/files/system.rb', line 63

def directory?
	File.directory? self
end

#eql?(other) ⇒ Boolean

Returns:

  • (Boolean)


206
207
208
# File 'lib/build/files/path.rb', line 206

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)


58
59
60
# File 'lib/build/files/system.rb', line 58

def exist?
	File.exist? self
end

#file?Boolean

Returns:

  • (Boolean)


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

def file?
	File.file? self
end

#for_appendingObject



231
232
233
# File 'lib/build/files/path.rb', line 231

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

#for_readingObject



223
224
225
# File 'lib/build/files/path.rb', line 223

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

#for_writingObject



227
228
229
# File 'lib/build/files/path.rb', line 227

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

#glob(pattern) ⇒ Object



26
27
28
# File 'lib/build/files/glob.rb', line 26

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

#hashObject



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

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

#inspectObject



198
199
200
# File 'lib/build/files/path.rb', line 198

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

#lengthObject



94
95
96
# File 'lib/build/files/path.rb', line 94

def length
	@full_path.length
end

#match(pattern, flags = 0) ⇒ Object

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



217
218
219
220
221
# File 'lib/build/files/path.rb', line 217

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.



85
86
87
# File 'lib/build/files/system.rb', line 85

def mkpath
	FileUtils.mkpath self
end

#modified_timeObject

The time the file was last modified.



80
81
82
# File 'lib/build/files/system.rb', line 80

def modified_time
	File.mtime self
end

#open(mode, &block) ⇒ Object

Open a file with the specified mode.



30
31
32
# File 'lib/build/files/system.rb', line 30

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

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

Read the entire contents of the file.



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

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

#readable?Boolean

Returns:

  • (Boolean)


75
76
77
# File 'lib/build/files/system.rb', line 75

def readable?
	File.readable? self
end

#rebase(root) ⇒ Object



146
147
148
# File 'lib/build/files/path.rb', line 146

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

#relative_partsObject



116
117
118
119
120
# File 'lib/build/files/path.rb', line 116

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

#relative_pathObject



112
113
114
# File 'lib/build/files/path.rb', line 112

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

#rmObject Also known as: delete

Recursively delete the given path and all contents.



92
93
94
# File 'lib/build/files/system.rb', line 92

def rm
	FileUtils.rm_rf self
end

#shortest_path(root) ⇒ Object



182
183
184
# File 'lib/build/files/path.rb', line 182

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

#start_with?(*args) ⇒ Boolean

Returns:

  • (Boolean)


106
107
108
# File 'lib/build/files/path.rb', line 106

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

#statObject



53
54
55
# File 'lib/build/files/system.rb', line 53

def stat
	File.stat self
end

#symlink?Boolean

Returns:

  • (Boolean)


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

def symlink?
	File.symlink? self
end

#to_pathObject



190
191
192
# File 'lib/build/files/path.rb', line 190

def to_path
	@full_path
end

#to_sObject



194
195
196
# File 'lib/build/files/path.rb', line 194

def to_s
	@full_path
end

#to_strObject



186
187
188
# File 'lib/build/files/path.rb', line 186

def to_str
	@full_path
end

#touchObject

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



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

def touch
	FileUtils.touch self
end

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



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/build/files/path.rb', line 150

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.



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

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