Class: Utopia::Path

Inherits:
Object
  • Object
show all
Includes:
Comparable
Defined in:
lib/utopia/path.rb,
lib/utopia/path/matcher.rb

Overview

Represents a path as an array of path components. Useful for efficient URL manipulation.

Defined Under Namespace

Classes: Matcher

Constant Summary collapse

SEPARATOR =
'/'

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(components = []) ⇒ Path

Returns a new instance of Path.



13
14
15
# File 'lib/utopia/path.rb', line 13

def initialize(components = [])
	@components = components
end

Instance Attribute Details

#componentsObject

Returns the value of attribute components.



17
18
19
# File 'lib/utopia/path.rb', line 17

def components
  @components
end

Class Method Details

.[](path) ⇒ Object



65
66
67
# File 'lib/utopia/path.rb', line 65

def self.[] path
	self.create(path)
end

.create(path) ⇒ Object



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

def self.create(path)
	case path
	when Path
		return path
	when Array
		return self.new(path)
	when String
		return self.new(unescape(path).split(SEPARATOR, -1))
	when nil
		return nil
	else
		return self.new([path])
	end
end

.dump(instance) ⇒ Object



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

def self.dump(instance)
	instance.to_s if instance
end

.from_string(string) ⇒ Object

This constructor takes a string and generates a relative path as efficiently as possible. This is a direct entry point for all controller invocations so it’s designed to suit the requirements of that function.



83
84
85
# File 'lib/utopia/path.rb', line 83

def self.from_string(string)
	self.new(unescape(string).split(SEPARATOR, -1))
end

.load(value) ⇒ Object



87
88
89
# File 'lib/utopia/path.rb', line 87

def self.load(value)
	from_string(value) if value
end

.prefix_length(a, b) ⇒ Object

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



36
37
38
# File 'lib/utopia/path.rb', line 36

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

.rootObject



31
32
33
# File 'lib/utopia/path.rb', line 31

def self.root
	self.new([''])
end

.shortest_path(path, root) ⇒ Object

Return the shortest relative path to get to path from root:



41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/utopia/path.rb', line 41

def self.shortest_path(path, root)
	path = self.create(path)
	root = self.create(root).dirname
	
	# 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 self.create([".."] * up + path.components[i..-1])
end

.split(path) ⇒ Object



69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/utopia/path.rb', line 69

def self.split(path)
	case path
	when Path
		return path.to_a
	when Array
		return path
	when String
		create(path).to_a
	else
		[path]
	end
end

.unescape(string) ⇒ Object

Converts ‘+’ into whitespace and hex encoded characters into their equivalent characters.



59
60
61
62
63
# File 'lib/utopia/path.rb', line 59

def self.unescape(string)
	string.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n) {
		[$1.delete('%')].pack('H*')
	}
end

Instance Method Details

#+(other) ⇒ Object



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

def +(other)
	if other.kind_of? Path
		if other.absolute?
			return other
		else
			return join(other.components)
		end
	elsif other.kind_of? Array
		return join(other)
	elsif other.kind_of? String
		return join(other.split(SEPARATOR, -1))
	else
		return join([other.to_s])
	end
end

#-(other) ⇒ Object

Computes the difference of the path. /a/b/c - /a/b -> c a/b/c - a/b -> c



205
206
207
208
209
210
211
212
213
214
215
# File 'lib/utopia/path.rb', line 205

def -(other)
	i = 0
	
	while i < other.components.size
		break if @components[i] != other.components[i]
		
		i += 1
	end
	
	return self.class.new(@components[i,@components.size])
end

#<=>(other) ⇒ Object



323
324
325
# File 'lib/utopia/path.rb', line 323

def <=> other
	@components <=> other.components
end

#==(other) ⇒ Object



335
336
337
338
339
340
341
342
343
# File 'lib/utopia/path.rb', line 335

def == other
	return false unless other
	
	case other
	when String then self.to_s == other
	when Array then self.to_a == other
	else other.is_a?(self.class) && @components == other.components
	end
end

#[](index) ⇒ Object



353
354
355
# File 'lib/utopia/path.rb', line 353

def [] index
	return @components[component_offset(index)]
end

#[]=(index, value) ⇒ Object

Replaces a named component, indexing as per



358
359
360
# File 'lib/utopia/path.rb', line 358

def []= index, value
	return @components[component_offset(index)] = value
end

#absolute?Boolean

Returns:

  • (Boolean)


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

def absolute?
	@components.first == ''
end

#ascend(&block) ⇒ Object



295
296
297
298
299
300
301
302
303
304
305
# File 'lib/utopia/path.rb', line 295

def ascend(&block)
	return to_enum(:ascend) unless block_given?
	
	components = self.components.dup
	
	while components.any?
		yield self.class.new(components.dup)
		
		components.pop
	end
end

#basenameString

Returns the last path component without any file extension.

Returns:

  • (String)

    the last path component without any file extension.



260
261
262
263
264
# File 'lib/utopia/path.rb', line 260

def basename
	basename, _ = @components.last.split('.', 2)
	
	return basename || ''
end

#delete_at(index) ⇒ Object



362
363
364
# File 'lib/utopia/path.rb', line 362

def delete_at(index)
	@components.delete_at(component_offset(index))
end

#descend(&block) ⇒ Object



283
284
285
286
287
288
289
290
291
292
293
# File 'lib/utopia/path.rb', line 283

def descend(&block)
	return to_enum(:descend) unless block_given?
	
	components = []
	
	@components.each do |component|
		components << component
		
		yield self.class.new(components.dup)
	end
end

#directory?Boolean

Returns:

  • (Boolean)


118
119
120
# File 'lib/utopia/path.rb', line 118

def directory?
	return @components.last == ''
end

#dirname(count = 1) ⇒ Object



273
274
275
276
277
# File 'lib/utopia/path.rb', line 273

def dirname(count = 1)
	path = self.class.new(@components[0...-count])
	
	return absolute? ? path.to_absolute : path
end

#dupObject



319
320
321
# File 'lib/utopia/path.rb', line 319

def dup
	return Path.new(components.dup)
end

#empty?Boolean

Returns:

  • (Boolean)


27
28
29
# File 'lib/utopia/path.rb', line 27

def empty?
	@components.empty?
end

#eql?(other) ⇒ Boolean

Returns:

  • (Boolean)


327
328
329
# File 'lib/utopia/path.rb', line 327

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

#expand(root) ⇒ Object



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

def expand(root)
	root + self
end

#extensionString

Returns the last path component’s file extension.

Returns:

  • (String)

    the last path component’s file extension.



267
268
269
270
271
# File 'lib/utopia/path.rb', line 267

def extension
	_, extension = @components.last.split('.', 2)
	
	return extension
end

#file?Boolean Also known as: last?

Returns:

  • (Boolean)


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

def file?
	return @components.last != ''
end

#firstObject

Returns the first path component.



234
235
236
237
238
239
240
# File 'lib/utopia/path.rb', line 234

def first
	if absolute?
		@components[1]
	else
		@components[0]
	end
end

#freezeObject



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

def freeze
	return self if frozen?
	
	@components.freeze
	
	super
end

#hashObject



331
332
333
# File 'lib/utopia/path.rb', line 331

def hash
	@components.hash
end

#include?(*arguments) ⇒ Boolean

Returns:

  • (Boolean)


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

def include?(*arguments)
	@components.include?(*arguments)
end

#join(other) ⇒ Object



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

def join(other)
	# Check whether other is an absolute path:
	if other.first == ''
		self.class.new(other)
	else
		self.class.new(@components + other).simplify
	end
end

#lastObject

Returns the last path component.



243
244
245
246
247
# File 'lib/utopia/path.rb', line 243

def last
	if @components != ['']
		@components.last
	end
end

#local_path(separator = File::SEPARATOR) ⇒ Object



279
280
281
# File 'lib/utopia/path.rb', line 279

def local_path(separator = File::SEPARATOR)
	@components.join(separator)
end

#popObject

Pops the last path component.



252
253
254
255
256
257
# File 'lib/utopia/path.rb', line 252

def pop
	# We don't want to convert an absolute path to a relative path.
	if @components != ['']
		@components.pop
	end
end

#relative?Boolean

Returns:

  • (Boolean)


134
135
136
# File 'lib/utopia/path.rb', line 134

def relative?
	@components.first != ''
end

#replace(other_path) ⇒ Object



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

def replace(other_path)
	@components = other_path.components.dup
end

#shortest_path(root) ⇒ Object



54
55
56
# File 'lib/utopia/path.rb', line 54

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

#simplifyObject



217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
# File 'lib/utopia/path.rb', line 217

def simplify
	result = absolute? ? [''] : []
	
	@components.each do |bit|
		if bit == ".."
			result.pop
		elsif bit != "." && bit != ''
			result << bit
		end
	end
	
	result << '' if directory?
	
	return self.class.new(result)
end

#split(at) ⇒ Object



307
308
309
310
311
312
313
314
315
316
317
# File 'lib/utopia/path.rb', line 307

def split(at)
	if at.kind_of?(String)
		at = @components.index(at)
	end
	
	if at
		return [self.class.new(@components[0...at]), self.class.new(@components[at+1..-1])]
	else
		return nil
	end
end

#start_with?(other) ⇒ Boolean

Returns:

  • (Boolean)


345
346
347
348
349
350
351
# File 'lib/utopia/path.rb', line 345

def start_with? other
	other.components.each_with_index do |part, index|
		return false if @components[index] != part
	end
	
	return true
end

#to_aObject



164
165
166
# File 'lib/utopia/path.rb', line 164

def to_a
	@components
end

#to_absoluteObject



142
143
144
145
146
147
148
# File 'lib/utopia/path.rb', line 142

def to_absolute
	if absolute?
		return self
	else
		return self.class.new([''] + @components)
	end
end

#to_directoryObject



126
127
128
129
130
131
132
# File 'lib/utopia/path.rb', line 126

def to_directory
	if directory?
		return self
	else
		return self.class.new(@components + [''])
	end
end

#to_relative!Object



150
151
152
# File 'lib/utopia/path.rb', line 150

def to_relative!
	@components.shift if relative?
end

#to_strObject Also known as: to_s



154
155
156
157
158
159
160
# File 'lib/utopia/path.rb', line 154

def to_str
	if @components == ['']
		SEPARATOR
	else
		@components.join(SEPARATOR)
	end
end

#with_prefix(*arguments) ⇒ Object



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

def with_prefix(*arguments)
	self.class.create(*arguments) + self
end