class Utopia::Path
Represents a path as an array of path components. Useful for efficient URL manipulation.
Constants
- SEPARATOR
Attributes
Public Class Methods
# File lib/utopia/path.rb, line 82 def self.[] path self.create(path) end
# File lib/utopia/path.rb, line 112 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
# File lib/utopia/path.rb, line 108 def self.dump(instance) instance.to_s if instance end
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.
# File lib/utopia/path.rb, line 100 def self.from_string(string) self.new(unescape(string).split(SEPARATOR, -1)) end
# File lib/utopia/path.rb, line 104 def self.load(value) from_string(value) if value end
# File lib/utopia/path.rb, line 30 def initialize(components = []) @components = components end
Returns the length of the prefix which is shared by two strings.
# File lib/utopia/path.rb, line 53 def self.prefix_length(a, b) [a.size, b.size].min.times{|i| return i if a[i] != b[i]} end
# File lib/utopia/path.rb, line 48 def self.root self.new(['']) end
Return the shortest relative path to get to path from root:
# File lib/utopia/path.rb, line 58 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
# File lib/utopia/path.rb, line 86 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
Converts '+' into whitespace and hex encoded characters into their equivalent characters.
# File lib/utopia/path.rb, line 76 def self.unescape(string) string.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n) { [$1.delete('%')].pack('H*') } end
Public Instance Methods
# File lib/utopia/path.rb, line 199 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
Computes the difference of the path. /a/b/c - /a/b -> c a/b/c - a/b -> c
# File lib/utopia/path.rb, line 222 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
# File lib/utopia/path.rb, line 340 def <=> other @components <=> other.components end
# File lib/utopia/path.rb, line 352 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
# File lib/utopia/path.rb, line 370 def [] index return @components[component_offset(index)] end
Replaces a named component, indexing as per
# File lib/utopia/path.rb, line 375 def []= index, value return @components[component_offset(index)] = value end
# File lib/utopia/path.rb, line 155 def absolute? @components.first == '' end
# File lib/utopia/path.rb, line 312 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
@return [String] the last path component without any file extension.
# File lib/utopia/path.rb, line 277 def basename basename, _ = @components.last.split('.', 2) return basename || '' end
# File lib/utopia/path.rb, line 379 def delete_at(index) @components.delete_at(component_offset(index)) end
# File lib/utopia/path.rb, line 300 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
# File lib/utopia/path.rb, line 135 def directory? return @components.last == '' end
# File lib/utopia/path.rb, line 290 def dirname(count = 1) path = self.class.new(@components[0...-count]) return absolute? ? path.to_absolute : path end
# File lib/utopia/path.rb, line 336 def dup return Path.new(components.dup) end
# File lib/utopia/path.rb, line 44 def empty? @components.empty? end
# File lib/utopia/path.rb, line 344 def eql? other self.class.eql?(other.class) and @components.eql?(other.components) end
# File lib/utopia/path.rb, line 195 def expand(root) root + self end
@return [String] the last path component's file extension.
# File lib/utopia/path.rb, line 284 def extension _, extension = @components.last.split('.', 2) return extension end
# File lib/utopia/path.rb, line 139 def file? return @components.last != '' end
Returns the first path component.
# File lib/utopia/path.rb, line 251 def first if absolute? @components[1] else @components[0] end end
# File lib/utopia/path.rb, line 36 def freeze return self if frozen? @components.freeze super end
# File lib/utopia/path.rb, line 348 def hash @components.hash end
# File lib/utopia/path.rb, line 131 def include?(*arguments) @components.include?(*arguments) end
@parameter other [Array(String)] The path components to append.
# File lib/utopia/path.rb, line 186 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
Returns the last path component.
# File lib/utopia/path.rb, line 260 def last if @components != [''] @components.last end end
# File lib/utopia/path.rb, line 296 def local_path(separator = File::SEPARATOR) @components.join(separator) end
Pops the last path component.
# File lib/utopia/path.rb, line 269 def pop # We don't want to convert an absolute path to a relative path. if @components != [''] @components.pop end end
# File lib/utopia/path.rb, line 151 def relative? @components.first != '' end
# File lib/utopia/path.rb, line 127 def replace(other_path) @components = other_path.components.dup end
# File lib/utopia/path.rb, line 71 def shortest_path(root) self.class.shortest_path(self, root) end
# File lib/utopia/path.rb, line 234 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
# File lib/utopia/path.rb, line 324 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
# File lib/utopia/path.rb, line 362 def start_with? other other.components.each_with_index do |part, index| return false if @components[index] != part end return true end
# File lib/utopia/path.rb, line 181 def to_a @components end
# File lib/utopia/path.rb, line 159 def to_absolute if absolute? return self else return self.class.new([''] + @components) end end
# File lib/utopia/path.rb, line 143 def to_directory if directory? return self else return self.class.new(@components + ['']) end end
# File lib/utopia/path.rb, line 167 def to_relative! @components.shift if relative? end
# File lib/utopia/path.rb, line 171 def to_str if @components == [''] SEPARATOR else @components.join(SEPARATOR) end end
# File lib/utopia/path.rb, line 215 def with_prefix(*arguments) self.class.create(*arguments) + self end
Private Instance Methods
# File lib/utopia/path.rb, line 394 def adjust_index(index) if index < 0 index -= 1 if directory? else index += 1 if absolute? end return index end
We adjust the index slightly so that indices reference path components rather than the directory markers at the start and end of the path components array.
# File lib/utopia/path.rb, line 386 def component_offset(index) if Range === index Range.new(adjust_index(index.first), adjust_index(index.last), index.exclude_end?) else adjust_index(index) end end