module BerkeleyLibrary::Util::Paths
This module, modeled on the {golang.org/pkg/path/ Go `path` package}, provides utility routines for modifying paths separated by forward slashes, such as URL paths. For system-dependent file paths, use {ruby-doc.org/stdlib-2.7.0/libdoc/pathname/rdoc/Pathname.html `Pathname`} instead.
Public Instance Methods
Returns the shortest path name equivalent to `path` by purely lexical processing by:
-
replacing runs of multiple `/` with a single `/`
-
eliminating all `.` (current directory) elements
-
eliminating all `<child>/..` in favor of directly referencing the parent directory
-
replaing all `/..` at the beginning of the path with a single leading `/`
The returned path ends in a slash only if it is the root `/`. @see 9p.io/sys/doc/lexnames.html Rob Pike, “Lexical File Names in Plan 9 or Getting Dot-Dot Right”
@param path [String, nil] the path to clean @return [String, nil] the cleaned path, or `nil` for a nil path.
# File lib/berkeley_library/util/paths.rb, line 32 def clean(path) return unless path return '.' if ['', '.'].include?(path) StringIO.new.tap do |out| out << '/' if path[0] == '/' dotdot = (r = out.size) r, dotdot = process_next(r, dotdot, path, out) while r < path.size out << '.' if out.pos == 0 end.string end
Joins any number of path elements into a single path, separating them with slashes, ignoring empty elements and passing the result to {Paths#clean}.
@param elements [Array<String>] the elements to join @return [String] the joined path
# File lib/berkeley_library/util/paths.rb, line 50 def join(*elements) elements = elements.reject { |e| [nil, ''].include?(e) } joined_raw = elements.join('/') return '' if joined_raw == '' clean(joined_raw) end
Private Instance Methods
# File lib/berkeley_library/util/paths.rb, line 97 def append_dotdot(out) out << '/' if out.pos > 1 out << '..' out.pos end
# File lib/berkeley_library/util/paths.rb, line 88 def append_from(r, path, out) out << '/' if (path[0] == '/' && out.pos != 1) || (path[0] != '/' && out.pos != 0) while r < path.size && path[r] != '/' out << path[r] r += 1 end r end
# File lib/berkeley_library/util/paths.rb, line 103 def backtrack_to_dotdot(out, dotdot) out.seek(-1, IO::SEEK_CUR) out.seek(-1, IO::SEEK_CUR) while out.pos > dotdot && getbyte(out, out.pos) != 47 # '/' is ASCII 37 out.truncate(out.pos) end
# File lib/berkeley_library/util/paths.rb, line 80 def dotdot?(r, path) path[r] == '.' && (r + 2 == path.size || path[r + 2] == '/') end
# File lib/berkeley_library/util/paths.rb, line 84 def empty_or_dot?(r, path) path[r] == '/' || (path[r] == '.' && (r + 1 == path.size || path[r + 1] == '/')) end
# File lib/berkeley_library/util/paths.rb, line 70 def handle_dotdot(r, dotdot, path, out) if out.pos > dotdot backtrack_to_dotdot(out, dotdot) elsif path[0] != '/' dotdot = append_dotdot(out) end [r + 2, dotdot] end
# File lib/berkeley_library/util/paths.rb, line 60 def process_next(r, dotdot, path, out) # empty path element, or . return r + 1, dotdot if empty_or_dot?(r, path) # .. element: remove to last / return handle_dotdot(r, dotdot, path, out) if dotdot?(r, path) # real path element [append_from(r, path, out), dotdot] end