class Relative
Represents a relative URL, which does not include a scheme or authority.
Definitions
def initialize(path, query = nil, fragment = nil)
Initialize a new relative URL.
Signature
-
parameter
pathString The path component.
-
parameter
queryString, nil The query string.
-
parameter
fragmentString, nil The fragment identifier.
Implementation
def initialize(path, query = nil, fragment = nil)
@path = path.to_s
@query = query
@fragment = fragment
end
attr :path
Signature
-
attribute
String The path component of the URL.
attr :query
Signature
-
attribute
String, nil The query string component.
attr :fragment
Signature
-
attribute
String, nil The fragment identifier.
def to_local_path
Convert the URL path to a local filesystem path.
Signature
-
returns
String The local filesystem path.
Implementation
def to_local_path
Path.to_local_path(@path)
end
def query?
Signature
-
returns
Boolean If there is a query string.
Implementation
def query?
@query and !@query.empty?
end
def fragment?
Signature
-
returns
Boolean If there is a fragment.
Implementation
def fragment?
@fragment and !@fragment.empty?
end
def +(other)
Combine this relative URL with another URL or path.
Example: Combine two relative paths.
base = Relative.new("/documents/reports/")
other = Relative.new("invoices/2024.pdf")
result = base + other
result.path # => "/documents/reports/invoices/2024.pdf"
Example: Navigate to parent directory.
base = Relative.new("/documents/reports/archive/")
other = Relative.new("../../summary.pdf")
result = base + other
result.path # => "/documents/summary.pdf"
Signature
-
parameter
otherString, Absolute, Relative The URL or path to combine.
-
returns
Absolute, Relative The combined URL.
Implementation
def +(other)
case other
when Absolute
# Relative + Absolute: the absolute URL takes precedence
# You can't apply relative navigation to an absolute URL
other
when Relative
# Relative + Relative: merge paths directly
self.class.new(
Path.expand(self.path, other.path, true),
other.query,
other.fragment
)
when String
# Relative + String: parse and combine
self + URL[other]
else
raise ArgumentError, "Cannot combine Relative URL with #{other.class}"
end
end
def with(path: nil, query: @query, fragment: @fragment, pop: true)
Create a new Relative URL with modified components.
Example: Update the query string.
url = Relative.new("/search", "query=ruby")
updated = url.with(query: "query=python")
updated.to_s # => "/search?query=python"
Example: Append to the path.
url = Relative.new("/documents/")
updated = url.with(path: "report.pdf", pop: false)
updated.to_s # => "/documents/report.pdf"
Signature
-
parameter
pathString, nil The path to merge with the current path.
-
parameter
queryString, nil The query string to use.
-
parameter
fragmentString, nil The fragment to use.
-
parameter
popBoolean Whether to pop the last path component before merging.
-
returns
Relative A new Relative URL with the modified components.
Implementation
def with(path: nil, query: @query, fragment: @fragment, pop: true)
self.class.new(Path.expand(@path, path, pop), query, fragment)
end
def normalize!
Normalize the path by resolving "." and ".." segments and removing duplicate slashes.
This modifies the URL in-place by simplifying the path component:
- Removes "." segments (current directory)
- Resolves ".." segments (parent directory)
- Collapses multiple consecutive slashes to single slashes (except at start)
Example: Basic normalization
url = Relative.new("/foo//bar/./baz/../qux")
url.normalize!
url.path # => "/foo/bar/qux"
Signature
-
returns
self The normalized URL.
Implementation
def normalize!
components = Path.split(@path)
normalized = Path.simplify(components)
@path = Path.join(normalized)
return self
end
def append(buffer = String.new)
Append the relative URL to the given buffer. The path, query, and fragment are expected to already be properly encoded.
Implementation
def append(buffer = String.new)
buffer << @path
if @query and !@query.empty?
buffer << "?" << @query
end
if @fragment and !@fragment.empty?
buffer << "#" << @fragment
end
return buffer
end
def to_ary
Convert the URL to an array representation.
Signature
-
returns
Array An array of
[path, query, fragment].
Implementation
def to_ary
[@path, @query, @fragment]
end
def hash
Compute a hash value for the URL based on its components.
Signature
-
returns
Integer The hash value.
Implementation
def hash
to_ary.hash
end
def equal?(other)
Check if this URL is equal to another URL by comparing components.
Signature
-
parameter
otherRelative The URL to compare with.
-
returns
Boolean True if the URLs have identical components.
Implementation
def equal?(other)
to_ary == other.to_ary
end
def <=>(other)
Compare this URL with another for sorting purposes.
Signature
-
parameter
otherRelative The URL to compare with.
-
returns
Integer -1, 0, or 1 based on component-wise comparison.
Implementation
def <=>(other)
to_ary <=> other.to_ary
end
def ==(other)
Check structural equality by comparing components.
Signature
-
parameter
otherRelative The URL to compare with.
-
returns
Boolean True if the URLs have identical components.
Implementation
def ==(other)
to_ary == other.to_ary
end
def ===(other)
Check string equality, useful for case statements.
Signature
-
parameter
otherString, Relative The value to compare with.
-
returns
Boolean True if the string representations match.
Implementation
def ===(other)
to_s === other
end
def to_s
Convert the URL to its string representation.
Signature
-
returns
String The formatted URL string.
Implementation
def to_s
append
end
def as_json(...)
Convert the URL to a JSON-compatible representation.
Signature
-
returns
String The URL as a string.
Implementation
def as_json(...)
to_s
end
def to_json(...)
Convert the URL to JSON.
Signature
-
returns
String The JSON-encoded URL.
Implementation
def to_json(...)
as_json.to_json(...)
end
def inspect
Generate a human-readable representation for debugging.
Signature
-
returns
String A string like
#<Protocol::URL::Relative /path?query#fragment>.
Implementation
def inspect
"#<#{self.class} #{to_s}>"
end