class Filepath

This is free software released into the public domain (CC0 license).

Constants

SEPARATOR

Attributes

segments[R]

@private

Public Class Methods

getwd() click to toggle source
# File lib/filepath/filepath.rb, line 963
def Filepath.getwd
        return Dir.getwd.as_path
end
join(*raw_paths) click to toggle source

Creates a Filepath joining the given segments.

@return [Filepath] a Filepath created joining the given segments

# File lib/filepath/filepath.rb, line 25
def Filepath.join(*raw_paths)
        if (raw_paths.count == 1) && (raw_paths.first.is_a? Array)
                raw_paths = raw_paths.first
        end

        paths = raw_paths.map { |p| p.as_path }

        segs = []
        paths.each { |path| segs += path.segments }

        return Filepath.new(segs)
end
new(path) click to toggle source
# File lib/filepath/filepath.rb, line 7
def initialize(path)
        if path.is_a? Filepath
                @segments = path.segments
        elsif path.is_a? Array
                @segments = path
        else
                @segments = split_path_string(path.to_str)
        end
end

Public Instance Methods

+(extra_path) click to toggle source

An alias for {Filepath#/}.

@deprecated Use the {Filepath#/} (slash) method instead. This method

does not show clearly if a path is being added or if a
string should be added to the filename
# File lib/filepath/filepath.rb, line 77
def +(extra_path)
        warn "Filepath#+ is deprecated, use Filepath#/ instead."
        return self / extra_path
end
/(extra_path) click to toggle source

Appends another path to the current path.

@example Append a string

"a/b".as_path / "c" #=> <a/b/c>

@example Append another Filepath

home = (ENV["HOME"] || "/root").as_path
conf_dir = '.config'.as_path

home / conf_dir #=> </home/user/.config>

@param [Filepath, String] extra_path the path to be appended to the

current path

@return [Filepath] a new path with the given path appended

# File lib/filepath/filepath.rb, line 57
def /(extra_path)
        return Filepath.join(self, extra_path)
end
<=>(other) click to toggle source

@private

# File lib/filepath/filepath.rb, line 665
def <=>(other)
        return self.normalized_segments <=> other.normalized_segments
end
==(other) click to toggle source

Checks whether two paths are equivalent.

Two paths are equivalent when they have the same normalized segments.

A relative and an absolute path will always be considered different. To compare relative paths to absolute path, expand first the relative path using {#absolute_path} or {#real_path}.

@example

path1 = "foo/bar".as_path
path2 = "foo/bar/baz".as_path
path3 = "foo/bar/baz/../../bar".as_path

path1 == path2            #=> false
path1 == path2.parent_dir #=> true
path1 == path3            #=> true

@param [Filepath, String] other the other path to compare

@return [boolean] whether the other path is equivalent to the current path

@note this method compares the normalized versions of the paths

# File lib/filepath/filepath.rb, line 648
def ==(other)
        return self.normalized_segments == other.as_path.normalized_segments
end
=~(pattern) click to toggle source

Matches a pattern against this path.

@param [Regexp, Object] pattern the pattern to match against

this path

@return [Fixnum, nil] the position of the pattern in the path, or

nil if there is no match

@note this method operates on the normalized path

# File lib/filepath/filepath.rb, line 390
def =~(pattern)
        return self.to_s =~ pattern
end
absolute?() click to toggle source

Is this path absolute?

@example

"/tmp".absolute?   #=> true
"tmp".absolute?    #=> false
"../tmp".absolute? #=> false

FIXME: document what an absolute path is.

@return whether the current path is absolute

@see relative?

# File lib/filepath/filepath.rb, line 420
def absolute?
        return @segments.first == SEPARATOR # FIXME: windows, mac
end
as_path() click to toggle source

@return [Filepath] the path itself.

# File lib/filepath/filepath.rb, line 613
def as_path
        self
end
ascend(max_depth = nil, &block) click to toggle source

Iterates over all the path directories, from the current path to the root.

@example

web_dir = "/srv/example.org/web/html/".as_path
web_dir.ascend do |path|
    is = path.readable? ? "is" : "is NOT"

    puts "#{path} #{is} readable"
end

# produces
#
# /srv/example.org/web/html is NOT redable
# /srv/example.org/web is NOT readable
# /srv/example.org is readable
# /srv is readable
# / is readable

@param max_depth the maximum depth to ascend to, nil to ascend

without limits.

@yield [path] TODO

@return [Filepath] the path itself.

@see each_segment @see descend

# File lib/filepath/filepath.rb, line 527
def ascend(max_depth = nil, &block)
        iterate(max_depth, :reverse_each, &block)
end
basename()
Alias for: filename
descend(max_depth = nil, &block) click to toggle source

Iterates over all the directory that lead to the current path.

@example

web_dir = "/srv/example.org/web/html/".as_path
web_dir.descend do |path|
    is = path.readable? ? "is" : "is NOT"

    puts "#{path} #{is} readable"
end

# produces
#
# / is readable
# /srv is readable
# /srv/example.org is readable
# /srv/example.org/web is NOT readable
# /srv/example.org/web/html is NOT redable

@param max_depth the maximum depth to descent to, nil to descend

without limits.

@yield [path] TODO

@return [Filepath] the path itself.

@see each_segment @see ascend

# File lib/filepath/filepath.rb, line 561
def descend(max_depth = nil, &block)
        iterate(max_depth, :each, &block)
end
each_segment(&block) click to toggle source

Iterates over all the path segments, from the leftmost to the rightmost.

@example

web_dir = "/srv/example.org/web/html".as_path
web_dir.each_segment do |seg|
    puts seg
end

# produces
#
# /
# srv
# example.org
# web
# html

@yield [path] TODO

@return [Filepath] the path itself.

@see ascend @see descend

# File lib/filepath/filepath.rb, line 491
def each_segment(&block)
        @segments.each(&block)
        return self
end
eql?(other) click to toggle source

@private

# File lib/filepath/filepath.rb, line 654
def eql?(other)
        if self.equal?(other)
                return true
        elsif self.class != other.class
                return false
        end

        return @segments == other.segments
end
ext()
Alias for: extension
ext?(ext = nil)
Alias for: extension?
extension() click to toggle source

The extension of the file.

The extension of a file are the characters after the last dot.

@return [String] the extension of the file or nil if the file has no

extension

@see extension?

# File lib/filepath/filepath.rb, line 243
def extension
        filename = @segments.last

        num_dots = filename.count('.')

        if num_dots.zero?
                ext = nil
        elsif filename.start_with?('.') && num_dots == 1
                ext = nil
        elsif filename.end_with?('.')
                ext = ''
        else
                ext = filename.split('.').last
        end

        return ext
end
Also aliased as: ext
extension?(ext = nil) click to toggle source

@overload extension?(ext)

@param [String, Regexp] ext the extension to be matched

@return whether the file extension matches the given extension

@overload extension?

@return whether the file has an extension
# File lib/filepath/filepath.rb, line 272
def extension?(ext = nil)
        cur_ext = self.extension

        if ext.nil?
                return !cur_ext.nil?
        else
                if ext.is_a? Regexp
                        return !cur_ext.match(ext).nil?
                else
                        return cur_ext == ext
                end
        end
end
Also aliased as: ext?
filename() click to toggle source

The filename component of the path.

The filename is the component of a path that appears after the last path separator.

@return [Filepath] the filename

# File lib/filepath/filepath.rb, line 184
def filename
        segs = self.normalized_segments

        if self.root? || segs.empty?
                return ''.as_path
        end

        filename = segs.last
        return filename.as_path
end
Also aliased as: basename
hash() click to toggle source

@private

# File lib/filepath/filepath.rb, line 670
def hash
        return @segments.hash
end
inspect() click to toggle source

@private

# File lib/filepath/filepath.rb, line 619
def inspect
        return '<' +  self.to_raw_string + '>'
end
iterate(max_depth, method) { |join| ... } click to toggle source

@private

# File lib/filepath/filepath.rb, line 567
def iterate(max_depth, method, &block)
        max_depth ||= @segments.length
        (1..max_depth).send(method) do |limit|
                segs = @segments.take(limit)
                yield Filepath.join(segs)
        end

        return self
end
join(*extra_paths) click to toggle source

Append multiple paths to the current path.

@return [Filepath] a new path with all the paths appended

# File lib/filepath/filepath.rb, line 66
def join(*extra_paths)
        return Filepath.join(self, *extra_paths)
end
join_segments(segs) click to toggle source

@private

# File lib/filepath/filepath.rb, line 721
def join_segments(segs)
        # FIXME: windows, mac
        # FIXME: avoid string substitutions and regexen
        return segs.join(SEPARATOR).sub(%r{^//}, SEPARATOR).sub(/\A\Z/, '.')
end
normalised()
Alias for: normalized
normalized() click to toggle source

Simplify paths that contain `.` and `..`.

The resulting path will be in normal form.

@example

path = $ENV["HOME"] / ".." / "jack" / "."

path #=> </home/gioele/../jack/.>
path.normalized #=> </home/jack>

FIXME: document what normal form is.

@return [Filepath] a new path that does not contain `.` or `..`

segments.
# File lib/filepath/filepath.rb, line 460
def normalized
        return Filepath.join(self.normalized_segments)
end
Also aliased as: normalised
normalized_relative_segs(orig_segs) click to toggle source

@private

# File lib/filepath/filepath.rb, line 695
def normalized_relative_segs(orig_segs)
        segs = orig_segs.dup

        i = 0
        while (i < segs.length)
                if segs[i] == '..' && segs[i-1] == SEPARATOR
                        # remove '..' segments following a root delimiter
                        segs.delete_at(i)
                        i -= 1
                elsif segs[i] == '..' && segs[i-1] != '..' && i >= 1
                        # remove every segment followed by a ".." marker
                        segs.delete_at(i)
                        segs.delete_at(i-1)
                        i -= 2
                elsif segs[i] == '.'
                        # remove "current dir" markers
                        segs.delete_at(i)
                        i -= 1
                end
                i += 1
        end

        return segs
end
normalized_segments() click to toggle source

@private

# File lib/filepath/filepath.rb, line 690
def normalized_segments
        @normalized_segments ||= normalized_relative_segs(@segments)
end
parent_dir() click to toggle source

The dir that contains the file

@return [Filepath] the path of the parent dir

# File lib/filepath/filepath.rb, line 202
def parent_dir
        return self / '..'
end
relative?() click to toggle source

Is this path relative?

@example

"/tmp".relative?   #=> false
"tmp".relative?    #=> true
"../tmp".relative? #=> true

FIXME: document what a relative path is.

@return whether the current path is relative

@see absolute?

# File lib/filepath/filepath.rb, line 439
def relative?
        return !self.absolute?
end
relative_to(base) click to toggle source

Calculates the relative path from a given directory.

@example relative paths between relative paths

posts_dir = "posts".as_path
images_dir = "static/images".as_path

logo = images_dir / 'logo.png'

logo.relative_to(posts_dir) #=> <../static/images/logo.png>

@example relative paths between absolute paths

home_dir = "/home/gioele".as_path
docs_dir = "/home/gioele/Documents".as_path
tmp_dir = "/tmp".as_path

docs_dir.relative_to(home_dir) #=> <Documents>
home_dir.relative_to(docs_dir) #=> <..>

tmp_dir.relative_to(home_dir) #=> <../../tmp>

@param [Filepath, String] base the directory to use as base for the

relative path

@return [Filepath] the relative path

@note this method operates on the normalized paths

@see relative_to_file

# File lib/filepath/filepath.rb, line 114
def relative_to(base)
        base = base.as_path

        if self.absolute? != base.absolute?
                self_abs = self.absolute? ? "absolute" : "relative"
                base_abs = base.absolute? ? "absolute" : "relative"
                msg = "cannot compare: "
                msg += "`#{self}` is #{self_abs} while "
                msg += "`#{base}` is #{base_abs}"
                raise ArgumentError, msg
        end

        self_segs = self.normalized_segments
        base_segs = base.normalized_segments

        base_segs_tmp = base_segs.dup
        num_same = self_segs.find_index do |seg|
                base_segs_tmp.delete_at(0) != seg
        end

        # find_index returns nil if `self` is a subset of `base`
        num_same ||= self_segs.length

        num_parent_dirs = base_segs.length - num_same
        left_in_self = self_segs[num_same..-1]

        segs = [".."] * num_parent_dirs + left_in_self
        normalized_segs = normalized_relative_segs(segs)

        return Filepath.join(normalized_segs)
end
relative_to_file(base_file) click to toggle source

Calculates the relative path from a given file.

@example relative paths between relative paths

post = "posts/2012-02-14-hello.html".as_path
images_dir = "static/images".as_path

rel_img_dir = images_dir.relative_to_file(post)
rel_img_dir.to_s #=> "../static/images"

logo = rel_img_dir / 'logo.png' #=> <../static/images/logo.png>

@example relative paths between absolute paths

rc_file = "/home/gioele/.bashrc".as_path
tmp_dir = "/tmp".as_path

tmp_dir.relative_to_file(rc_file) #=> <../../tmp>

@param [Filepath, String] base_file the file to use as base for the

relative path

@return [Filepath] the relative path

@see relative_to

# File lib/filepath/filepath.rb, line 172
def relative_to_file(base_file)
        return relative_to(base_file.as_path.parent_dir)
end
remove_ext()
Alias for: without_extension
remove_extension()
Alias for: without_extension
replace_basename(new_path)
Alias for: with_filename
replace_ext(new_ext)
Alias for: with_extension
replace_extension(new_ext)
Alias for: with_extension
replace_filename(new_path)
Alias for: with_filename
root?() click to toggle source

Is this path pointing to the root directory?

@return whether the path points to the root directory

@note this method operates on the normalized paths

# File lib/filepath/filepath.rb, line 401
def root?
        return self.normalized_segments == [SEPARATOR] # FIXME: windows, mac
end
split_path_string(raw_path) click to toggle source

@private

# File lib/filepath/filepath.rb, line 675
def split_path_string(raw_path)
        segments = raw_path.split(SEPARATOR) # FIXME: windows, mac

        if raw_path == SEPARATOR
                segments << SEPARATOR
        end

        if !segments.empty? && segments.first.empty?
                segments[0] = SEPARATOR
        end

        return segments
end
sub_ext(new_ext)
Alias for: with_extension
to_raw_str()
Alias for: to_raw_string
to_raw_string() click to toggle source

This path converted to a String.

@example differences between to_raw_string and to_s

path = "/home/gioele/.config".as_path / ".." / ".cache"
path.to_raw_string #=> "/home/gioele/config/../.cache"
path.to_s #=> "/home/gioele/.cache"

@return [String] this path converted to a String

@see to_s

# File lib/filepath/filepath.rb, line 590
def to_raw_string
        @to_raw_string ||= join_segments(@segments)
end
Also aliased as: to_raw_str
to_s() click to toggle source

@return [String] this path converted to a String

@note this method operates on the normalized path

# File lib/filepath/filepath.rb, line 601
def to_s
        to_str
end
to_str() click to toggle source

@private

# File lib/filepath/filepath.rb, line 607
def to_str
        @to_str ||= join_segments(self.normalized_segments)
end
with_basename(new_path)
Alias for: with_filename
with_extension(new_ext) click to toggle source

Replaces or removes the file extension.

@see extension @see extension? @see without_extension @see with_filename

@overload with_extension(new_ext)

Replaces the file extension with the supplied one. If the file
has no extension it is added to the file name together with a dot.

@example Extension replacement

    src_path = "pages/about.markdown".as_path
    html_path = src_path.with_extension("html")
    html_path.to_s #=> "pages/about.html"

@example Extension addition

    base = "style/main-style".as_path
    sass_style = base.with_extension("sass")
    sass_style.to_s #=> "style/main-style.sass"

@param [String] new_ext the new extension

@return [Filepath] a new path with the replaced extension

@overload with_extension

Removes the file extension if present.

The {#without_extension} method provides the same functionality
but has a more meaningful name.

@example

    post_file = "post/welcome.html"
    post_url = post_file.with_extension(nil)
    post_url.to_s #=> "post/welcome"

@return [Filepath] a new path without the extension
# File lib/filepath/filepath.rb, line 330
def with_extension(new_ext) # FIXME: accept block
        orig_filename = filename.to_s

        if !self.extension?
                if new_ext.nil?
                        new_filename = orig_filename
                else
                        new_filename = orig_filename + '.' + new_ext
                end
        else
                if new_ext.nil?
                        pattern = /\.[^.]*?\Z/
                        new_filename = orig_filename.sub(pattern, '')
                else
                        pattern = Regexp.new('.' + extension + '\\Z')
                        new_filename = orig_filename.sub(pattern, '.' + new_ext)
                end
        end

        segs = @segments[0..-2]
        segs << new_filename

        return Filepath.new(segs)
end
with_filename(new_path) click to toggle source

Replace the path filename with the supplied path.

@example

post = "posts/2012-02-16-hello-world/index.md".as_path
style = post.with_filename("style.css")
style.to_s #=> "posts/2012-02-16-hello-world/style.css"

@param [Filepath, String] new_path the path to be put in place of

the current filename

@return [Filepath] a path with the supplied path instead of the

current filename

@see filename @see with_extension

# File lib/filepath/filepath.rb, line 224
def with_filename(new_path)
        dir = self.parent_dir
        return dir / new_path
end
without_extension() click to toggle source

Removes the file extension if present.

@example

post_file = "post/welcome.html"
post_url = post_file.without_extension
post_url.to_s #=> "post/welcome"

@return [Filepath] a new path without the extension

@see with_extension

# File lib/filepath/filepath.rb, line 372
def without_extension
        return with_extension(nil)
end
Also aliased as: remove_ext, remove_extension