class Utopia::Path

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

Constants

SEPARATOR

Attributes

components[RW]

Public Class Methods

[](path) click to toggle source
# File lib/utopia/path.rb, line 82
def self.[] path
        self.create(path)
end
create(path) click to toggle source
# 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
dump(instance) click to toggle source
# File lib/utopia/path.rb, line 108
def self.dump(instance)
        instance.to_s if instance
end
from_string(string) click to toggle source

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
load(value) click to toggle source
# File lib/utopia/path.rb, line 104
def self.load(value)
        from_string(value) if value
end
new(components = []) click to toggle source
# File lib/utopia/path.rb, line 30
def initialize(components = [])
        @components = components
end
prefix_length(a, b) click to toggle source

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
root() click to toggle source
# File lib/utopia/path.rb, line 48
def self.root
        self.new([''])
end
shortest_path(path, root) click to toggle source

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
split(path) click to toggle source
# 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
unescape(string) click to toggle source

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

+(other) click to toggle source
# 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
-(other) click to toggle source

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
<=>(other) click to toggle source
# File lib/utopia/path.rb, line 340
def <=> other
        @components <=> other.components
end
==(other) click to toggle source
# 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
[](index) click to toggle source
# File lib/utopia/path.rb, line 370
def [] index
        return @components[component_offset(index)]
end
[]=(index, value) click to toggle source

Replaces a named component, indexing as per

# File lib/utopia/path.rb, line 375
def []= index, value
        return @components[component_offset(index)] = value
end
absolute?() click to toggle source
# File lib/utopia/path.rb, line 155
def absolute?
        @components.first == ''
end
ascend() { |class.new(dup)| ... } click to toggle source
# 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
basename() click to toggle source

@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
delete_at(index) click to toggle source
# File lib/utopia/path.rb, line 379
def delete_at(index)
        @components.delete_at(component_offset(index))
end
descend() { |class.new(dup)| ... } click to toggle source
# 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
directory?() click to toggle source
# File lib/utopia/path.rb, line 135
def directory?
        return @components.last == ''
end
dirname(count = 1) click to toggle source
# 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
dup() click to toggle source
# File lib/utopia/path.rb, line 336
def dup
        return Path.new(components.dup)
end
empty?() click to toggle source
# File lib/utopia/path.rb, line 44
def empty?
        @components.empty?
end
eql?(other) click to toggle source
# File lib/utopia/path.rb, line 344
def eql? other
        self.class.eql?(other.class) and @components.eql?(other.components)
end
expand(root) click to toggle source
# File lib/utopia/path.rb, line 195
def expand(root)
        root + self
end
extension() click to toggle source

@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?() click to toggle source
# File lib/utopia/path.rb, line 139
def file?
        return @components.last != ''
end
Also aliased as: last?
first() click to toggle source

Returns the first path component.

# File lib/utopia/path.rb, line 251
def first
        if absolute?
                @components[1]
        else
                @components[0]
        end
end
freeze() click to toggle source
Calls superclass method
# File lib/utopia/path.rb, line 36
def freeze
        return self if frozen?
        
        @components.freeze
        
        super
end
hash() click to toggle source
# File lib/utopia/path.rb, line 348
def hash
        @components.hash
end
include?(*arguments) click to toggle source
# File lib/utopia/path.rb, line 131
def include?(*arguments)
        @components.include?(*arguments)
end
join(other) click to toggle source

@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
last() click to toggle source

Returns the last path component.

# File lib/utopia/path.rb, line 260
def last
        if @components != ['']
                @components.last
        end
end
last?()
Alias for: file?
local_path(separator = File::SEPARATOR) click to toggle source
# File lib/utopia/path.rb, line 296
def local_path(separator = File::SEPARATOR)
        @components.join(separator)
end
pop() click to toggle source

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
relative?() click to toggle source
# File lib/utopia/path.rb, line 151
def relative?
        @components.first != ''
end
replace(other_path) click to toggle source
# File lib/utopia/path.rb, line 127
def replace(other_path)
        @components = other_path.components.dup
end
shortest_path(root) click to toggle source
# File lib/utopia/path.rb, line 71
def shortest_path(root)
        self.class.shortest_path(self, root)
end
simplify() click to toggle source
# 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
split(at) click to toggle source
# 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
start_with?(other) click to toggle source
# 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
to_a() click to toggle source
# File lib/utopia/path.rb, line 181
def to_a
        @components
end
to_absolute() click to toggle source
# File lib/utopia/path.rb, line 159
def to_absolute
        if absolute?
                return self
        else
                return self.class.new([''] + @components)
        end
end
to_directory() click to toggle source
# File lib/utopia/path.rb, line 143
def to_directory
        if directory?
                return self
        else
                return self.class.new(@components + [''])
        end
end
to_relative!() click to toggle source
# File lib/utopia/path.rb, line 167
def to_relative!
        @components.shift if relative?
end
to_s()
Alias for: to_str
to_str() click to toggle source
# File lib/utopia/path.rb, line 171
def to_str
        if @components == ['']
                SEPARATOR
        else
                @components.join(SEPARATOR)
        end
end
Also aliased as: to_s
with_prefix(*arguments) click to toggle source
# File lib/utopia/path.rb, line 215
def with_prefix(*arguments)
        self.class.create(*arguments) + self
end

Private Instance Methods

adjust_index(index) click to toggle source
# 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
component_offset(index) click to toggle source

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