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.



93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/build/files/path.rb', line 93

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.



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

def full_path
  @full_path
end

#rootObject (readonly)

Returns the value of attribute root.



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

def root
  @root
end

Class Method Details

.[](path) ⇒ Object



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

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.



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

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

.currentObject



25
26
27
# File 'lib/build/files/path.rb', line 25

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

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

Expand a path within a given root.



206
207
208
209
210
211
212
# File 'lib/build/files/path.rb', line 206

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



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

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.



38
39
40
# File 'lib/build/files/path.rb', line 38

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



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



51
52
53
54
55
56
57
# File 'lib/build/files/path.rb', line 51

def self.root(path)
	if Path === path
		path.root
	else
		File.dirname(path)
	end
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.



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/build/files/path.rb', line 60

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



29
30
31
32
33
34
35
# File 'lib/build/files/path.rb', line 29

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.



160
161
162
163
164
165
166
# File 'lib/build/files/path.rb', line 160

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.



170
171
172
173
174
175
176
# File 'lib/build/files/path.rb', line 170

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

#<=>(other) ⇒ Object



245
246
247
# File 'lib/build/files/path.rb', line 245

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

#append(extension) ⇒ Object



154
155
156
# File 'lib/build/files/path.rb', line 154

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

#basenameObject



119
120
121
# File 'lib/build/files/path.rb', line 119

def basename
	self.parts.last
end

#componentsObject Also known as: parts



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

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)


239
240
241
# File 'lib/build/files/path.rb', line 239

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



264
265
266
# File 'lib/build/files/path.rb', line 264

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

#for_readingObject



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

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

#for_writingObject



260
261
262
# File 'lib/build/files/path.rb', line 260

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



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

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

#inspectObject



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

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

#lengthObject Also known as: size



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

def length
	@full_path.length
end

#list(*relative_paths) ⇒ Object



74
75
76
# File 'lib/build/files/paths.rb', line 74

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



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

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

#parentObject



123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/build/files/path.rb', line 123

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.



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



178
179
180
# File 'lib/build/files/path.rb', line 178

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

#relative_partsObject



148
149
150
151
152
# File 'lib/build/files/path.rb', line 148

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

#relative_pathObject



144
145
146
# File 'lib/build/files/path.rb', line 144

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.



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

def rm
	FileUtils.rm_rf self
end

#shortest_path(root) ⇒ Object



214
215
216
# File 'lib/build/files/path.rb', line 214

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

#start_with?(*args) ⇒ Boolean

Returns:

  • (Boolean)


138
139
140
# File 'lib/build/files/path.rb', line 138

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



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

def to_path
	@full_path
end

#to_sObject



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

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

#to_strObject



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

def to_str
	@full_path.to_str
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



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/build/files/path.rb', line 182

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