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 =
'/'.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(components = []) ⇒ Path

Returns a new instance of Path.



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

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

Instance Attribute Details

#componentsObject

Returns the value of attribute components.



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

def components
  @components
end

Class Method Details

.[](path) ⇒ Object



80
81
82
# File 'lib/utopia/path.rb', line 80

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

.create(path) ⇒ Object



110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/utopia/path.rb', line 110

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



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

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.



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

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

.load(value) ⇒ Object



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

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.



51
52
53
# File 'lib/utopia/path.rb', line 51

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

.rootObject



46
47
48
# File 'lib/utopia/path.rb', line 46

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

.shortest_path(path, root) ⇒ Object

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



56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/utopia/path.rb', line 56

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



84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/utopia/path.rb', line 84

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.



74
75
76
77
78
# File 'lib/utopia/path.rb', line 74

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

Instance Method Details

#+(other) ⇒ Object



187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/utopia/path.rb', line 187

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



210
211
212
213
214
215
216
217
218
219
220
# File 'lib/utopia/path.rb', line 210

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



302
303
304
# File 'lib/utopia/path.rb', line 302

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

#==(other) ⇒ Object



314
315
316
317
318
319
320
321
322
# File 'lib/utopia/path.rb', line 314

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



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

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

#[]=(index, value) ⇒ Object

Replaces a named component, indexing as per



337
338
339
# File 'lib/utopia/path.rb', line 337

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

#absolute?Boolean

Returns:

  • (Boolean)


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

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

#ascend(&block) ⇒ Object



274
275
276
277
278
279
280
281
282
283
284
# File 'lib/utopia/path.rb', line 274

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.



239
240
241
242
243
# File 'lib/utopia/path.rb', line 239

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

#delete_at(index) ⇒ Object



341
342
343
# File 'lib/utopia/path.rb', line 341

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

#descend(&block) ⇒ Object



262
263
264
265
266
267
268
269
270
271
272
# File 'lib/utopia/path.rb', line 262

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)


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

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

#dirname(count = 1) ⇒ Object



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

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

#dupObject



298
299
300
# File 'lib/utopia/path.rb', line 298

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

#empty?Boolean

Returns:

  • (Boolean)


42
43
44
# File 'lib/utopia/path.rb', line 42

def empty?
  @components.empty?
end

#eql?(other) ⇒ Boolean

Returns:

  • (Boolean)


306
307
308
# File 'lib/utopia/path.rb', line 306

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

#expand(root) ⇒ Object



183
184
185
# File 'lib/utopia/path.rb', line 183

def expand(root)
  root + self
end

#extensionString

Returns the last path component's file extension.

Returns:

  • (String)

    the last path component's file extension.



246
247
248
249
250
# File 'lib/utopia/path.rb', line 246

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

#firstObject



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

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

#freezeObject



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

def freeze
  @components.freeze
  
  super
end

#hashObject



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

def hash
  @components.hash
end

#include?(*args) ⇒ Boolean

Returns:

  • (Boolean)


129
130
131
# File 'lib/utopia/path.rb', line 129

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

#join(other) ⇒ Object



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

def join(other)
  self.class.new(@components + other).simplify
end

#lastObject



353
354
355
356
357
358
359
# File 'lib/utopia/path.rb', line 353

def last
  if directory?
    @components[-2]
  else
    @components[-1]
  end
end

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



258
259
260
# File 'lib/utopia/path.rb', line 258

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

#relative?Boolean

Returns:

  • (Boolean)


145
146
147
# File 'lib/utopia/path.rb', line 145

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

#replace(other_path) ⇒ Object



125
126
127
# File 'lib/utopia/path.rb', line 125

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

#shortest_path(root) ⇒ Object



69
70
71
# File 'lib/utopia/path.rb', line 69

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

#simplifyObject



222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/utopia/path.rb', line 222

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



286
287
288
289
290
291
292
293
294
295
296
# File 'lib/utopia/path.rb', line 286

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)


324
325
326
327
328
329
330
# File 'lib/utopia/path.rb', line 324

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

#to_aObject



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

def to_a
  @components
end

#to_absoluteObject



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

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

#to_directoryObject



137
138
139
140
141
142
143
# File 'lib/utopia/path.rb', line 137

def to_directory
  if directory?
    return self
  else
    return join([''])
  end
end

#to_relative!Object



161
162
163
# File 'lib/utopia/path.rb', line 161

def to_relative!
  @components.shift if relative?
end

#to_strObject Also known as: to_s



165
166
167
168
169
170
171
# File 'lib/utopia/path.rb', line 165

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

#with_prefix(*args) ⇒ Object



203
204
205
# File 'lib/utopia/path.rb', line 203

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